@transitrix/diagrams 1.8.21 → 1.10.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/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/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 +21 -2
- package/dist/compliance/classify.js +68 -13
- 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/build.js +0 -0
- 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-candidate-fields.d.ts +6 -0
- package/dist/repo-validate/check-candidate-fields.js +87 -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 +7 -0
- package/dist/requirement/index.d.ts +2 -2
- package/dist/requirement/index.js +1 -1
- package/dist/requirement/types.d.ts +19 -1
- package/dist/requirement/types.js +3 -1
- 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/entry.js +11 -4
- 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/codex/index.ts +1 -1
- package/src/codex/types.ts +11 -3
- package/src/codex/validate.ts +30 -2
- package/src/compliance/classify.ts +87 -14
- 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/build.ts +0 -0
- 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-candidate-fields.ts +93 -0
- package/src/repo-validate/check-versioned-attributes.ts +112 -0
- package/src/repo-validate/validate-repo.ts +7 -0
- package/src/requirement/index.ts +2 -2
- package/src/requirement/types.ts +26 -1
- 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/entry.ts +13 -6
- 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,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,9 +10,18 @@
|
|
|
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
|
|
|
13
|
-
import type { ComplianceIndex, IndexAssertion, IndexRequirement } from './types.js';
|
|
21
|
+
import type { ComplianceIndex, IndexAssertion, IndexRequirement, IndexNeed } from './types.js';
|
|
14
22
|
import { computeDeadlineStatus } from './impact.js';
|
|
23
|
+
import { CLOSED_VERIFICATION_OUTCOMES } from '../verification/types.js';
|
|
24
|
+
import { CLOSED_VALIDATION_OUTCOMES } from '../validation/types.js';
|
|
15
25
|
|
|
16
26
|
export interface GapReport {
|
|
17
27
|
/** Requirements with no assertion about them, severity-sorted then id. */
|
|
@@ -26,6 +36,36 @@ export interface GapReport {
|
|
|
26
36
|
* (oldest first). Empty when `today` is not supplied.
|
|
27
37
|
*/
|
|
28
38
|
pastDeadlineRequirements: IndexRequirement[];
|
|
39
|
+
/**
|
|
40
|
+
* REQ-VERIF-COVERAGE-001: requirements with no VERIFICATION targeting them,
|
|
41
|
+
* severity-sorted then id — the engineering V&V analogue of
|
|
42
|
+
* `requirementsWithoutAssertions`.
|
|
43
|
+
*/
|
|
44
|
+
requirementsWithoutVerification: IndexRequirement[];
|
|
45
|
+
/**
|
|
46
|
+
* REQ-VERIF-COVERAGE-002: requirements with one or more VERIFICATIONs
|
|
47
|
+
* targeting them, but none closed (`pass`/`fail`) — every one is still
|
|
48
|
+
* `not_yet_run`/`inconclusive`. Severity-sorted then id.
|
|
49
|
+
*/
|
|
50
|
+
requirementsWithUnresolvedVerification: IndexRequirement[];
|
|
51
|
+
/**
|
|
52
|
+
* NEED-COVERAGE-001: needs with no REQUIREMENT whose `serves` names them —
|
|
53
|
+
* the NEED-side analogue of `requirementsWithoutAssertions`, id-sorted.
|
|
54
|
+
*/
|
|
55
|
+
needsWithoutRequirements: IndexNeed[];
|
|
56
|
+
/**
|
|
57
|
+
* NEED-VALIDATION-COVERAGE-001: needs with no VALIDATION targeting them,
|
|
58
|
+
* id-sorted — the validation-side analogue of
|
|
59
|
+
* `requirementsWithoutVerification`.
|
|
60
|
+
*/
|
|
61
|
+
needsWithoutValidation: IndexNeed[];
|
|
62
|
+
/**
|
|
63
|
+
* NEED-VALIDATION-COVERAGE-002: needs with one or more VALIDATIONs
|
|
64
|
+
* targeting them, but none closed (`pass`/`fail`) — every one is still
|
|
65
|
+
* `not_yet_run`/`inconclusive`. Mutually exclusive with
|
|
66
|
+
* `needsWithoutValidation` by construction. Id-sorted.
|
|
67
|
+
*/
|
|
68
|
+
needsWithUnresolvedValidation: IndexNeed[];
|
|
29
69
|
}
|
|
30
70
|
|
|
31
71
|
export interface GapReportOptions {
|
|
@@ -90,5 +130,52 @@ export function buildGapReport(index: ComplianceIndex, options: GapReportOptions
|
|
|
90
130
|
})
|
|
91
131
|
: [];
|
|
92
132
|
|
|
93
|
-
|
|
133
|
+
// REQ-VERIF-COVERAGE-001 — no VERIFICATION targets the requirement at all.
|
|
134
|
+
const requirementsWithoutVerification = [...index.requirementById.values()]
|
|
135
|
+
.filter(r => (index.verificationsByRequirement.get(r.id) ?? []).length === 0)
|
|
136
|
+
.sort((a, b) => {
|
|
137
|
+
const d = severityRank(a.severity) - severityRank(b.severity);
|
|
138
|
+
return d !== 0 ? d : a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// REQ-VERIF-COVERAGE-002 — has verifications, but none closed (pass/fail).
|
|
142
|
+
// Mutually exclusive with -001 by construction (only requirements with at
|
|
143
|
+
// least one verification are considered here).
|
|
144
|
+
const requirementsWithUnresolvedVerification = [...index.requirementById.values()]
|
|
145
|
+
.filter(r => {
|
|
146
|
+
const verifications = index.verificationsByRequirement.get(r.id) ?? [];
|
|
147
|
+
return verifications.length > 0
|
|
148
|
+
&& !verifications.some(v => (CLOSED_VERIFICATION_OUTCOMES as readonly string[]).includes(v.outcome));
|
|
149
|
+
})
|
|
150
|
+
.sort((a, b) => {
|
|
151
|
+
const d = severityRank(a.severity) - severityRank(b.severity);
|
|
152
|
+
return d !== 0 ? d : a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// NEED-COVERAGE-001 — no REQUIREMENT.serves names this need.
|
|
156
|
+
const needsWithoutRequirements = [...index.needById.values()]
|
|
157
|
+
.filter(n => (index.requirementsByNeed.get(n.id) ?? []).length === 0)
|
|
158
|
+
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
159
|
+
|
|
160
|
+
// NEED-VALIDATION-COVERAGE-001 — no VALIDATION targets the need at all.
|
|
161
|
+
const needsWithoutValidation = [...index.needById.values()]
|
|
162
|
+
.filter(n => (index.validationsByNeed.get(n.id) ?? []).length === 0)
|
|
163
|
+
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
164
|
+
|
|
165
|
+
// NEED-VALIDATION-COVERAGE-002 — has validations, but none closed (pass/fail).
|
|
166
|
+
// Mutually exclusive with -001 by construction (only needs with at least
|
|
167
|
+
// one validation are considered here).
|
|
168
|
+
const needsWithUnresolvedValidation = [...index.needById.values()]
|
|
169
|
+
.filter(n => {
|
|
170
|
+
const validations = index.validationsByNeed.get(n.id) ?? [];
|
|
171
|
+
return validations.length > 0
|
|
172
|
+
&& !validations.some(v => (CLOSED_VALIDATION_OUTCOMES as readonly string[]).includes(v.outcome));
|
|
173
|
+
})
|
|
174
|
+
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
requirementsWithoutAssertions, assertionsWithoutEvidence, staleAssertions, pastDeadlineRequirements,
|
|
178
|
+
requirementsWithoutVerification, requirementsWithUnresolvedVerification,
|
|
179
|
+
needsWithoutRequirements, needsWithoutValidation, needsWithUnresolvedValidation,
|
|
180
|
+
};
|
|
94
181
|
}
|
package/src/compliance/html.ts
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
|
//
|
|
@@ -183,11 +183,13 @@ export function renderComplianceHtml(canon: ComplianceCanon, scope: ReportScope,
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
case 'gap': {
|
|
186
|
-
const index = buildComplianceIndex({
|
|
186
|
+
const index = buildComplianceIndex({
|
|
187
|
+
requirements: canon.requirements,
|
|
188
|
+
assertions: canon.assertions,
|
|
189
|
+
verifications: canon.verifications,
|
|
190
|
+
});
|
|
187
191
|
const report = buildGapReport(index, { today: options.today });
|
|
188
192
|
const title = options.title ?? 'Compliance Gap Dashboard';
|
|
189
|
-
const total = report.requirementsWithoutAssertions.length + report.assertionsWithoutEvidence.length + report.staleAssertions.length;
|
|
190
|
-
const summary = `<p class="cmp-summary">${total} gap(s)</p>`;
|
|
191
193
|
const section = (heading: string, count: number, items: string[]): string => {
|
|
192
194
|
const body = items.length === 0
|
|
193
195
|
? '<p class="cmp-ok">✓ none</p>'
|
|
@@ -206,13 +208,23 @@ export function renderComplianceHtml(canon: ComplianceCanon, scope: ReportScope,
|
|
|
206
208
|
const pastDl = report.pastDeadlineRequirements.map(r => (
|
|
207
209
|
`<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>`
|
|
208
210
|
));
|
|
209
|
-
const
|
|
211
|
+
const noVerif = report.requirementsWithoutVerification.map(r => (
|
|
212
|
+
`<li><code>${escHtml(r.id)}</code> ${escHtml(r.name)}${r.severity ? ` <span class="cmp-meta">severity ${escHtml(r.severity)}</span>` : ''}</li>`
|
|
213
|
+
));
|
|
214
|
+
const unresolvedVerif = report.requirementsWithUnresolvedVerification.map(r => (
|
|
215
|
+
`<li><code>${escHtml(r.id)}</code> ${escHtml(r.name)}${r.severity ? ` <span class="cmp-meta">severity ${escHtml(r.severity)}</span>` : ''}</li>`
|
|
216
|
+
));
|
|
217
|
+
const total6 = report.requirementsWithoutAssertions.length + report.assertionsWithoutEvidence.length
|
|
218
|
+
+ report.staleAssertions.length + report.pastDeadlineRequirements.length
|
|
219
|
+
+ report.requirementsWithoutVerification.length + report.requirementsWithUnresolvedVerification.length;
|
|
210
220
|
const body = [
|
|
211
|
-
`<p class="cmp-summary">${
|
|
221
|
+
`<p class="cmp-summary">${total6} gap(s) across 6 checks</p>`,
|
|
212
222
|
section('Requirements without assertions', report.requirementsWithoutAssertions.length, reqs),
|
|
213
223
|
section('Assertions without evidence — ASSERT-007', report.assertionsWithoutEvidence.length, noEvidence),
|
|
214
224
|
section('Stale assertions — ASSERT-008', report.staleAssertions.length, stale),
|
|
215
225
|
section('Past-deadline requirements (CV-5)', report.pastDeadlineRequirements.length, pastDl),
|
|
226
|
+
section('Requirements without verification — REQ-VERIF-COVERAGE-001', report.requirementsWithoutVerification.length, noVerif),
|
|
227
|
+
section('Requirements with unresolved verification — REQ-VERIF-COVERAGE-002', report.requirementsWithUnresolvedVerification.length, unresolvedVerif),
|
|
216
228
|
].join('');
|
|
217
229
|
return htmlDoc(title, options.today, body);
|
|
218
230
|
}
|
package/src/compliance/impact.ts
CHANGED
|
@@ -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
|
package/src/compliance/index.ts
CHANGED
|
@@ -5,6 +5,8 @@ export { buildGapReport } from './gap-report.js';
|
|
|
5
5
|
export type { GapReport, GapReportOptions } from './gap-report.js';
|
|
6
6
|
export { emptyCanon, ingestComplianceDoc } from './classify.js';
|
|
7
7
|
export type { ComplianceCanon, ComplianceProduct, ComplianceCodexDoc } from './classify.js';
|
|
8
|
+
export { CLOSED_VERIFICATION_OUTCOMES } from '../verification/types.js';
|
|
9
|
+
export { CLOSED_VALIDATION_OUTCOMES } from '../validation/types.js';
|
|
8
10
|
export {
|
|
9
11
|
admitDocumentToCatalog,
|
|
10
12
|
buildComplianceScan,
|
|
@@ -58,6 +60,9 @@ export type {
|
|
|
58
60
|
DeadlineStatus,
|
|
59
61
|
IndexRequirement,
|
|
60
62
|
IndexAssertion,
|
|
63
|
+
IndexVerification,
|
|
64
|
+
IndexNeed,
|
|
65
|
+
IndexValidation,
|
|
61
66
|
LawTree,
|
|
62
67
|
LawTreeRequirement,
|
|
63
68
|
ProductView,
|
|
@@ -66,6 +71,7 @@ export type {
|
|
|
66
71
|
ObjectDetailInput,
|
|
67
72
|
RequirementTrace,
|
|
68
73
|
TraceAssertionRow,
|
|
74
|
+
TraceVerificationRow,
|
|
69
75
|
TraceElementCatalog,
|
|
70
76
|
TraceElementRef,
|
|
71
77
|
TraceSourceRef,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Markdown renderers for the compliance views (
|
|
1
|
+
// Markdown renderers for the compliance views (Phase 5).
|
|
2
2
|
// Pure string builders consumed by the `transitrix export-compliance` CLI.
|
|
3
3
|
// PDF rendering lives in `html.ts` (HTML doc fed to WeasyPrint by the CLI).
|
|
4
4
|
|
|
@@ -111,10 +111,16 @@ function productMarkdown(canon: ComplianceCanon, productId: string): string {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
function gapMarkdown(canon: ComplianceCanon, today?: string): string {
|
|
114
|
-
const index = buildComplianceIndex({
|
|
114
|
+
const index = buildComplianceIndex({
|
|
115
|
+
requirements: canon.requirements,
|
|
116
|
+
assertions: canon.assertions,
|
|
117
|
+
verifications: canon.verifications,
|
|
118
|
+
});
|
|
115
119
|
const report = buildGapReport(index, { today });
|
|
116
|
-
const total = report.requirementsWithoutAssertions.length + report.assertionsWithoutEvidence.length
|
|
117
|
-
|
|
120
|
+
const total = report.requirementsWithoutAssertions.length + report.assertionsWithoutEvidence.length
|
|
121
|
+
+ report.staleAssertions.length + report.pastDeadlineRequirements.length
|
|
122
|
+
+ report.requirementsWithoutVerification.length + report.requirementsWithUnresolvedVerification.length;
|
|
123
|
+
const out: string[] = ['# Compliance Gap Dashboard', '', `_${total} gap(s) across 6 checks_`, ''];
|
|
118
124
|
|
|
119
125
|
out.push(`## Requirements without assertions (${report.requirementsWithoutAssertions.length})`, '');
|
|
120
126
|
if (report.requirementsWithoutAssertions.length === 0) out.push('_✓ none_', '');
|
|
@@ -132,5 +138,13 @@ function gapMarkdown(canon: ComplianceCanon, today?: string): string {
|
|
|
132
138
|
if (report.pastDeadlineRequirements.length === 0) out.push('_✓ none_', '');
|
|
133
139
|
else { for (const r of report.pastDeadlineRequirements) out.push(`- [ ] \`${r.id}\` ${r.name} — deadline: ${r.deadline ?? '—'}${r.severity ? `, severity: ${r.severity}` : ''}`); out.push(''); }
|
|
134
140
|
|
|
141
|
+
out.push(`## Requirements without verification — REQ-VERIF-COVERAGE-001 (${report.requirementsWithoutVerification.length})`, '');
|
|
142
|
+
if (report.requirementsWithoutVerification.length === 0) out.push('_✓ none_', '');
|
|
143
|
+
else { for (const r of report.requirementsWithoutVerification) out.push(`- [ ] \`${r.id}\` ${r.name}${r.severity ? ` — severity: ${r.severity}` : ''}`); out.push(''); }
|
|
144
|
+
|
|
145
|
+
out.push(`## Requirements with unresolved verification — REQ-VERIF-COVERAGE-002 (${report.requirementsWithUnresolvedVerification.length})`, '');
|
|
146
|
+
if (report.requirementsWithUnresolvedVerification.length === 0) out.push('_✓ none_', '');
|
|
147
|
+
else { for (const r of report.requirementsWithUnresolvedVerification) out.push(`- [ ] \`${r.id}\` ${r.name}${r.severity ? ` — severity: ${r.severity}` : ''}`); out.push(''); }
|
|
148
|
+
|
|
135
149
|
return out.join('\n');
|
|
136
150
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
// Reverse-index builder (
|
|
1
|
+
// Reverse-index builder (Phase 3).
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type {
|
|
4
|
+
ComplianceIndex,
|
|
5
|
+
ComplianceIndexInput,
|
|
6
|
+
IndexAssertion,
|
|
7
|
+
IndexRequirement,
|
|
8
|
+
IndexVerification,
|
|
9
|
+
IndexNeed,
|
|
10
|
+
IndexValidation,
|
|
11
|
+
} from './types.js';
|
|
4
12
|
|
|
5
13
|
function push<K, V>(map: Map<K, V[]>, key: K, value: V): void {
|
|
6
14
|
const list = map.get(key);
|
|
@@ -12,7 +20,12 @@ function push<K, V>(map: Map<K, V[]>, key: K, value: V): void {
|
|
|
12
20
|
* Builds the reverse indexes used by every compliance view. Pure; the input
|
|
13
21
|
* arrays are read, not mutated. Requirements with no `derived_from` simply do
|
|
14
22
|
* not appear under any law; assertions are indexed by both their requirement
|
|
15
|
-
* (`about`) and their subject
|
|
23
|
+
* (`about`) and their subject; verifications are indexed by their requirement
|
|
24
|
+
* (`verifies`) — an independent catalogue from assertions (27-verification.md §4).
|
|
25
|
+
* Needs are indexed by id; requirements are additionally indexed by the NEED
|
|
26
|
+
* they `serves` (15-requirement.md §2.7), and validations by the NEED they
|
|
27
|
+
* `validate` (28-validation.md §2) — both feed the NEED-side coverage rules
|
|
28
|
+
* (ELEMENT_PRIMITIVES.md §9).
|
|
16
29
|
*/
|
|
17
30
|
export function buildComplianceIndex(input: ComplianceIndexInput): ComplianceIndex {
|
|
18
31
|
const requirementById = new Map<string, IndexRequirement>();
|
|
@@ -20,16 +33,34 @@ export function buildComplianceIndex(input: ComplianceIndexInput): ComplianceInd
|
|
|
20
33
|
const assertionsByRequirement = new Map<string, IndexAssertion[]>();
|
|
21
34
|
const assertionsBySubject = new Map<string, IndexAssertion[]>();
|
|
22
35
|
const requirementsByParent = new Map<string, IndexRequirement[]>();
|
|
36
|
+
const verificationsByRequirement = new Map<string, IndexVerification[]>();
|
|
37
|
+
const needById = new Map<string, IndexNeed>();
|
|
38
|
+
const requirementsByNeed = new Map<string, IndexRequirement[]>();
|
|
39
|
+
const validationsByNeed = new Map<string, IndexValidation[]>();
|
|
23
40
|
|
|
24
41
|
for (const r of input.requirements) {
|
|
25
42
|
requirementById.set(r.id, r);
|
|
26
43
|
for (const law of r.derived_from ?? []) push(requirementsByLaw, law, r);
|
|
27
44
|
if (r.parent) push(requirementsByParent, r.parent, r);
|
|
45
|
+
if (r.serves) push(requirementsByNeed, r.serves, r);
|
|
28
46
|
}
|
|
29
47
|
for (const a of input.assertions) {
|
|
30
48
|
push(assertionsByRequirement, a.about, a);
|
|
31
49
|
push(assertionsBySubject, a.subject, a);
|
|
32
50
|
}
|
|
51
|
+
for (const v of input.verifications ?? []) {
|
|
52
|
+
push(verificationsByRequirement, v.verifies, v);
|
|
53
|
+
}
|
|
54
|
+
for (const n of input.needs ?? []) {
|
|
55
|
+
needById.set(n.id, n);
|
|
56
|
+
}
|
|
57
|
+
for (const v of input.validations ?? []) {
|
|
58
|
+
push(validationsByNeed, v.validates, v);
|
|
59
|
+
}
|
|
33
60
|
|
|
34
|
-
return {
|
|
61
|
+
return {
|
|
62
|
+
requirementById, requirementsByLaw, assertionsByRequirement, assertionsBySubject,
|
|
63
|
+
requirementsByParent, verificationsByRequirement,
|
|
64
|
+
needById, requirementsByNeed, validationsByNeed,
|
|
65
|
+
};
|
|
35
66
|
}
|
package/src/compliance/trace.ts
CHANGED
|
@@ -22,6 +22,7 @@ import type {
|
|
|
22
22
|
IndexRequirement,
|
|
23
23
|
RequirementTrace,
|
|
24
24
|
TraceAssertionRow,
|
|
25
|
+
TraceVerificationRow,
|
|
25
26
|
TraceElementCatalog,
|
|
26
27
|
TraceElementRef,
|
|
27
28
|
TraceSourceRef,
|
|
@@ -95,10 +96,15 @@ export function buildRequirementTrace(
|
|
|
95
96
|
realisedVia: (assertion.realised_via ?? []).map(rid => resolveElement(rid, catalog)),
|
|
96
97
|
}));
|
|
97
98
|
|
|
99
|
+
const rawVerifications = index.verificationsByRequirement.get(requirementId) ?? [];
|
|
100
|
+
const verifications: TraceVerificationRow[] = [...rawVerifications]
|
|
101
|
+
.sort(byId)
|
|
102
|
+
.map(verification => ({ verification }));
|
|
103
|
+
|
|
98
104
|
const ancestors = collectAncestors(requirement, index);
|
|
99
105
|
const children = [...(index.requirementsByParent.get(requirementId) ?? [])].sort(byId);
|
|
100
106
|
|
|
101
|
-
return { requirement, sources, assertions, ancestors, children };
|
|
107
|
+
return { requirement, sources, assertions, verifications, ancestors, children };
|
|
102
108
|
}
|
|
103
109
|
|
|
104
110
|
/**
|
package/src/compliance/types.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
// Shared compliance reverse-index + derived views (
|
|
2
|
-
// Phase 3). The reverse-index pass (Law → Requirements, Requirement →
|
|
1
|
+
// Shared compliance reverse-index + derived views (Phase 3). The reverse-index pass (Law → Requirements, Requirement →
|
|
3
2
|
// Assertions, Subject → Assertions) backs the single-law tree and the
|
|
4
3
|
// single-product view here, and the gap dashboard in Phase 4.
|
|
5
4
|
|
|
6
5
|
import type { AssertionStatus } from '../assertion/types.js';
|
|
6
|
+
import type { VerificationMethod, VerificationOutcome } from '../verification/types.js';
|
|
7
|
+
import type { ValidationMethod, ValidationOutcome } from '../validation/types.js';
|
|
7
8
|
import type { ComplianceCodexDoc } from './classify.js';
|
|
8
9
|
|
|
9
10
|
/** Projection of a REQUIREMENT (or CONSTRAINT) the compliance views need. */
|
|
@@ -53,6 +54,35 @@ export interface IndexRequirement {
|
|
|
53
54
|
* Present when the author has scheduled a review; drives `REQ-STALE-001`.
|
|
54
55
|
*/
|
|
55
56
|
next_review_at?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Typed ID of the upstream `NEED` this requirement traces to
|
|
59
|
+
* (15-requirement.md §2.7; `REQ-SERVES-001`). Optional, singular. Feeds
|
|
60
|
+
* `NEED-COVERAGE-001` (ELEMENT_PRIMITIVES.md §9) on the NEED side.
|
|
61
|
+
*/
|
|
62
|
+
serves?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Projection of a `NEED` element (ELEMENT_PRIMITIVES.md §7.28) the compliance
|
|
66
|
+
* views need — just enough to list an unaddressed / unvalidated need. */
|
|
67
|
+
export interface IndexNeed {
|
|
68
|
+
id: string;
|
|
69
|
+
name: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Projection of a `VALIDATION` claim (28-validation.md §2) the compliance
|
|
73
|
+
* views need — the validation-domain peer of `IndexVerification`, anchored
|
|
74
|
+
* on NEED instead of REQUIREMENT. */
|
|
75
|
+
export interface IndexValidation {
|
|
76
|
+
id: string;
|
|
77
|
+
/** Typed ID of the NEED this validation targets. */
|
|
78
|
+
validates: string;
|
|
79
|
+
method: ValidationMethod;
|
|
80
|
+
outcome: ValidationOutcome;
|
|
81
|
+
performed_at?: string;
|
|
82
|
+
/** Number of evidence entries — feeds the VALID-006 gap. */
|
|
83
|
+
evidenceCount?: number;
|
|
84
|
+
/** Admission date (CONTRACT §6) — feeds DQ-1 freshness decay. */
|
|
85
|
+
admitted_at?: string;
|
|
56
86
|
}
|
|
57
87
|
|
|
58
88
|
// ── Temporal status (CV-3) ──────────────────────────────────────────────────
|
|
@@ -91,6 +121,20 @@ export interface IndexAssertion {
|
|
|
91
121
|
owner_to_confirm?: string;
|
|
92
122
|
}
|
|
93
123
|
|
|
124
|
+
/** Projection of a VERIFICATION the compliance views need (27-verification.md §2). */
|
|
125
|
+
export interface IndexVerification {
|
|
126
|
+
id: string;
|
|
127
|
+
/** Typed ID of the REQUIREMENT this verification targets. */
|
|
128
|
+
verifies: string;
|
|
129
|
+
method: VerificationMethod;
|
|
130
|
+
outcome: VerificationOutcome;
|
|
131
|
+
performed_at?: string;
|
|
132
|
+
/** Number of evidence entries — feeds the VERIF-006 gap. */
|
|
133
|
+
evidenceCount?: number;
|
|
134
|
+
/** Admission date (CONTRACT §6) — feeds DQ-1 freshness decay. */
|
|
135
|
+
admitted_at?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
94
138
|
// ── Stage grouping (CV-3a) ──────────────────────────────────────────────────
|
|
95
139
|
|
|
96
140
|
/** One stage (or task) element of a business object, used as a matrix sub-column. */
|
|
@@ -128,6 +172,12 @@ export interface ObjectDetailInput {
|
|
|
128
172
|
export interface ComplianceIndexInput {
|
|
129
173
|
requirements: IndexRequirement[];
|
|
130
174
|
assertions: IndexAssertion[];
|
|
175
|
+
/** Optional — absent inputs (existing callers) simply index no verifications. */
|
|
176
|
+
verifications?: IndexVerification[];
|
|
177
|
+
/** Optional — absent inputs (existing callers) simply index no needs. */
|
|
178
|
+
needs?: IndexNeed[];
|
|
179
|
+
/** Optional — absent inputs (existing callers) simply index no validations. */
|
|
180
|
+
validations?: IndexValidation[];
|
|
131
181
|
}
|
|
132
182
|
|
|
133
183
|
/** The reverse-index — all maps are keyed by canonical id. */
|
|
@@ -144,6 +194,18 @@ export interface ComplianceIndex {
|
|
|
144
194
|
* (15-requirement.md §2.4). Enables the requirement-hierarchy view.
|
|
145
195
|
*/
|
|
146
196
|
requirementsByParent: Map<string, IndexRequirement[]>;
|
|
197
|
+
/** Requirement id → verifications whose `verifies` names it
|
|
198
|
+
* (27-verification.md §2). The engineering V&V analogue of
|
|
199
|
+
* `assertionsByRequirement` — the two catalogues are independent. */
|
|
200
|
+
verificationsByRequirement: Map<string, IndexVerification[]>;
|
|
201
|
+
/** NEED id → needs, keyed by their own id (single-entry lookup convenience). */
|
|
202
|
+
needById: Map<string, IndexNeed>;
|
|
203
|
+
/** NEED id → requirements whose `serves` names it (15-requirement.md §2.7).
|
|
204
|
+
* Feeds `NEED-COVERAGE-001`. */
|
|
205
|
+
requirementsByNeed: Map<string, IndexRequirement[]>;
|
|
206
|
+
/** NEED id → validations whose `validates` names it (28-validation.md §2).
|
|
207
|
+
* Feeds `NEED-VALIDATION-COVERAGE-001..002`. */
|
|
208
|
+
validationsByNeed: Map<string, IndexValidation[]>;
|
|
147
209
|
}
|
|
148
210
|
|
|
149
211
|
// ── Single-law tree ─────────────────────────────────────────────────────────
|
|
@@ -198,6 +260,13 @@ export interface TraceAssertionRow {
|
|
|
198
260
|
realisedVia: TraceElementRef[];
|
|
199
261
|
}
|
|
200
262
|
|
|
263
|
+
/** One VERIFICATION targeting the traced requirement (27-verification.md §2).
|
|
264
|
+
* No subject/realised_via — a verification claims a protocol was run against
|
|
265
|
+
* the requirement's acceptance criteria directly (16-assertion.md §4). */
|
|
266
|
+
export interface TraceVerificationRow {
|
|
267
|
+
verification: IndexVerification;
|
|
268
|
+
}
|
|
269
|
+
|
|
201
270
|
/**
|
|
202
271
|
* Requirement traceability + hierarchy view.
|
|
203
272
|
*
|
|
@@ -219,6 +288,10 @@ export interface RequirementTrace {
|
|
|
219
288
|
sources: TraceSourceRef[];
|
|
220
289
|
/** Forward-trace via ASSERTION. Empty for CONSTRAINT elements (16-assertion.md §1) or for a REQUIREMENT with no filed assertion. */
|
|
221
290
|
assertions: TraceAssertionRow[];
|
|
291
|
+
/** Forward-trace via VERIFICATION (27-verification.md §2). Empty for CONSTRAINT
|
|
292
|
+
* elements (the engineering V&V leg applies to REQUIREMENT only, same posture
|
|
293
|
+
* as ASSERTION) or for a REQUIREMENT with no filed verification. */
|
|
294
|
+
verifications: TraceVerificationRow[];
|
|
222
295
|
/** Parent chain: immediate parent first, root last. Empty when the element has no `parent`. */
|
|
223
296
|
ancestors: IndexRequirement[];
|
|
224
297
|
/** Direct children: elements naming this one as their `parent`, id-sorted. */
|
package/src/compliance/views.ts
CHANGED
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Compliance matrix model (
|
|
1
|
+
// Compliance matrix model (Phase 2).
|
|
2
2
|
//
|
|
3
3
|
// The matrix is Products × Requirements. Each cell is the status of the
|
|
4
4
|
// (Product, Requirement) ASSERTION when one exists, or a gap when none does.
|
|
@@ -97,6 +97,6 @@ export interface MatrixFilter {
|
|
|
97
97
|
statuses?: AssertionStatus[];
|
|
98
98
|
/** Keep only requirement columns whose resolved jurisdictions intersect this
|
|
99
99
|
* set. Requirements with no resolved jurisdiction are filtered out when this
|
|
100
|
-
* is non-empty (F16
|
|
100
|
+
* is non-empty (F16). */
|
|
101
101
|
jurisdictions?: string[];
|
|
102
102
|
}
|
package/src/confidence/score.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// callers (Studio previews, DSM React) pass already-resolved element inputs
|
|
3
3
|
// and a `today` reference; this module returns the per-element scores and
|
|
4
4
|
// the view composite. Lives in @transitrix/diagrams so Studio and DSM share
|
|
5
|
-
// one implementation (
|
|
5
|
+
// one implementation (DQ-1).
|
|
6
6
|
|
|
7
7
|
import type {
|
|
8
8
|
ConfidenceBand,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
import type { ValidationError, ValidationWarning, ValidationResult } from '../validation-types.js';
|
|
5
5
|
import { typeOfId, isCanonicalIdOfType, type CanonCatalog } from '../typed-id.js';
|
|
6
|
+
import { checkAgreement } from '../agreement.js';
|
|
6
7
|
import { CONSTRAINT_STATUSES, CONSTRAINT_SEVERITIES } from './types.js';
|
|
7
8
|
|
|
8
9
|
export interface ConstraintValidateOptions {
|
|
@@ -127,5 +128,8 @@ export function validateConstraint(
|
|
|
127
128
|
}
|
|
128
129
|
}
|
|
129
130
|
|
|
131
|
+
// AGREE-001..003 — agreement axis (CONTRACT.md §6.3.1), optional; absent ⇒ agreed.
|
|
132
|
+
errors.push(...checkAgreement(c));
|
|
133
|
+
|
|
130
134
|
return { valid: errors.length === 0, errors, warnings };
|
|
131
135
|
}
|
package/src/edge-path.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// arrow reads as perpendicular to the node's vertical edge.
|
|
6
6
|
//
|
|
7
7
|
// Pulling the path math here (rather than duplicating it inline in each
|
|
8
|
-
// preview) makes the configurable-curvature behaviour
|
|
8
|
+
// preview) makes the configurable-curvature behaviour
|
|
9
9
|
// unit-testable — the extension has no test harness.
|
|
10
10
|
|
|
11
11
|
/**
|
package/src/factor/validate.ts
CHANGED
|
@@ -36,8 +36,8 @@ export function validateFactor(input: unknown, options: FactorValidateOptions =
|
|
|
36
36
|
const f = input as Record<string, unknown>;
|
|
37
37
|
|
|
38
38
|
// FACTOR-001 — id grammar + notation tag + envelope.
|
|
39
|
-
if (!isCanonicalIdOfType(f.id, '
|
|
40
|
-
errors.push({ code: 'FACTOR-001', message: `id "${String(f.id)}" must match
|
|
39
|
+
if (!isCanonicalIdOfType(f.id, 'DRIVER')) {
|
|
40
|
+
errors.push({ code: 'FACTOR-001', message: `id "${String(f.id)}" must match DRIVER-[<middle>-]<INTEGER>.`, path: 'id' });
|
|
41
41
|
}
|
|
42
42
|
if (f.notation !== 'driver') {
|
|
43
43
|
errors.push({ code: 'FACTOR-001', message: 'notation must be the fixed value "driver".', path: 'notation' });
|
package/src/fgca/chain-table.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Chain-table model for the FGCA / FGA previews
|
|
1
|
+
// Chain-table model for the FGCA / FGA previews.
|
|
2
2
|
//
|
|
3
3
|
// The tree/chain preview (`layoutFGCAPreview`) shows Driver → Goal → Change →
|
|
4
4
|
// Activity as columns of nodes joined by edges. This module produces the
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* historically but the layout code only uses IDs as opaque keys, so
|
|
18
18
|
* string IDs work fine.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
20
|
+
* This lives at the input boundary so the library's
|
|
21
21
|
* internal types (currently using DSM-API form — numeric IDs and
|
|
22
22
|
* singular cross-refs) can stay where they are. A follow-up task can
|
|
23
23
|
* unify the internal types with the canonical form across all sister
|
|
@@ -354,7 +354,7 @@ export interface CanonicalFGAResult extends ValidationResult {
|
|
|
354
354
|
* FGA-001..011). The resulting internal `FGCADoc` carries `activity.goal_id`,
|
|
355
355
|
* which is exactly what the renderer needs to draw goal → activity edges —
|
|
356
356
|
* the field whose absence produced the "FGA nodes render, no edges" bug
|
|
357
|
-
* (transitrix/methodology#65
|
|
357
|
+
* (transitrix/methodology#65).
|
|
358
358
|
*
|
|
359
359
|
* Lives beside `parseCanonicalFGCA` (rather than inline in the extension) so
|
|
360
360
|
* the canonical FGA path is unit-testable and the library owns the single
|
|
@@ -49,7 +49,7 @@ export interface FGCAPreviewLayoutOptions {
|
|
|
49
49
|
colGap?: number;
|
|
50
50
|
/** Vertical gap (px) between stacked nodes within a column. Default matches the historical hardcoded value. */
|
|
51
51
|
rowGap?: number;
|
|
52
|
-
/** Trim to a level cap or a single root goal
|
|
52
|
+
/** Trim to a level cap or a single root goal. Defaults to 'all'. */
|
|
53
53
|
scope?: Scope;
|
|
54
54
|
/** Entity node width (px). Default {@link FGCA_NODE_W}. */
|
|
55
55
|
nodeWidth?: number;
|
package/src/goals/layout.ts
CHANGED
|
@@ -8,7 +8,7 @@ const DEFAULT_RANK_SEP = 80;
|
|
|
8
8
|
const DEFAULT_NODE_SEP = 40;
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Trims a flat goal list to a scope
|
|
11
|
+
* Trims a flat goal list to a scope.
|
|
12
12
|
* - 'level' → goals with `level <= maxLevel`.
|
|
13
13
|
* - 'root' → the root goal (matched by string id) plus all its descendants
|
|
14
14
|
* via `parent_id`; an empty list when the root is absent.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* mapped to internal sequential numbers, `parent: GOAL-X` becomes
|
|
16
16
|
* `parent_id: <numeric>`, `parent_id: 0` denotes a root.
|
|
17
17
|
*
|
|
18
|
-
*
|
|
18
|
+
* This lives at the input boundary so the library's
|
|
19
19
|
* internal types (numeric IDs) stay where they are. Convergence on
|
|
20
20
|
* canonical typed-string IDs across all sister modules is a follow-up
|
|
21
21
|
* task.
|
package/src/goals/types.ts
CHANGED
|
@@ -60,7 +60,7 @@ export interface LayoutOptions {
|
|
|
60
60
|
nodeSep?: number;
|
|
61
61
|
hideCollapsed?: number[];
|
|
62
62
|
viewDepth?: number | null;
|
|
63
|
-
/** Trim the tree to a level cap or a subtree root
|
|
63
|
+
/** Trim the tree to a level cap or a subtree root. Defaults to 'all'. */
|
|
64
64
|
scope?: Scope;
|
|
65
65
|
}
|
|
66
66
|
|
package/src/goals/validate.ts
CHANGED
|
@@ -21,7 +21,7 @@ export function validateGoalTree(input: unknown): ValidationResult {
|
|
|
21
21
|
const tree = raw as unknown as GoalTree;
|
|
22
22
|
|
|
23
23
|
// GOALS-013 — goal_types[].level values must be contiguous starting from 0.
|
|
24
|
-
// Per the N+1 rule
|
|
24
|
+
// Per the N+1 rule, the level set must
|
|
25
25
|
// be exactly {0, 1, …, N}; a gap breaks the parent→child level invariant
|
|
26
26
|
// and means GOALS-012 cannot be enforced consistently.
|
|
27
27
|
const levels = tree.goal_types
|
|
@@ -96,7 +96,7 @@ export function validateGoalTree(input: unknown): ValidationResult {
|
|
|
96
96
|
continue;
|
|
97
97
|
}
|
|
98
98
|
// GOALS-012 — parent must be exactly one level above the child (N+1
|
|
99
|
-
// hierarchy
|
|
99
|
+
// hierarchy). Only enforced when the
|
|
100
100
|
// parent is resolvable; a missing parent is covered by BROKEN_PARENT_REF.
|
|
101
101
|
if (g.parent_id !== 0) {
|
|
102
102
|
const parentLevel = levelById.get(g.parent_id);
|