@transitrix/diagrams 1.0.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/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/__tests__/edge-path.test.d.ts +1 -0
- package/dist/__tests__/edge-path.test.js +39 -0
- package/dist/__tests__/scope.test.d.ts +1 -0
- package/dist/__tests__/scope.test.js +23 -0
- package/dist/__tests__/yaml-normalize.test.d.ts +1 -0
- package/dist/__tests__/yaml-normalize.test.js +148 -0
- package/dist/activities/__tests__/cpm.test.d.ts +1 -0
- package/dist/activities/__tests__/cpm.test.js +146 -0
- package/dist/activities/__tests__/gantt.test.d.ts +1 -0
- package/dist/activities/__tests__/gantt.test.js +224 -0
- package/dist/activities/__tests__/layout.test.d.ts +1 -0
- package/dist/activities/__tests__/layout.test.js +98 -0
- package/dist/activities/__tests__/validate.test.d.ts +1 -0
- package/dist/activities/__tests__/validate.test.js +363 -0
- package/dist/activities/cpm.d.ts +2 -0
- package/dist/activities/cpm.js +86 -0
- package/dist/activities/gantt.d.ts +6 -0
- package/dist/activities/gantt.js +250 -0
- package/dist/activities/index.d.ts +5 -0
- package/dist/activities/index.js +4 -0
- package/dist/activities/layout.d.ts +2 -0
- package/dist/activities/layout.js +103 -0
- package/dist/activities/types.d.ts +125 -0
- package/dist/activities/types.js +1 -0
- package/dist/activities/validate.d.ts +2 -0
- package/dist/activities/validate.js +326 -0
- package/dist/activity-card/__tests__/example.test.d.ts +1 -0
- package/dist/activity-card/__tests__/example.test.js +56 -0
- package/dist/activity-card/__tests__/layout.test.d.ts +1 -0
- package/dist/activity-card/__tests__/layout.test.js +57 -0
- package/dist/activity-card/__tests__/resolver.test.d.ts +1 -0
- package/dist/activity-card/__tests__/resolver.test.js +146 -0
- package/dist/activity-card/__tests__/validate.test.d.ts +1 -0
- package/dist/activity-card/__tests__/validate.test.js +116 -0
- package/dist/activity-card/index.d.ts +5 -0
- package/dist/activity-card/index.js +3 -0
- package/dist/activity-card/layout.d.ts +7 -0
- package/dist/activity-card/layout.js +167 -0
- package/dist/activity-card/resolver.d.ts +7 -0
- package/dist/activity-card/resolver.js +310 -0
- package/dist/activity-card/types.d.ts +175 -0
- package/dist/activity-card/types.js +20 -0
- package/dist/activity-card/validate.d.ts +3 -0
- package/dist/activity-card/validate.js +145 -0
- package/dist/actor/__tests__/validate.test.d.ts +1 -0
- package/dist/actor/__tests__/validate.test.js +57 -0
- package/dist/actor/index.d.ts +3 -0
- package/dist/actor/index.js +2 -0
- package/dist/actor/types.d.ts +20 -0
- package/dist/actor/types.js +4 -0
- package/dist/actor/validate.d.ts +2 -0
- package/dist/actor/validate.js +34 -0
- package/dist/applications/__tests__/validate.test.d.ts +1 -0
- package/dist/applications/__tests__/validate.test.js +223 -0
- package/dist/applications/index.d.ts +2 -0
- package/dist/applications/index.js +1 -0
- package/dist/applications/types.d.ts +39 -0
- package/dist/applications/types.js +1 -0
- package/dist/applications/validate.d.ts +3 -0
- package/dist/applications/validate.js +109 -0
- package/dist/assertion/__tests__/example.test.d.ts +1 -0
- package/dist/assertion/__tests__/example.test.js +32 -0
- package/dist/assertion/__tests__/validate.test.d.ts +1 -0
- package/dist/assertion/__tests__/validate.test.js +130 -0
- package/dist/assertion/index.d.ts +4 -0
- package/dist/assertion/index.js +2 -0
- package/dist/assertion/types.d.ts +43 -0
- package/dist/assertion/types.js +7 -0
- package/dist/assertion/validate.d.ts +9 -0
- package/dist/assertion/validate.js +137 -0
- package/dist/blocks/__tests__/layout.test.d.ts +1 -0
- package/dist/blocks/__tests__/layout.test.js +153 -0
- package/dist/blocks/__tests__/validate.test.d.ts +1 -0
- package/dist/blocks/__tests__/validate.test.js +235 -0
- package/dist/blocks/index.d.ts +4 -0
- package/dist/blocks/index.js +2 -0
- package/dist/blocks/layout.d.ts +4 -0
- package/dist/blocks/layout.js +145 -0
- package/dist/blocks/types.d.ts +56 -0
- package/dist/blocks/types.js +1 -0
- package/dist/blocks/validate.d.ts +9 -0
- package/dist/blocks/validate.js +186 -0
- package/dist/capability-map/__tests__/validate.test.d.ts +1 -0
- package/dist/capability-map/__tests__/validate.test.js +212 -0
- package/dist/capability-map/index.d.ts +2 -0
- package/dist/capability-map/index.js +1 -0
- package/dist/capability-map/types.d.ts +26 -0
- package/dist/capability-map/types.js +1 -0
- package/dist/capability-map/validate.d.ts +3 -0
- package/dist/capability-map/validate.js +103 -0
- package/dist/change/__tests__/validate.test.d.ts +1 -0
- package/dist/change/__tests__/validate.test.js +73 -0
- package/dist/change/index.d.ts +2 -0
- package/dist/change/index.js +1 -0
- package/dist/change/types.d.ts +17 -0
- package/dist/change/types.js +3 -0
- package/dist/change/validate.d.ts +2 -0
- package/dist/change/validate.js +57 -0
- package/dist/compliance/__tests__/classify.test.d.ts +1 -0
- package/dist/compliance/__tests__/classify.test.js +24 -0
- package/dist/compliance/__tests__/confidence.test.d.ts +1 -0
- package/dist/compliance/__tests__/confidence.test.js +78 -0
- package/dist/compliance/__tests__/coverage-metric.test.d.ts +1 -0
- package/dist/compliance/__tests__/coverage-metric.test.js +164 -0
- package/dist/compliance/__tests__/demo-scenario.test.d.ts +1 -0
- package/dist/compliance/__tests__/demo-scenario.test.js +226 -0
- package/dist/compliance/__tests__/example.test.d.ts +1 -0
- package/dist/compliance/__tests__/example.test.js +51 -0
- package/dist/compliance/__tests__/gap-report.test.d.ts +1 -0
- package/dist/compliance/__tests__/gap-report.test.js +113 -0
- package/dist/compliance/__tests__/html.test.d.ts +1 -0
- package/dist/compliance/__tests__/html.test.js +122 -0
- package/dist/compliance/__tests__/impact.test.d.ts +1 -0
- package/dist/compliance/__tests__/impact.test.js +410 -0
- package/dist/compliance/__tests__/markdown.test.d.ts +1 -0
- package/dist/compliance/__tests__/markdown.test.js +85 -0
- package/dist/compliance/__tests__/views.test.d.ts +1 -0
- package/dist/compliance/__tests__/views.test.js +59 -0
- package/dist/compliance/classify.d.ts +27 -0
- package/dist/compliance/classify.js +60 -0
- package/dist/compliance/confidence.d.ts +27 -0
- package/dist/compliance/confidence.js +43 -0
- package/dist/compliance/coverage-metric.d.ts +74 -0
- package/dist/compliance/coverage-metric.js +181 -0
- package/dist/compliance/gap-report.d.ts +20 -0
- package/dist/compliance/gap-report.js +64 -0
- package/dist/compliance/html.d.ts +22 -0
- package/dist/compliance/html.js +256 -0
- package/dist/compliance/impact.d.ts +251 -0
- package/dist/compliance/impact.js +464 -0
- package/dist/compliance/index.d.ts +16 -0
- package/dist/compliance/index.js +9 -0
- package/dist/compliance/markdown.d.ts +18 -0
- package/dist/compliance/markdown.js +129 -0
- package/dist/compliance/reverse-index.d.ts +8 -0
- package/dist/compliance/reverse-index.js +30 -0
- package/dist/compliance/types.d.ts +98 -0
- package/dist/compliance/types.js +5 -0
- package/dist/compliance/views.d.ts +14 -0
- package/dist/compliance/views.js +35 -0
- package/dist/compliance-matrix/__tests__/build.test.d.ts +1 -0
- package/dist/compliance-matrix/__tests__/build.test.js +183 -0
- package/dist/compliance-matrix/__tests__/example.test.d.ts +1 -0
- package/dist/compliance-matrix/__tests__/example.test.js +49 -0
- package/dist/compliance-matrix/build.d.ts +18 -0
- package/dist/compliance-matrix/build.js +0 -0
- package/dist/compliance-matrix/index.d.ts +2 -0
- package/dist/compliance-matrix/index.js +1 -0
- package/dist/compliance-matrix/types.d.ts +86 -0
- package/dist/compliance-matrix/types.js +8 -0
- package/dist/confidence/__tests__/score.test.d.ts +1 -0
- package/dist/confidence/__tests__/score.test.js +188 -0
- package/dist/confidence/index.d.ts +2 -0
- package/dist/confidence/index.js +1 -0
- package/dist/confidence/score.d.ts +62 -0
- package/dist/confidence/score.js +215 -0
- package/dist/confidence/types.d.ts +79 -0
- package/dist/confidence/types.js +6 -0
- package/dist/edge-path.d.ts +18 -0
- package/dist/edge-path.js +35 -0
- package/dist/element-envelope.d.ts +15 -0
- package/dist/element-envelope.js +55 -0
- package/dist/factor/__tests__/validate.test.d.ts +1 -0
- package/dist/factor/__tests__/validate.test.js +83 -0
- package/dist/factor/index.d.ts +3 -0
- package/dist/factor/index.js +2 -0
- package/dist/factor/types.d.ts +23 -0
- package/dist/factor/types.js +6 -0
- package/dist/factor/validate.d.ts +2 -0
- package/dist/factor/validate.js +69 -0
- package/dist/fgca/__tests__/chain-table.test.d.ts +1 -0
- package/dist/fgca/__tests__/chain-table.test.js +158 -0
- package/dist/fgca/__tests__/layout.test.d.ts +1 -0
- package/dist/fgca/__tests__/layout.test.js +140 -0
- package/dist/fgca/__tests__/parse-canonical.test.d.ts +1 -0
- package/dist/fgca/__tests__/parse-canonical.test.js +224 -0
- package/dist/fgca/__tests__/preview-layout.test.d.ts +1 -0
- package/dist/fgca/__tests__/preview-layout.test.js +106 -0
- package/dist/fgca/__tests__/validate.test.d.ts +1 -0
- package/dist/fgca/__tests__/validate.test.js +125 -0
- package/dist/fgca/chain-table.d.ts +36 -0
- package/dist/fgca/chain-table.js +177 -0
- package/dist/fgca/index.d.ts +16 -0
- package/dist/fgca/index.js +11 -0
- package/dist/fgca/layout.d.ts +27 -0
- package/dist/fgca/layout.js +156 -0
- package/dist/fgca/nodeTypes.d.ts +6 -0
- package/dist/fgca/nodeTypes.js +10 -0
- package/dist/fgca/nodes/FGCAActivityNode.d.ts +13 -0
- package/dist/fgca/nodes/FGCAActivityNode.js +35 -0
- package/dist/fgca/nodes/FGCAChangeNode.d.ts +12 -0
- package/dist/fgca/nodes/FGCAChangeNode.js +35 -0
- package/dist/fgca/nodes/FGCAFactorNode.d.ts +12 -0
- package/dist/fgca/nodes/FGCAFactorNode.js +35 -0
- package/dist/fgca/nodes/FGCAGoalNode.d.ts +14 -0
- package/dist/fgca/nodes/FGCAGoalNode.js +40 -0
- package/dist/fgca/parse-canonical.d.ts +58 -0
- package/dist/fgca/parse-canonical.js +339 -0
- package/dist/fgca/preview-layout.d.ts +87 -0
- package/dist/fgca/preview-layout.js +122 -0
- package/dist/fgca/types.d.ts +39 -0
- package/dist/fgca/types.js +7 -0
- package/dist/fgca/validate.d.ts +14 -0
- package/dist/fgca/validate.js +108 -0
- package/dist/geometry.d.ts +7 -0
- package/dist/geometry.js +2 -0
- package/dist/goals/__tests__/layout.test.d.ts +1 -0
- package/dist/goals/__tests__/layout.test.js +153 -0
- package/dist/goals/__tests__/mutations.test.d.ts +1 -0
- package/dist/goals/__tests__/mutations.test.js +95 -0
- package/dist/goals/__tests__/parse-canonical.test.d.ts +1 -0
- package/dist/goals/__tests__/parse-canonical.test.js +129 -0
- package/dist/goals/__tests__/validate.test.d.ts +1 -0
- package/dist/goals/__tests__/validate.test.js +183 -0
- package/dist/goals/index.d.ts +6 -0
- package/dist/goals/index.js +4 -0
- package/dist/goals/layout.d.ts +11 -0
- package/dist/goals/layout.js +147 -0
- package/dist/goals/mutations.d.ts +6 -0
- package/dist/goals/mutations.js +116 -0
- package/dist/goals/parse-canonical.d.ts +29 -0
- package/dist/goals/parse-canonical.js +257 -0
- package/dist/goals/types.d.ts +71 -0
- package/dist/goals/types.js +1 -0
- package/dist/goals/validate.d.ts +2 -0
- package/dist/goals/validate.js +122 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +23 -0
- package/dist/issues/__tests__/layout.test.d.ts +1 -0
- package/dist/issues/__tests__/layout.test.js +62 -0
- package/dist/issues/__tests__/validate.test.d.ts +1 -0
- package/dist/issues/__tests__/validate.test.js +82 -0
- package/dist/issues/index.d.ts +4 -0
- package/dist/issues/index.js +2 -0
- package/dist/issues/layout.d.ts +10 -0
- package/dist/issues/layout.js +82 -0
- package/dist/issues/types.d.ts +61 -0
- package/dist/issues/types.js +6 -0
- package/dist/issues/validate.d.ts +3 -0
- package/dist/issues/validate.js +143 -0
- package/dist/process-blueprint/__tests__/compliance-lane.test.d.ts +1 -0
- package/dist/process-blueprint/__tests__/compliance-lane.test.js +272 -0
- package/dist/process-blueprint/__tests__/layout.test.d.ts +1 -0
- package/dist/process-blueprint/__tests__/layout.test.js +159 -0
- package/dist/process-blueprint/__tests__/validate.test.d.ts +1 -0
- package/dist/process-blueprint/__tests__/validate.test.js +280 -0
- package/dist/process-blueprint/index.d.ts +4 -0
- package/dist/process-blueprint/index.js +2 -0
- package/dist/process-blueprint/layout.d.ts +2 -0
- package/dist/process-blueprint/layout.js +416 -0
- package/dist/process-blueprint/types.d.ts +220 -0
- package/dist/process-blueprint/types.js +1 -0
- package/dist/process-blueprint/validate.d.ts +3 -0
- package/dist/process-blueprint/validate.js +165 -0
- package/dist/process-map/__tests__/validate.test.d.ts +1 -0
- package/dist/process-map/__tests__/validate.test.js +199 -0
- package/dist/process-map/index.d.ts +2 -0
- package/dist/process-map/index.js +1 -0
- package/dist/process-map/types.d.ts +32 -0
- package/dist/process-map/types.js +1 -0
- package/dist/process-map/validate.d.ts +3 -0
- package/dist/process-map/validate.js +115 -0
- package/dist/products/__tests__/validate.test.d.ts +1 -0
- package/dist/products/__tests__/validate.test.js +210 -0
- package/dist/products/index.d.ts +2 -0
- package/dist/products/index.js +1 -0
- package/dist/products/types.d.ts +28 -0
- package/dist/products/types.js +1 -0
- package/dist/products/validate.d.ts +3 -0
- package/dist/products/validate.js +93 -0
- package/dist/repo-validate/__tests__/validate-repo.test.d.ts +1 -0
- package/dist/repo-validate/__tests__/validate-repo.test.js +132 -0
- package/dist/repo-validate/index.d.ts +2 -0
- package/dist/repo-validate/index.js +1 -0
- package/dist/repo-validate/types.d.ts +34 -0
- package/dist/repo-validate/types.js +14 -0
- package/dist/repo-validate/validate-repo.d.ts +8 -0
- package/dist/repo-validate/validate-repo.js +189 -0
- package/dist/requirement/__tests__/example.test.d.ts +1 -0
- package/dist/requirement/__tests__/example.test.js +21 -0
- package/dist/requirement/__tests__/validate.test.d.ts +1 -0
- package/dist/requirement/__tests__/validate.test.js +80 -0
- package/dist/requirement/index.d.ts +4 -0
- package/dist/requirement/index.js +2 -0
- package/dist/requirement/types.d.ts +25 -0
- package/dist/requirement/types.js +4 -0
- package/dist/requirement/validate.d.ts +7 -0
- package/dist/requirement/validate.js +81 -0
- package/dist/scenarios/__tests__/validate.test.d.ts +1 -0
- package/dist/scenarios/__tests__/validate.test.js +183 -0
- package/dist/scenarios/index.d.ts +2 -0
- package/dist/scenarios/index.js +1 -0
- package/dist/scenarios/types.d.ts +45 -0
- package/dist/scenarios/types.js +1 -0
- package/dist/scenarios/validate.d.ts +3 -0
- package/dist/scenarios/validate.js +110 -0
- package/dist/schema-version.d.ts +16 -0
- package/dist/schema-version.js +16 -0
- package/dist/scope.d.ts +23 -0
- package/dist/scope.js +38 -0
- package/dist/stakeholder/__tests__/validate.test.d.ts +1 -0
- package/dist/stakeholder/__tests__/validate.test.js +86 -0
- package/dist/stakeholder/index.d.ts +3 -0
- package/dist/stakeholder/index.js +2 -0
- package/dist/stakeholder/types.d.ts +24 -0
- package/dist/stakeholder/types.js +5 -0
- package/dist/stakeholder/validate.d.ts +2 -0
- package/dist/stakeholder/validate.js +58 -0
- package/dist/target-state/__tests__/validate.test.d.ts +1 -0
- package/dist/target-state/__tests__/validate.test.js +62 -0
- package/dist/target-state/index.d.ts +2 -0
- package/dist/target-state/index.js +1 -0
- package/dist/target-state/types.d.ts +18 -0
- package/dist/target-state/types.js +7 -0
- package/dist/target-state/validate.d.ts +2 -0
- package/dist/target-state/validate.js +61 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +2 -0
- package/dist/theme/themes.d.ts +16 -0
- package/dist/theme/themes.js +213 -0
- package/dist/theme/tokens.d.ts +158 -0
- package/dist/theme/tokens.js +105 -0
- package/dist/typed-id.d.ts +20 -0
- package/dist/typed-id.js +38 -0
- package/dist/validation-types.d.ts +34 -0
- package/dist/validation-types.js +1 -0
- package/dist/webview/__tests__/entry-notations.test.d.ts +1 -0
- package/dist/webview/__tests__/entry-notations.test.js +62 -0
- package/dist/webview/__tests__/entry.test.d.ts +1 -0
- package/dist/webview/__tests__/entry.test.js +56 -0
- package/dist/webview/__tests__/render-goals.test.d.ts +1 -0
- package/dist/webview/__tests__/render-goals.test.js +65 -0
- package/dist/webview/entry.d.ts +38 -0
- package/dist/webview/entry.js +182 -0
- package/dist/webview/render-activities.d.ts +24 -0
- package/dist/webview/render-activities.js +127 -0
- package/dist/webview/render-activity-card.d.ts +5 -0
- package/dist/webview/render-activity-card.js +168 -0
- package/dist/webview/render-applications.d.ts +11 -0
- package/dist/webview/render-applications.js +105 -0
- package/dist/webview/render-blocks.d.ts +5 -0
- package/dist/webview/render-blocks.js +71 -0
- package/dist/webview/render-capability-map.d.ts +7 -0
- package/dist/webview/render-capability-map.js +88 -0
- package/dist/webview/render-fgca.d.ts +8 -0
- package/dist/webview/render-fgca.js +94 -0
- package/dist/webview/render-goals.d.ts +6 -0
- package/dist/webview/render-goals.js +92 -0
- package/dist/webview/render-process-blueprint.d.ts +5 -0
- package/dist/webview/render-process-blueprint.js +135 -0
- package/dist/webview/render-process-map.d.ts +7 -0
- package/dist/webview/render-process-map.js +82 -0
- package/dist/webview/render-products.d.ts +7 -0
- package/dist/webview/render-products.js +81 -0
- package/dist/webview/render-scenarios.d.ts +7 -0
- package/dist/webview/render-scenarios.js +75 -0
- package/dist/webview/render-util.d.ts +16 -0
- package/dist/webview/render-util.js +31 -0
- package/dist/webview/transitrix-render.css +323 -0
- package/dist/webview/transitrix-render.js +6452 -0
- package/dist/webview/transitrix-render.js.map +7 -0
- package/dist/yaml-normalize.d.ts +20 -0
- package/dist/yaml-normalize.js +57 -0
- package/package.json +40 -0
- package/src/__tests__/edge-path.test.ts +46 -0
- package/src/__tests__/scope.test.ts +27 -0
- package/src/__tests__/yaml-normalize.test.ts +158 -0
- package/src/activities/__tests__/cpm.test.ts +164 -0
- package/src/activities/__tests__/gantt.test.ts +245 -0
- package/src/activities/__tests__/layout.test.ts +112 -0
- package/src/activities/__tests__/validate.test.ts +409 -0
- package/src/activities/cpm.ts +95 -0
- package/src/activities/gantt.ts +276 -0
- package/src/activities/index.ts +28 -0
- package/src/activities/layout.ts +112 -0
- package/src/activities/types.ts +148 -0
- package/src/activities/validate.ts +357 -0
- package/src/activity-card/__tests__/example.test.ts +66 -0
- package/src/activity-card/__tests__/layout.test.ts +63 -0
- package/src/activity-card/__tests__/resolver.test.ts +161 -0
- package/src/activity-card/__tests__/validate.test.ts +131 -0
- package/src/activity-card/index.ts +32 -0
- package/src/activity-card/layout.ts +194 -0
- package/src/activity-card/resolver.ts +337 -0
- package/src/activity-card/types.ts +237 -0
- package/src/activity-card/validate.ts +158 -0
- package/src/actor/__tests__/validate.test.ts +67 -0
- package/src/actor/index.ts +3 -0
- package/src/actor/types.ts +29 -0
- package/src/actor/validate.ts +38 -0
- package/src/applications/__tests__/validate.test.ts +256 -0
- package/src/applications/index.ts +11 -0
- package/src/applications/types.ts +43 -0
- package/src/applications/validate.ts +131 -0
- package/src/assertion/__tests__/example.test.ts +37 -0
- package/src/assertion/__tests__/validate.test.ts +142 -0
- package/src/assertion/index.ts +12 -0
- package/src/assertion/types.ts +58 -0
- package/src/assertion/validate.ts +145 -0
- package/src/blocks/__tests__/layout.test.ts +169 -0
- package/src/blocks/__tests__/validate.test.ts +260 -0
- package/src/blocks/index.ts +17 -0
- package/src/blocks/layout.ts +186 -0
- package/src/blocks/types.ts +62 -0
- package/src/blocks/validate.ts +206 -0
- package/src/capability-map/__tests__/validate.test.ts +238 -0
- package/src/capability-map/index.ts +8 -0
- package/src/capability-map/types.ts +29 -0
- package/src/capability-map/validate.ts +127 -0
- package/src/change/__tests__/validate.test.ts +88 -0
- package/src/change/index.ts +2 -0
- package/src/change/types.ts +23 -0
- package/src/change/validate.ts +61 -0
- package/src/compliance/__tests__/classify.test.ts +27 -0
- package/src/compliance/__tests__/confidence.test.ts +98 -0
- package/src/compliance/__tests__/coverage-metric.test.ts +188 -0
- package/src/compliance/__tests__/demo-scenario.test.ts +273 -0
- package/src/compliance/__tests__/example.test.ts +57 -0
- package/src/compliance/__tests__/gap-report.test.ts +129 -0
- package/src/compliance/__tests__/html.test.ts +142 -0
- package/src/compliance/__tests__/impact.test.ts +482 -0
- package/src/compliance/__tests__/markdown.test.ts +95 -0
- package/src/compliance/__tests__/views.test.ts +64 -0
- package/src/compliance/classify.ts +84 -0
- package/src/compliance/confidence.ts +69 -0
- package/src/compliance/coverage-metric.ts +263 -0
- package/src/compliance/gap-report.ts +94 -0
- package/src/compliance/html.ts +295 -0
- package/src/compliance/impact.ts +677 -0
- package/src/compliance/index.ts +56 -0
- package/src/compliance/markdown.ts +135 -0
- package/src/compliance/reverse-index.ts +33 -0
- package/src/compliance/types.ts +120 -0
- package/src/compliance/views.ts +46 -0
- package/src/compliance-matrix/__tests__/build.test.ts +200 -0
- package/src/compliance-matrix/__tests__/example.test.ts +57 -0
- package/src/compliance-matrix/build.ts +0 -0
- package/src/compliance-matrix/index.ts +11 -0
- package/src/compliance-matrix/types.ts +102 -0
- package/src/confidence/__tests__/score.test.ts +254 -0
- package/src/confidence/index.ts +21 -0
- package/src/confidence/score.ts +243 -0
- package/src/confidence/types.ts +96 -0
- package/src/edge-path.ts +44 -0
- package/src/element-envelope.ts +76 -0
- package/src/factor/__tests__/validate.test.ts +98 -0
- package/src/factor/index.ts +8 -0
- package/src/factor/types.ts +41 -0
- package/src/factor/validate.ts +74 -0
- package/src/fgca/__tests__/chain-table.test.ts +172 -0
- package/src/fgca/__tests__/layout.test.ts +179 -0
- package/src/fgca/__tests__/parse-canonical.test.ts +248 -0
- package/src/fgca/__tests__/preview-layout.test.ts +124 -0
- package/src/fgca/__tests__/validate.test.ts +142 -0
- package/src/fgca/chain-table.ts +225 -0
- package/src/fgca/index.ts +45 -0
- package/src/fgca/layout.ts +204 -0
- package/src/fgca/nodeTypes.ts +11 -0
- package/src/fgca/nodes/FGCAActivityNode.tsx +65 -0
- package/src/fgca/nodes/FGCAChangeNode.tsx +64 -0
- package/src/fgca/nodes/FGCAFactorNode.tsx +64 -0
- package/src/fgca/nodes/FGCAGoalNode.tsx +72 -0
- package/src/fgca/parse-canonical.ts +400 -0
- package/src/fgca/preview-layout.ts +217 -0
- package/src/fgca/types.ts +50 -0
- package/src/fgca/validate.ts +126 -0
- package/src/geometry.ts +9 -0
- package/src/goals/__tests__/layout.test.ts +175 -0
- package/src/goals/__tests__/mutations.test.ts +111 -0
- package/src/goals/__tests__/parse-canonical.test.ts +145 -0
- package/src/goals/__tests__/validate.test.ts +202 -0
- package/src/goals/index.ts +21 -0
- package/src/goals/layout.ts +172 -0
- package/src/goals/mutations.ts +123 -0
- package/src/goals/parse-canonical.ts +286 -0
- package/src/goals/types.ts +85 -0
- package/src/goals/validate.ts +136 -0
- package/src/index.ts +23 -0
- package/src/process-blueprint/__tests__/compliance-lane.test.ts +320 -0
- package/src/process-blueprint/__tests__/layout.test.ts +195 -0
- package/src/process-blueprint/__tests__/validate.test.ts +310 -0
- package/src/process-blueprint/index.ts +31 -0
- package/src/process-blueprint/layout.ts +531 -0
- package/src/process-blueprint/types.ts +242 -0
- package/src/process-blueprint/validate.ts +181 -0
- package/src/process-map/__tests__/validate.test.ts +223 -0
- package/src/process-map/index.ts +10 -0
- package/src/process-map/types.ts +36 -0
- package/src/process-map/validate.ts +135 -0
- package/src/products/__tests__/validate.test.ts +239 -0
- package/src/products/index.ts +9 -0
- package/src/products/types.ts +31 -0
- package/src/products/validate.ts +112 -0
- package/src/repo-validate/__tests__/validate-repo.test.ts +149 -0
- package/src/repo-validate/index.ts +2 -0
- package/src/repo-validate/types.ts +50 -0
- package/src/repo-validate/validate-repo.ts +196 -0
- package/src/requirement/__tests__/example.test.ts +24 -0
- package/src/requirement/__tests__/validate.test.ts +96 -0
- package/src/requirement/index.ts +4 -0
- package/src/requirement/types.ts +35 -0
- package/src/requirement/validate.ts +92 -0
- package/src/scenarios/__tests__/validate.test.ts +209 -0
- package/src/scenarios/index.ts +15 -0
- package/src/scenarios/types.ts +37 -0
- package/src/scenarios/validate.ts +122 -0
- package/src/schema-version.ts +16 -0
- package/src/scope.ts +46 -0
- package/src/stakeholder/__tests__/validate.test.ts +101 -0
- package/src/stakeholder/index.ts +3 -0
- package/src/stakeholder/types.ts +33 -0
- package/src/stakeholder/validate.ts +63 -0
- package/src/target-state/__tests__/validate.test.ts +74 -0
- package/src/target-state/index.ts +2 -0
- package/src/target-state/types.ts +28 -0
- package/src/target-state/validate.ts +67 -0
- package/src/theme/index.ts +4 -0
- package/src/theme/themes.ts +237 -0
- package/src/theme/tokens.ts +133 -0
- package/src/typed-id.ts +56 -0
- package/src/validation-types.ts +36 -0
- package/src/webview/__tests__/entry-notations.test.ts +77 -0
- package/src/webview/__tests__/entry.test.ts +64 -0
- package/src/webview/__tests__/render-goals.test.ts +72 -0
- package/src/webview/entry.ts +292 -0
- package/src/webview/render-activities.ts +156 -0
- package/src/webview/render-activity-card.ts +234 -0
- package/src/webview/render-applications.ts +123 -0
- package/src/webview/render-blocks.ts +91 -0
- package/src/webview/render-capability-map.ts +98 -0
- package/src/webview/render-fgca.ts +122 -0
- package/src/webview/render-goals.ts +110 -0
- package/src/webview/render-process-blueprint.ts +207 -0
- package/src/webview/render-process-map.ts +93 -0
- package/src/webview/render-products.ts +91 -0
- package/src/webview/render-scenarios.ts +90 -0
- package/src/webview/render-util.ts +34 -0
- package/src/webview/styles.css +323 -0
- package/src/yaml-normalize.ts +56 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Compliance matrix model (vkgeorgia/strategy#84 Phase 2).
|
|
2
|
+
//
|
|
3
|
+
// The matrix is Products × Requirements. Each cell is the status of the
|
|
4
|
+
// (Product, Requirement) ASSERTION when one exists, or a gap when none does.
|
|
5
|
+
// Pure data — the layout is computed by `build.ts`; the Studio preview renders
|
|
6
|
+
// it. Non-product-subject assertions (CAPABILITY / PROCESS) belong to the
|
|
7
|
+
// single-product / single-law views (Phase 3) and are ignored here.
|
|
8
|
+
|
|
9
|
+
import type { AssertionStatus } from '../assertion/types.js';
|
|
10
|
+
|
|
11
|
+
/** A row of the matrix — a discovered PRODUCT element. */
|
|
12
|
+
export interface MatrixProduct {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
/** True when the product was discovered only as an assertion subject (no
|
|
16
|
+
* product element file resolves it) — a dangling subject reference. */
|
|
17
|
+
unresolved?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** A column of the matrix — a discovered REQUIREMENT. */
|
|
21
|
+
export interface MatrixRequirement {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
/** Organisation-defined priority (high | medium | low), used by the filter. */
|
|
25
|
+
severity?: string;
|
|
26
|
+
/** Typed IDs of the codex sources this requirement derives from (verbatim
|
|
27
|
+
* from `derived_from`). Carried so the renderer can surface provenance and
|
|
28
|
+
* resolve jurisdictions without re-scanning. */
|
|
29
|
+
derived_from?: string[];
|
|
30
|
+
/** Jurisdictions resolved by walking `derived_from` → codex artefacts and
|
|
31
|
+
* collecting their `jurisdiction` (Codex §1.1). Unique, sorted. Empty when
|
|
32
|
+
* no codex source resolves with a jurisdiction (internal-only requirements,
|
|
33
|
+
* or codex artefacts the scan did not find / has no jurisdiction). The
|
|
34
|
+
* toolbar jurisdiction filter (F16) keys on this set. */
|
|
35
|
+
jurisdictions?: string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A codex source document the matrix uses to resolve `Requirement.jurisdictions`. */
|
|
39
|
+
export interface MatrixCodexDoc {
|
|
40
|
+
id: string;
|
|
41
|
+
jurisdiction?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The assertion fields the matrix needs (a projection of the full Assertion). */
|
|
45
|
+
export interface MatrixAssertionRef {
|
|
46
|
+
id: string;
|
|
47
|
+
about: string;
|
|
48
|
+
subject: string;
|
|
49
|
+
status: AssertionStatus;
|
|
50
|
+
assessed_at?: string;
|
|
51
|
+
next_review_at?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** One grid cell. `status === undefined` is a gap (no assertion for the pair). */
|
|
55
|
+
export interface MatrixCell {
|
|
56
|
+
productId: string;
|
|
57
|
+
requirementId: string;
|
|
58
|
+
/** The assertion filling this cell, when one exists. */
|
|
59
|
+
assertionId?: string;
|
|
60
|
+
status?: AssertionStatus;
|
|
61
|
+
assessed_at?: string;
|
|
62
|
+
next_review_at?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ComplianceMatrix {
|
|
66
|
+
/** Rows, sorted by id. */
|
|
67
|
+
products: MatrixProduct[];
|
|
68
|
+
/** Columns, sorted by id. */
|
|
69
|
+
requirements: MatrixRequirement[];
|
|
70
|
+
/** `cells[rowIndex][colIndex]`, aligned to `products` × `requirements`. */
|
|
71
|
+
cells: MatrixCell[][];
|
|
72
|
+
/** Counts for the summary strip. */
|
|
73
|
+
summary: {
|
|
74
|
+
products: number;
|
|
75
|
+
requirements: number;
|
|
76
|
+
assertions: number;
|
|
77
|
+
/** Cells with no assertion (compliance gaps). */
|
|
78
|
+
gaps: number;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface ComplianceMatrixInput {
|
|
83
|
+
products: MatrixProduct[];
|
|
84
|
+
requirements: MatrixRequirement[];
|
|
85
|
+
assertions: MatrixAssertionRef[];
|
|
86
|
+
/** Codex source documents — used to resolve each Requirement's jurisdictions
|
|
87
|
+
* by walking `derived_from`. Optional: when omitted the builder still works
|
|
88
|
+
* and every requirement's `jurisdictions` is left empty. */
|
|
89
|
+
codex?: MatrixCodexDoc[];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Toolbar filter — empty/omitted fields mean "no filter on that dimension". */
|
|
93
|
+
export interface MatrixFilter {
|
|
94
|
+
/** Keep only requirement columns whose severity is in this set. */
|
|
95
|
+
severities?: string[];
|
|
96
|
+
/** Keep only the cell status badges in this set; others render as filtered. */
|
|
97
|
+
statuses?: AssertionStatus[];
|
|
98
|
+
/** Keep only requirement columns whose resolved jurisdictions intersect this
|
|
99
|
+
* set. Requirements with no resolved jurisdiction are filtered out when this
|
|
100
|
+
* is non-empty (F16, epic #84). */
|
|
101
|
+
jurisdictions?: string[];
|
|
102
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// Pins CONTRACT §11 — source-trust scale, freshness curve, element confidence,
|
|
2
|
+
// view composite, unsourced handling, manifest decay resolution, A–D bands.
|
|
3
|
+
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import {
|
|
6
|
+
IMPLICIT_DECAY_DEFAULTS,
|
|
7
|
+
composeView,
|
|
8
|
+
confidenceBand,
|
|
9
|
+
formatConfidenceHeader,
|
|
10
|
+
freshness,
|
|
11
|
+
resolveDecayParams,
|
|
12
|
+
scoreElement,
|
|
13
|
+
scoreView,
|
|
14
|
+
sourceTrustWeight,
|
|
15
|
+
} from '../score.js';
|
|
16
|
+
import type { ConfidenceDecayConfig, DecayParams } from '../types.js';
|
|
17
|
+
|
|
18
|
+
const D: DecayParams = { fresh_days: 100, stale_days: 200, floor: 0.3 };
|
|
19
|
+
|
|
20
|
+
describe('sourceTrustWeight (§11.2)', () => {
|
|
21
|
+
it('matches the closed scale', () => {
|
|
22
|
+
expect(sourceTrustWeight('authoritative')).toBe(1.0);
|
|
23
|
+
expect(sourceTrustWeight('corroborated')).toBe(0.8);
|
|
24
|
+
expect(sourceTrustWeight('single_source')).toBe(0.5);
|
|
25
|
+
expect(sourceTrustWeight('unverified')).toBe(0.25);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('freshness (§11.3)', () => {
|
|
30
|
+
const today = '2026-06-05';
|
|
31
|
+
|
|
32
|
+
it('returns 1.0 when age ≤ fresh_days', () => {
|
|
33
|
+
expect(freshness('2026-05-01', today, D)).toBe(1.0);
|
|
34
|
+
expect(freshness('2026-06-05', today, D)).toBe(1.0);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('returns floor when age ≥ stale_days', () => {
|
|
38
|
+
expect(freshness('2025-01-01', today, D)).toBeCloseTo(0.3, 10);
|
|
39
|
+
const exactlyStale = '2025-11-17';
|
|
40
|
+
expect(freshness(exactlyStale, today, { fresh_days: 100, stale_days: 200, floor: 0.3 })).toBeCloseTo(0.3, 10);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('interpolates linearly between fresh_days and stale_days', () => {
|
|
44
|
+
// midpoint: age 150 → 1.0 − (1.0 − 0.3) · (150 − 100) / (200 − 100) = 0.65
|
|
45
|
+
const midAge = new Date(Date.UTC(2026, 5, 5));
|
|
46
|
+
midAge.setUTCDate(midAge.getUTCDate() - 150);
|
|
47
|
+
const iso = midAge.toISOString().slice(0, 10);
|
|
48
|
+
expect(freshness(iso, today, D)).toBeCloseTo(0.65, 10);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('clamps future-dated admitted_at to 1.0', () => {
|
|
52
|
+
expect(freshness('2030-01-01', today, D)).toBe(1.0);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('treats absent admitted_at as fully stale (returns floor)', () => {
|
|
56
|
+
expect(freshness(undefined, today, D)).toBe(0.3);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('treats unparseable admitted_at as fully stale', () => {
|
|
60
|
+
expect(freshness('not-a-date', today, D)).toBe(0.3);
|
|
61
|
+
expect(freshness('2026/06/05', today, D)).toBe(0.3);
|
|
62
|
+
expect(freshness('2026-13-01', today, D)).toBe(0.3);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('returns floor when stale_days <= fresh_days (degenerate config) and age exceeds fresh', () => {
|
|
66
|
+
// age 365d for 2025-06-05; both branches collapse to floor.
|
|
67
|
+
expect(freshness('2025-06-05', today, { fresh_days: 100, stale_days: 100, floor: 0.4 })).toBe(0.4);
|
|
68
|
+
expect(freshness('2025-06-05', today, { fresh_days: 200, stale_days: 100, floor: 0.4 })).toBe(0.4);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('resolveDecayParams (§11.3 + MANIFEST §2)', () => {
|
|
73
|
+
const config: ConfidenceDecayConfig = {
|
|
74
|
+
defaults: { fresh_days: 180, stale_days: 730, floor: 0.3 },
|
|
75
|
+
by_type: {
|
|
76
|
+
CAPABILITY: { fresh_days: 365, stale_days: 1825 },
|
|
77
|
+
APPLICATION: { fresh_days: 180, stale_days: 730 },
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
it('uses by_type overrides and inherits missing fields from defaults', () => {
|
|
82
|
+
// Pins the §11.3 example: CAPABILITY overrides fresh/stale but inherits floor=0.3.
|
|
83
|
+
expect(resolveDecayParams('CAPABILITY', config)).toEqual({ fresh_days: 365, stale_days: 1825, floor: 0.3 });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('falls back to defaults for an unknown TYPE', () => {
|
|
87
|
+
expect(resolveDecayParams('PROCESS', config)).toEqual({ fresh_days: 180, stale_days: 730, floor: 0.3 });
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('falls back to §11.3 implicit defaults when the whole block is omitted', () => {
|
|
91
|
+
expect(resolveDecayParams('CAPABILITY', undefined)).toEqual(IMPLICIT_DECAY_DEFAULTS);
|
|
92
|
+
expect(resolveDecayParams('CAPABILITY', {})).toEqual(IMPLICIT_DECAY_DEFAULTS);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('fills missing defaults fields from §11.3 implicit defaults', () => {
|
|
96
|
+
const partial: ConfidenceDecayConfig = { defaults: { floor: 0.5 } };
|
|
97
|
+
expect(resolveDecayParams('PROCESS', partial)).toEqual({
|
|
98
|
+
fresh_days: IMPLICIT_DECAY_DEFAULTS.fresh_days,
|
|
99
|
+
stale_days: IMPLICIT_DECAY_DEFAULTS.stale_days,
|
|
100
|
+
floor: 0.5,
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('scoreElement (§11.4–11.5)', () => {
|
|
106
|
+
const today = '2026-06-05';
|
|
107
|
+
|
|
108
|
+
it('takes the max source-trust weight (§11.4): extra weak sources never subtract', () => {
|
|
109
|
+
const score = scoreElement(
|
|
110
|
+
{ type: 'CAPABILITY', admitted_at: today, sources: ['unverified', 'authoritative', 'single_source'] },
|
|
111
|
+
today,
|
|
112
|
+
);
|
|
113
|
+
expect(score.source_trust).toBe(1.0);
|
|
114
|
+
expect(score.confidence).toBe(1.0);
|
|
115
|
+
expect(score.sourced).toBe(true);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('multiplies source_trust by freshness', () => {
|
|
119
|
+
// §11.4 worked example: authoritative · floor.
|
|
120
|
+
const score = scoreElement(
|
|
121
|
+
{ type: 'X', admitted_at: '2024-01-01', sources: ['authoritative'] },
|
|
122
|
+
today,
|
|
123
|
+
{ defaults: D },
|
|
124
|
+
);
|
|
125
|
+
expect(score.source_trust).toBe(1.0);
|
|
126
|
+
expect(score.freshness).toBeCloseTo(0.3, 10);
|
|
127
|
+
expect(score.confidence).toBeCloseTo(0.3, 10);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('marks unsourced elements at the unverified floor (§11.5) and sourced=false', () => {
|
|
131
|
+
const undef = scoreElement({ type: 'X', admitted_at: today }, today);
|
|
132
|
+
const empty = scoreElement({ type: 'X', admitted_at: today, sources: [] }, today);
|
|
133
|
+
for (const score of [undef, empty]) {
|
|
134
|
+
expect(score.source_trust).toBe(0.25);
|
|
135
|
+
expect(score.sourced).toBe(false);
|
|
136
|
+
expect(score.confidence).toBeCloseTo(0.25, 10);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('distinguishes unsourced (counted separately) from sourced-but-unverified', () => {
|
|
141
|
+
const unsourced = scoreElement({ type: 'X', admitted_at: today }, today);
|
|
142
|
+
const sourcedUnverified = scoreElement(
|
|
143
|
+
{ type: 'X', admitted_at: today, sources: ['unverified'] },
|
|
144
|
+
today,
|
|
145
|
+
);
|
|
146
|
+
expect(unsourced.source_trust).toBe(sourcedUnverified.source_trust);
|
|
147
|
+
expect(unsourced.sourced).toBe(false);
|
|
148
|
+
expect(sourcedUnverified.sourced).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe('confidenceBand (§11.6)', () => {
|
|
153
|
+
it('uses the documented thresholds', () => {
|
|
154
|
+
expect(confidenceBand(1.0)).toBe('A');
|
|
155
|
+
expect(confidenceBand(0.8)).toBe('A');
|
|
156
|
+
expect(confidenceBand(0.79)).toBe('B');
|
|
157
|
+
expect(confidenceBand(0.6)).toBe('B');
|
|
158
|
+
expect(confidenceBand(0.59)).toBe('C');
|
|
159
|
+
expect(confidenceBand(0.4)).toBe('C');
|
|
160
|
+
expect(confidenceBand(0.39)).toBe('D');
|
|
161
|
+
expect(confidenceBand(0)).toBe('D');
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('composeView (§11.6)', () => {
|
|
166
|
+
it('headlines weakest link, reports mean and coverage', () => {
|
|
167
|
+
const composite = composeView([
|
|
168
|
+
{ source_trust: 1.0, freshness: 1.0, confidence: 1.0, sourced: true },
|
|
169
|
+
{ source_trust: 0.5, freshness: 0.8, confidence: 0.4, sourced: true },
|
|
170
|
+
{ source_trust: 0.25, freshness: 1.0, confidence: 0.25, sourced: false },
|
|
171
|
+
]);
|
|
172
|
+
expect(composite.weakest_link).toBeCloseTo(0.25, 10);
|
|
173
|
+
expect(composite.band).toBe('D');
|
|
174
|
+
expect(composite.mean).toBeCloseTo((1.0 + 0.4 + 0.25) / 3, 10);
|
|
175
|
+
expect(composite.coverage).toBeCloseTo(2 / 3, 10);
|
|
176
|
+
expect(composite.element_count).toBe(3);
|
|
177
|
+
expect(composite.unsourced_count).toBe(1);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('returns a degenerate composite (band D, counts 0) for an empty set', () => {
|
|
181
|
+
const composite = composeView([]);
|
|
182
|
+
expect(composite.element_count).toBe(0);
|
|
183
|
+
expect(composite.weakest_link).toBe(0);
|
|
184
|
+
expect(composite.mean).toBe(0);
|
|
185
|
+
expect(composite.coverage).toBe(0);
|
|
186
|
+
expect(composite.band).toBe('D');
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe('scoreView end-to-end', () => {
|
|
191
|
+
const today = '2026-06-05';
|
|
192
|
+
|
|
193
|
+
it('composes weakest-link / mean / coverage / band from heterogenous elements', () => {
|
|
194
|
+
const view = scoreView(
|
|
195
|
+
[
|
|
196
|
+
// weakest link = 0.50 → band C
|
|
197
|
+
{ type: 'X', admitted_at: today, sources: ['authoritative'] }, // 1.00
|
|
198
|
+
{ type: 'X', admitted_at: today, sources: ['corroborated'] }, // 0.80
|
|
199
|
+
{ type: 'X', admitted_at: today, sources: ['single_source'] }, // 0.50
|
|
200
|
+
{ type: 'X', admitted_at: today, sources: ['corroborated'] }, // 0.80
|
|
201
|
+
],
|
|
202
|
+
today,
|
|
203
|
+
);
|
|
204
|
+
expect(view.composite.weakest_link).toBeCloseTo(0.5, 10);
|
|
205
|
+
expect(view.composite.band).toBe('C');
|
|
206
|
+
expect(view.composite.mean).toBeCloseTo((1.0 + 0.8 + 0.5 + 0.8) / 4, 10);
|
|
207
|
+
expect(view.composite.coverage).toBe(1);
|
|
208
|
+
expect(view.composite.unsourced_count).toBe(0);
|
|
209
|
+
expect(view.today).toBe(today);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('accepts Date for today and converts to ISO', () => {
|
|
213
|
+
const view = scoreView(
|
|
214
|
+
[{ type: 'X', admitted_at: '2026-06-05', sources: ['authoritative'] }],
|
|
215
|
+
new Date(Date.UTC(2026, 5, 5)),
|
|
216
|
+
);
|
|
217
|
+
expect(view.today).toBe('2026-06-05');
|
|
218
|
+
expect(view.composite.band).toBe('A');
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('reports band D and 0% coverage when every element is unsourced and fully stale', () => {
|
|
222
|
+
const view = scoreView(
|
|
223
|
+
[
|
|
224
|
+
{ type: 'X' },
|
|
225
|
+
{ type: 'X' },
|
|
226
|
+
],
|
|
227
|
+
'2026-06-05',
|
|
228
|
+
{ defaults: D },
|
|
229
|
+
);
|
|
230
|
+
// unsourced (0.25) · freshness floor (0.3) = 0.075 → band D.
|
|
231
|
+
expect(view.composite.coverage).toBe(0);
|
|
232
|
+
expect(view.composite.unsourced_count).toBe(2);
|
|
233
|
+
expect(view.composite.band).toBe('D');
|
|
234
|
+
expect(view.composite.weakest_link).toBeCloseTo(0.075, 10);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe('formatConfidenceHeader (§11.6)', () => {
|
|
239
|
+
it('renders the documented header shape', () => {
|
|
240
|
+
const view = scoreView(
|
|
241
|
+
[{ type: 'X', admitted_at: '2026-06-05', sources: ['corroborated'] }],
|
|
242
|
+
'2026-06-05',
|
|
243
|
+
);
|
|
244
|
+
// corroborated (0.8) · freshness 1.0 = 0.80 → band A (A is ≥ 0.8).
|
|
245
|
+
expect(formatConfidenceHeader(view)).toBe(
|
|
246
|
+
'Data confidence (as of 2026-06-05): A (weakest link) · 0.80 mean · 100% sourced',
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('returns an empty string for an empty composite', () => {
|
|
251
|
+
const view = scoreView([], '2026-06-05');
|
|
252
|
+
expect(formatConfidenceHeader(view)).toBe('');
|
|
253
|
+
});
|
|
254
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export {
|
|
2
|
+
IMPLICIT_DECAY_DEFAULTS,
|
|
3
|
+
composeView,
|
|
4
|
+
confidenceBand,
|
|
5
|
+
formatConfidenceHeader,
|
|
6
|
+
freshness,
|
|
7
|
+
resolveDecayParams,
|
|
8
|
+
scoreElement,
|
|
9
|
+
scoreView,
|
|
10
|
+
sourceTrustWeight,
|
|
11
|
+
} from './score.js';
|
|
12
|
+
export type {
|
|
13
|
+
ConfidenceBand,
|
|
14
|
+
ConfidenceDecayConfig,
|
|
15
|
+
DecayParams,
|
|
16
|
+
ElementScore,
|
|
17
|
+
ScoringElement,
|
|
18
|
+
SourceQuality,
|
|
19
|
+
ViewComposite,
|
|
20
|
+
ViewScore,
|
|
21
|
+
} from './types.js';
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// Pure confidence-scoring per methodology CONTRACT §11. No I/O, no mutation:
|
|
2
|
+
// callers (Studio previews, DSM React) pass already-resolved element inputs
|
|
3
|
+
// and a `today` reference; this module returns the per-element scores and
|
|
4
|
+
// the view composite. Lives in @transitrix/diagrams so Studio and DSM share
|
|
5
|
+
// one implementation (see vkgeorgia/strategy#159 — DQ-1).
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
ConfidenceBand,
|
|
9
|
+
ConfidenceDecayConfig,
|
|
10
|
+
DecayParams,
|
|
11
|
+
ElementScore,
|
|
12
|
+
ScoringElement,
|
|
13
|
+
SourceQuality,
|
|
14
|
+
ViewComposite,
|
|
15
|
+
ViewScore,
|
|
16
|
+
} from './types.js';
|
|
17
|
+
|
|
18
|
+
/** §11.3 implicit defaults — used when the adopter omits `confidence_decay`. */
|
|
19
|
+
export const IMPLICIT_DECAY_DEFAULTS: DecayParams = {
|
|
20
|
+
fresh_days: 180,
|
|
21
|
+
stale_days: 730,
|
|
22
|
+
floor: 0.3,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** §11.2 source-trust weights. The fallback for an unknown label is `unverified`. */
|
|
26
|
+
const SOURCE_TRUST_WEIGHTS: Record<SourceQuality, number> = {
|
|
27
|
+
authoritative: 1.0,
|
|
28
|
+
corroborated: 0.8,
|
|
29
|
+
single_source: 0.5,
|
|
30
|
+
unverified: 0.25,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** Weight for the `unverified` floor — both an explicit label and the unsourced fallback. */
|
|
34
|
+
const UNVERIFIED_WEIGHT = SOURCE_TRUST_WEIGHTS.unverified;
|
|
35
|
+
|
|
36
|
+
export function sourceTrustWeight(q: SourceQuality): number {
|
|
37
|
+
return SOURCE_TRUST_WEIGHTS[q] ?? UNVERIFIED_WEIGHT;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Resolves the decay parameters for one element TYPE: `by_type[type]` overrides
|
|
42
|
+
* `defaults`, which overrides {@link IMPLICIT_DECAY_DEFAULTS}. Each field
|
|
43
|
+
* resolves independently — a `by_type` entry that sets only `fresh_days` /
|
|
44
|
+
* `stale_days` inherits `floor` from `defaults` (per the §11.3 example).
|
|
45
|
+
*/
|
|
46
|
+
export function resolveDecayParams(
|
|
47
|
+
type: string,
|
|
48
|
+
config?: ConfidenceDecayConfig,
|
|
49
|
+
): DecayParams {
|
|
50
|
+
const typeOverride = config?.by_type?.[type] ?? {};
|
|
51
|
+
const defaults = config?.defaults ?? {};
|
|
52
|
+
return {
|
|
53
|
+
fresh_days: pick(typeOverride.fresh_days, defaults.fresh_days, IMPLICIT_DECAY_DEFAULTS.fresh_days),
|
|
54
|
+
stale_days: pick(typeOverride.stale_days, defaults.stale_days, IMPLICIT_DECAY_DEFAULTS.stale_days),
|
|
55
|
+
floor: pick(typeOverride.floor, defaults.floor, IMPLICIT_DECAY_DEFAULTS.floor),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function pick(...values: (number | undefined)[]): number {
|
|
60
|
+
for (const v of values) if (typeof v === 'number' && Number.isFinite(v)) return v;
|
|
61
|
+
// The trailing implicit default is always finite; this is only reached if a
|
|
62
|
+
// caller passes a non-finite implicit default — which we treat as 0.
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Computes freshness per CONTRACT §11.3.
|
|
68
|
+
*
|
|
69
|
+
* ```
|
|
70
|
+
* freshness = 1.0 if age_days ≤ fresh_days
|
|
71
|
+
* = floor if age_days ≥ stale_days
|
|
72
|
+
* = 1.0 − (1.0 − floor) · (age_days − fresh_days) / (stale_days − fresh_days) otherwise
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* Future-dated `admitted_at` (negative age) is clamped to 1.0. An absent or
|
|
76
|
+
* unparseable `admitted_at` returns `floor` — the element is treated as fully
|
|
77
|
+
* stale, never as fresh.
|
|
78
|
+
*/
|
|
79
|
+
export function freshness(
|
|
80
|
+
admittedAt: string | undefined,
|
|
81
|
+
today: string,
|
|
82
|
+
params: DecayParams,
|
|
83
|
+
): number {
|
|
84
|
+
if (!admittedAt) return params.floor;
|
|
85
|
+
const age = daysBetween(admittedAt, today);
|
|
86
|
+
if (age === null) return params.floor;
|
|
87
|
+
if (age <= params.fresh_days) return 1.0;
|
|
88
|
+
if (age >= params.stale_days) return params.floor;
|
|
89
|
+
// Linear interpolation. Guard against a degenerate fresh==stale config: the
|
|
90
|
+
// two equalities above would already have returned, but if a caller passes
|
|
91
|
+
// stale_days < fresh_days we keep monotonic behaviour by returning floor.
|
|
92
|
+
const span = params.stale_days - params.fresh_days;
|
|
93
|
+
if (span <= 0) return params.floor;
|
|
94
|
+
return 1.0 - (1.0 - params.floor) * ((age - params.fresh_days) / span);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Scores one element per §11.4–11.5. The element's `sources` list is the
|
|
99
|
+
* resolved `derived_from` provenance: max trust wins (`max` in §11.4), and
|
|
100
|
+
* an empty / absent list marks the element unsourced — scored at the
|
|
101
|
+
* `unverified` floor and `sourced: false` so the view composite can count
|
|
102
|
+
* it separately per §11.5.
|
|
103
|
+
*/
|
|
104
|
+
export function scoreElement(
|
|
105
|
+
element: ScoringElement,
|
|
106
|
+
today: string,
|
|
107
|
+
config?: ConfidenceDecayConfig,
|
|
108
|
+
): ElementScore {
|
|
109
|
+
const sources = element.sources ?? [];
|
|
110
|
+
const sourced = sources.length > 0;
|
|
111
|
+
const source_trust = sourced
|
|
112
|
+
? Math.max(...sources.map(sourceTrustWeight))
|
|
113
|
+
: UNVERIFIED_WEIGHT;
|
|
114
|
+
const params = resolveDecayParams(element.type, config);
|
|
115
|
+
const f = freshness(element.admitted_at, today, params);
|
|
116
|
+
return {
|
|
117
|
+
source_trust,
|
|
118
|
+
freshness: f,
|
|
119
|
+
confidence: source_trust * f,
|
|
120
|
+
sourced,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Confidence band per §11.6: A ≥ 0.8, B ≥ 0.6, C ≥ 0.4, D < 0.4. */
|
|
125
|
+
export function confidenceBand(confidence: number): ConfidenceBand {
|
|
126
|
+
if (confidence >= 0.8) return 'A';
|
|
127
|
+
if (confidence >= 0.6) return 'B';
|
|
128
|
+
if (confidence >= 0.4) return 'C';
|
|
129
|
+
return 'D';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Rolls per-element scores up to the view composite per §11.6. The weakest
|
|
134
|
+
* link is the headline; mean and coverage are reported alongside. An empty
|
|
135
|
+
* element set returns a degenerate composite (`weakest_link` and `mean` of 0,
|
|
136
|
+
* coverage 0, band `D`) — callers should detect `element_count === 0` if they
|
|
137
|
+
* want to suppress the header entirely rather than display the floor.
|
|
138
|
+
*/
|
|
139
|
+
export function composeView(scores: ElementScore[]): ViewComposite {
|
|
140
|
+
if (scores.length === 0) {
|
|
141
|
+
return {
|
|
142
|
+
weakest_link: 0,
|
|
143
|
+
mean: 0,
|
|
144
|
+
coverage: 0,
|
|
145
|
+
band: 'D',
|
|
146
|
+
element_count: 0,
|
|
147
|
+
unsourced_count: 0,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
let min = Infinity;
|
|
151
|
+
let sum = 0;
|
|
152
|
+
let sourcedCount = 0;
|
|
153
|
+
for (const s of scores) {
|
|
154
|
+
if (s.confidence < min) min = s.confidence;
|
|
155
|
+
sum += s.confidence;
|
|
156
|
+
if (s.sourced) sourcedCount += 1;
|
|
157
|
+
}
|
|
158
|
+
const mean = sum / scores.length;
|
|
159
|
+
return {
|
|
160
|
+
weakest_link: min,
|
|
161
|
+
mean,
|
|
162
|
+
coverage: sourcedCount / scores.length,
|
|
163
|
+
band: confidenceBand(min),
|
|
164
|
+
element_count: scores.length,
|
|
165
|
+
unsourced_count: scores.length - sourcedCount,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Scores a view's elements end-to-end. The caller passes the rendered set,
|
|
171
|
+
* the adopter manifest's `confidence_decay` block (optional), and the
|
|
172
|
+
* reference date (`today`); the result is purely derived and never stored
|
|
173
|
+
* — re-running on the same inputs yields the same numbers (§11.6).
|
|
174
|
+
*/
|
|
175
|
+
export function scoreView(
|
|
176
|
+
elements: ScoringElement[],
|
|
177
|
+
today: string | Date,
|
|
178
|
+
config?: ConfidenceDecayConfig,
|
|
179
|
+
): ViewScore {
|
|
180
|
+
const todayIso = toIsoDate(today);
|
|
181
|
+
const elementScores = elements.map(e => scoreElement(e, todayIso, config));
|
|
182
|
+
return {
|
|
183
|
+
elements: elementScores,
|
|
184
|
+
composite: composeView(elementScores),
|
|
185
|
+
today: todayIso,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Renders the §11.6 example header line, e.g.:
|
|
191
|
+
*
|
|
192
|
+
* ```
|
|
193
|
+
* Data confidence (as of 2026-06-05): B (weakest link) · 0.71 mean · 92% sourced
|
|
194
|
+
* ```
|
|
195
|
+
*
|
|
196
|
+
* Shared between Studio (DQ-2) and DSM (DQ-5) so the banding/labels stay
|
|
197
|
+
* consistent across the two consumers. Returns an empty string for an empty
|
|
198
|
+
* composite — callers decide whether to suppress the header in that case.
|
|
199
|
+
*/
|
|
200
|
+
export function formatConfidenceHeader(view: ViewScore): string {
|
|
201
|
+
const c = view.composite;
|
|
202
|
+
if (c.element_count === 0) return '';
|
|
203
|
+
const mean = c.mean.toFixed(2);
|
|
204
|
+
const coverage = Math.round(c.coverage * 100);
|
|
205
|
+
return `Data confidence (as of ${view.today}): ${c.band} (weakest link) · ${mean} mean · ${coverage}% sourced`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ── Date helpers ────────────────────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
/** Strict ISO-date parser. Accepts `YYYY-MM-DD`; returns null on anything else. */
|
|
211
|
+
const ISO_DATE_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
212
|
+
|
|
213
|
+
function parseIsoDate(s: string): Date | null {
|
|
214
|
+
const m = ISO_DATE_RE.exec(s);
|
|
215
|
+
if (!m) return null;
|
|
216
|
+
const year = Number(m[1]);
|
|
217
|
+
const month = Number(m[2]);
|
|
218
|
+
const day = Number(m[3]);
|
|
219
|
+
// UTC to avoid the host TZ shifting age_days by ±1.
|
|
220
|
+
const d = new Date(Date.UTC(year, month - 1, day));
|
|
221
|
+
if (
|
|
222
|
+
d.getUTCFullYear() !== year ||
|
|
223
|
+
d.getUTCMonth() !== month - 1 ||
|
|
224
|
+
d.getUTCDate() !== day
|
|
225
|
+
) return null;
|
|
226
|
+
return d;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function toIsoDate(d: string | Date): string {
|
|
230
|
+
if (typeof d === 'string') return d;
|
|
231
|
+
const y = d.getUTCFullYear();
|
|
232
|
+
const m = String(d.getUTCMonth() + 1).padStart(2, '0');
|
|
233
|
+
const day = String(d.getUTCDate()).padStart(2, '0');
|
|
234
|
+
return `${y}-${m}-${day}`;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function daysBetween(fromIso: string, toIso: string): number | null {
|
|
238
|
+
const from = parseIsoDate(fromIso);
|
|
239
|
+
const to = parseIsoDate(toIso);
|
|
240
|
+
if (!from || !to) return null;
|
|
241
|
+
const ms = to.getTime() - from.getTime();
|
|
242
|
+
return Math.floor(ms / (24 * 60 * 60 * 1000));
|
|
243
|
+
}
|