@topogram/cli 0.3.64 → 0.3.65
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/package.json +1 -1
- package/src/adoption/plan/index.js +703 -0
- package/src/adoption/plan.js +12 -703
- package/src/agent-ops/query-builders/auth.js +375 -0
- package/src/agent-ops/query-builders/change-risk/change-plan.js +123 -0
- package/src/agent-ops/query-builders/change-risk/import-plan.js +49 -0
- package/src/agent-ops/query-builders/change-risk/maintained.js +286 -0
- package/src/agent-ops/query-builders/change-risk/review-packets.js +123 -0
- package/src/agent-ops/query-builders/change-risk/risk.js +189 -0
- package/src/agent-ops/query-builders/change-risk.js +25 -0
- package/src/agent-ops/query-builders/common.js +149 -0
- package/src/agent-ops/query-builders/maintained-risk.js +539 -0
- package/src/agent-ops/query-builders/maintained-shared.js +120 -0
- package/src/agent-ops/query-builders/multi-agent.js +547 -0
- package/src/agent-ops/query-builders/projection-impacts.js +514 -0
- package/src/agent-ops/query-builders/work-packets.js +417 -0
- package/src/agent-ops/query-builders/workflow-context-shared.js +300 -0
- package/src/agent-ops/query-builders/workflow-context.js +398 -0
- package/src/agent-ops/query-builders/workflow-presets-core.js +676 -0
- package/src/agent-ops/query-builders/workflow-presets.js +341 -0
- package/src/agent-ops/query-builders.d.ts +26 -26
- package/src/agent-ops/query-builders.js +42 -5021
- package/src/catalog/constants.js +10 -0
- package/src/catalog/copy.js +60 -0
- package/src/catalog/diagnostics.js +15 -0
- package/src/catalog/entries.js +42 -0
- package/src/catalog/files.js +67 -0
- package/src/catalog/provenance.js +122 -0
- package/src/catalog/source.js +150 -0
- package/src/catalog/validation.js +252 -0
- package/src/catalog.d.ts +2 -0
- package/src/catalog.js +18 -746
- package/src/cli/commands/catalog/check.js +31 -0
- package/src/cli/commands/catalog/copy.js +59 -0
- package/src/cli/commands/catalog/doctor.js +248 -0
- package/src/cli/commands/catalog/help.js +21 -0
- package/src/cli/commands/catalog/list.js +52 -0
- package/src/cli/commands/catalog/runner.js +92 -0
- package/src/cli/commands/catalog/shared.js +17 -0
- package/src/cli/commands/catalog/show.js +134 -0
- package/src/cli/commands/catalog.js +30 -615
- package/src/cli/commands/generator-policy/package-info.js +162 -0
- package/src/cli/commands/generator-policy/payloads.js +372 -0
- package/src/cli/commands/generator-policy/printers.js +159 -0
- package/src/cli/commands/generator-policy/runner.js +81 -0
- package/src/cli/commands/generator-policy/shared.js +39 -0
- package/src/cli/commands/generator-policy.js +15 -783
- package/src/cli/commands/import/adopt.js +170 -0
- package/src/cli/commands/import/check.js +91 -0
- package/src/cli/commands/import/diff.js +84 -0
- package/src/cli/commands/import/help.js +47 -0
- package/src/cli/commands/import/paths.js +277 -0
- package/src/cli/commands/import/plan.js +284 -0
- package/src/cli/commands/import/refresh.js +470 -0
- package/src/cli/commands/import/status-history.js +196 -0
- package/src/cli/commands/import/workspace.js +230 -0
- package/src/cli/commands/import.js +33 -1732
- package/src/cli/commands/package/constants.js +17 -0
- package/src/cli/commands/package/doctor.js +240 -0
- package/src/cli/commands/package/help.js +27 -0
- package/src/cli/commands/package/lockfile.js +135 -0
- package/src/cli/commands/package/npm.js +97 -0
- package/src/cli/commands/package/reporting.js +35 -0
- package/src/cli/commands/package/runner.js +33 -0
- package/src/cli/commands/package/shared.js +9 -0
- package/src/cli/commands/package/update-cli.js +252 -0
- package/src/cli/commands/package/versions.js +35 -0
- package/src/cli/commands/package.js +29 -813
- package/src/cli/commands/query/change-plan.js +68 -0
- package/src/cli/commands/query/definitions.js +202 -0
- package/src/cli/commands/query/import-adopt.js +121 -0
- package/src/cli/commands/query/runner/artifacts.js +102 -0
- package/src/cli/commands/query/runner/boundaries.js +211 -0
- package/src/cli/commands/query/runner/change.js +182 -0
- package/src/cli/commands/query/runner/import-adopt.js +111 -0
- package/src/cli/commands/query/runner/index.js +31 -0
- package/src/cli/commands/query/runner/output.js +12 -0
- package/src/cli/commands/query/runner/workflow.js +241 -0
- package/src/cli/commands/query/runner.js +3 -0
- package/src/cli/commands/query/workflow-context.js +5 -0
- package/src/cli/commands/query/workspace.js +274 -0
- package/src/cli/commands/query.js +9 -1300
- package/src/cli/commands/template/baseline.js +100 -0
- package/src/cli/commands/template/check.js +466 -0
- package/src/cli/commands/template/constants.js +8 -0
- package/src/cli/commands/template/diagnostics.js +26 -0
- package/src/cli/commands/template/help.js +28 -0
- package/src/cli/commands/template/lifecycle.js +404 -0
- package/src/cli/commands/template/list-show.js +287 -0
- package/src/cli/commands/template/policy.js +422 -0
- package/src/cli/commands/template/shared.js +127 -0
- package/src/cli/commands/template/updates.js +352 -0
- package/src/cli/commands/template.js +41 -2143
- package/src/generator/api/contracts.js +497 -0
- package/src/generator/api/metadata.js +221 -0
- package/src/generator/api/openapi.js +559 -0
- package/src/generator/api/schema.js +124 -0
- package/src/generator/api/types.d.ts +98 -0
- package/src/generator/api.js +3 -1195
- package/src/generator/context/shared/domain-sdlc.js +282 -0
- package/src/generator/context/shared/maintained-boundary.js +665 -0
- package/src/generator/context/shared/metrics.js +85 -0
- package/src/generator/context/shared/primitives.js +64 -0
- package/src/generator/context/shared/relationships.js +453 -0
- package/src/generator/context/shared/summaries.js +263 -0
- package/src/generator/context/shared/types.d.ts +207 -0
- package/src/generator/context/shared.d.ts +42 -0
- package/src/generator/context/shared.js +80 -1390
- package/src/generator/context/slice/core.js +397 -0
- package/src/generator/context/slice/sdlc.js +417 -0
- package/src/generator/context/slice/ui-packets.js +183 -0
- package/src/generator/context/slice.js +2 -859
- package/src/generator/registry/index.js +507 -0
- package/src/generator/registry.js +18 -504
- package/src/generator/runtime/environment/index.js +666 -0
- package/src/generator/runtime/environment.js +4 -666
- package/src/generator/runtime/runtime-check/index.js +554 -0
- package/src/generator/runtime/runtime-check.js +4 -554
- package/src/generator/runtime/shared/index.js +572 -0
- package/src/generator/runtime/shared.js +19 -570
- package/src/generator/shared.d.ts +2 -0
- package/src/generator/surfaces/shared.d.ts +3 -0
- package/src/generator/widget-conformance/behavior-report.js +258 -0
- package/src/generator/widget-conformance/checks.js +371 -0
- package/src/generator/widget-conformance/projection-context.js +200 -0
- package/src/generator/widget-conformance/report.js +166 -0
- package/src/generator/widget-conformance/types.d.ts +121 -0
- package/src/generator/widget-conformance.js +3 -824
- package/src/import/core/context.d.ts +3 -0
- package/src/import/core/contracts.d.ts +1 -0
- package/src/import/core/registry.d.ts +4 -0
- package/src/import/core/runner/candidates.js +217 -0
- package/src/import/core/runner/options.js +22 -0
- package/src/import/core/runner/reports.js +50 -0
- package/src/import/core/runner/run.js +79 -0
- package/src/import/core/runner/tracks.js +150 -0
- package/src/import/core/runner/ui-drafts.js +337 -0
- package/src/import/core/runner.js +3 -698
- package/src/import/core/shared/api-routes.js +221 -0
- package/src/import/core/shared/candidates.js +97 -0
- package/src/import/core/shared/files.js +177 -0
- package/src/import/core/shared/next-app.js +389 -0
- package/src/import/core/shared/types.d.ts +51 -0
- package/src/import/core/shared/ui-routes.js +230 -0
- package/src/import/core/shared.js +60 -861
- package/src/new-project/constants.js +128 -0
- package/src/new-project/create.js +83 -0
- package/src/new-project/json.js +28 -0
- package/src/new-project/metadata.js +96 -0
- package/src/new-project/package-spec.js +161 -0
- package/src/new-project/project-files.js +348 -0
- package/src/new-project/template-policy.js +269 -0
- package/src/new-project/template-resolution.js +368 -0
- package/src/new-project/template-snapshots.js +430 -0
- package/src/new-project/template-updates.js +512 -0
- package/src/new-project/types.d.ts +83 -0
- package/src/new-project.js +6 -2277
- package/src/parser.d.ts +87 -1
- package/src/parser.js +118 -0
- package/src/policy/review-boundaries.d.ts +15 -0
- package/src/project-config/index.js +564 -0
- package/src/project-config.js +19 -561
- package/src/resolver/enrich/acceptance-criterion.js +2 -0
- package/src/resolver/enrich/bug.js +2 -0
- package/src/resolver/enrich/pitch.js +2 -0
- package/src/resolver/enrich/requirement.js +2 -0
- package/src/resolver/enrich/task.js +2 -0
- package/src/resolver/index.js +19 -2089
- package/src/resolver/normalize.js +384 -1
- package/src/resolver/plans.js +168 -0
- package/src/resolver/projections-api.js +494 -0
- package/src/resolver/projections-db.js +133 -0
- package/src/resolver/projections-ui.js +317 -0
- package/src/resolver/shapes.js +251 -0
- package/src/resolver/shared.js +278 -0
- package/src/resolver/widgets.js +132 -0
- package/src/template-trust/constants.js +62 -0
- package/src/template-trust/content.js +258 -0
- package/src/template-trust/diff.js +92 -0
- package/src/template-trust/policy.js +61 -0
- package/src/template-trust/record.js +90 -0
- package/src/template-trust/status.js +182 -0
- package/src/template-trust.js +24 -687
- package/src/text-helpers.d.ts +1 -0
- package/src/topogram-types.d.ts +69 -0
- package/src/validator/common.js +488 -0
- package/src/validator/data-model.js +237 -0
- package/src/validator/docs.js +167 -0
- package/src/validator/expressions.js +146 -1
- package/src/validator/index.d.ts +23 -0
- package/src/validator/index.js +32 -3585
- package/src/validator/kinds.d.ts +41 -0
- package/src/validator/kinds.js +2 -0
- package/src/validator/model-helpers.js +46 -0
- package/src/validator/per-kind/acceptance-criterion.js +5 -0
- package/src/validator/per-kind/bug.js +6 -0
- package/src/validator/per-kind/domain.js +15 -2
- package/src/validator/per-kind/pitch.js +7 -0
- package/src/validator/per-kind/requirement.js +5 -0
- package/src/validator/per-kind/task.js +7 -0
- package/src/validator/per-kind/widget.js +14 -0
- package/src/validator/projections/api-http-async.js +410 -0
- package/src/validator/projections/api-http-authz.js +88 -0
- package/src/validator/projections/api-http-core.js +205 -0
- package/src/validator/projections/api-http-policies.js +339 -0
- package/src/validator/projections/api-http-responses.js +233 -0
- package/src/validator/projections/api-http.js +44 -0
- package/src/validator/projections/db.js +353 -0
- package/src/validator/projections/generator-defaults.js +45 -0
- package/src/validator/projections/helpers.js +87 -0
- package/src/validator/projections/ui-helpers.js +214 -0
- package/src/validator/projections/ui-navigation.js +344 -0
- package/src/validator/projections/ui-structure.js +364 -0
- package/src/validator/projections/ui-widgets.js +493 -0
- package/src/validator/projections/ui.js +46 -0
- package/src/validator/registry.js +48 -1
- package/src/validator/utils.d.ts +20 -0
- package/src/validator/utils.js +115 -12
- package/src/widget-behavior.d.ts +1 -0
- package/src/workflows/import-app/api/collect.js +221 -0
- package/src/workflows/import-app/api/openapi.js +257 -0
- package/src/workflows/import-app/api/routes.js +327 -0
- package/src/workflows/import-app/api/sources.js +22 -0
- package/src/workflows/import-app/api.js +2 -797
- package/src/workflows/reconcile/adoption-plan/build.js +208 -0
- package/src/workflows/reconcile/adoption-plan/dependencies.js +75 -0
- package/src/workflows/reconcile/adoption-plan/outputs.js +143 -0
- package/src/workflows/reconcile/adoption-plan/paths.js +58 -0
- package/src/workflows/reconcile/adoption-plan/projection-patches.js +177 -0
- package/src/workflows/reconcile/adoption-plan/reasons.js +107 -0
- package/src/workflows/reconcile/adoption-plan.js +30 -740
- package/src/workflows/reconcile/auth/closures.js +115 -0
- package/src/workflows/reconcile/auth/formatters.js +142 -0
- package/src/workflows/reconcile/auth/inference.js +330 -0
- package/src/workflows/reconcile/auth/roles.js +122 -0
- package/src/workflows/reconcile/auth.js +35 -690
- package/src/workflows/reconcile/bundle-core/index.js +600 -0
- package/src/workflows/reconcile/bundle-core.js +12 -598
- package/src/workflows/reconcile/canonical-surface.js +1 -1
- package/src/workflows/reconcile/impacts/adoption-plan.js +192 -0
- package/src/workflows/reconcile/impacts/indexes.js +101 -0
- package/src/workflows/reconcile/impacts/patches.js +252 -0
- package/src/workflows/reconcile/impacts/reports.js +80 -0
- package/src/workflows/reconcile/impacts.js +14 -623
- package/src/workspace-docs.d.ts +29 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
export function canonicalWriteCandidatesFromWriteScope(writeScope) {
|
|
2
|
+
return (writeScope?.safe_to_edit || []).filter((entry) => entry === "topogram/**" || String(entry).startsWith("topogram/"));
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function summarizeDiffArtifact(diffArtifact) {
|
|
6
|
+
if (!diffArtifact) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const maintainedOutputs = diffArtifact.affected_maintained_surfaces?.outputs || [];
|
|
10
|
+
const maintainedSeams = diffArtifact.affected_maintained_surfaces?.affected_seams || [];
|
|
11
|
+
const highestMaintainedSeverity = [...maintainedSeams]
|
|
12
|
+
.sort((a, b) => severityRank(b.status) - severityRank(a.status))[0]?.status || null;
|
|
13
|
+
return {
|
|
14
|
+
baseline_root: diffArtifact.baseline_root,
|
|
15
|
+
review_boundary_change_count: (diffArtifact.review_boundary_changes || []).length,
|
|
16
|
+
maintained_file_count: (diffArtifact.affected_maintained_surfaces?.maintained_files_in_scope || []).length,
|
|
17
|
+
affected_verification_count: (diffArtifact.affected_verifications || []).length,
|
|
18
|
+
affected_output_count: maintainedOutputs.length,
|
|
19
|
+
affected_seam_count: maintainedSeams.length,
|
|
20
|
+
highest_maintained_severity: highestMaintainedSeverity
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function severityRank(status) {
|
|
25
|
+
if (status === "no_go") return 3;
|
|
26
|
+
if (status === "manual_decision") return 2;
|
|
27
|
+
if (status === "review_required") return 1;
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function stableSortedStrings(values) {
|
|
32
|
+
return [...new Set((values || []).filter(Boolean))].sort();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const CANONICAL_TASK_MODES = new Set([
|
|
36
|
+
"modeling",
|
|
37
|
+
"maintained-app-edit",
|
|
38
|
+
"import-adopt",
|
|
39
|
+
"diff-review",
|
|
40
|
+
"verification"
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
export const WORKFLOW_REVIEW_BLOCKERS = new Set([
|
|
44
|
+
"review_required",
|
|
45
|
+
"manual_decision",
|
|
46
|
+
"no_go"
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
export const PROVIDER_PRESET_MANUAL_DECISION_CATEGORIES = new Set([
|
|
50
|
+
"auth",
|
|
51
|
+
"deployment_assumptions",
|
|
52
|
+
"runtime_assumptions",
|
|
53
|
+
"deploy_runtime_assumptions",
|
|
54
|
+
"environment_expectations",
|
|
55
|
+
"maintained_boundary",
|
|
56
|
+
"ownership_visibility"
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
export const WORKFLOW_QUERY_FAMILIES_BY_MODE = {
|
|
60
|
+
modeling: ["change-plan", "write-scope", "verification-targets", "risk-summary"],
|
|
61
|
+
"maintained-app-edit": ["maintained-boundary", "maintained-conformance", "seam-check", "change-plan"],
|
|
62
|
+
"import-adopt": ["import-plan", "risk-summary", "proceed-decision", "review-packet"],
|
|
63
|
+
"diff-review": ["change-plan", "risk-summary", "review-packet"],
|
|
64
|
+
verification: ["verification-targets", "proceed-decision", "risk-summary"]
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export function stableOrderedUnion(values = []) {
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
const result = [];
|
|
70
|
+
for (const value of values) {
|
|
71
|
+
if (!value || seen.has(value)) continue;
|
|
72
|
+
seen.add(value);
|
|
73
|
+
result.push(value);
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function targetWidgetId(target = {}) {
|
|
79
|
+
return target.widget_id || target.component_id || null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function componentBehaviorArtifactPath(target = {}) {
|
|
83
|
+
if (target.target !== "widget-behavior-report") {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const suffix = [target.projection_id, targetWidgetId(target)].filter(Boolean).join(".");
|
|
87
|
+
return suffix ? `${suffix}.widget-behavior-report.json` : "widget-behavior-report.json";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function componentBehaviorQueryCommand(target = {}) {
|
|
91
|
+
if (target.target !== "widget-behavior-report") {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const parts = ["topogram", "query", "widget-behavior", "./topogram"];
|
|
95
|
+
if (target.projection_id) {
|
|
96
|
+
parts.push("--projection", target.projection_id);
|
|
97
|
+
}
|
|
98
|
+
const widgetId = targetWidgetId(target);
|
|
99
|
+
if (widgetId) {
|
|
100
|
+
parts.push("--widget", widgetId);
|
|
101
|
+
}
|
|
102
|
+
parts.push("--json");
|
|
103
|
+
return parts.join(" ");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function recommendedArtifactQueriesFromGeneratorTargets(generatorTargets = []) {
|
|
107
|
+
const queries = [];
|
|
108
|
+
const seen = new Set();
|
|
109
|
+
for (const target of generatorTargets || []) {
|
|
110
|
+
const command = componentBehaviorQueryCommand(target);
|
|
111
|
+
if (!command || seen.has(command)) continue;
|
|
112
|
+
seen.add(command);
|
|
113
|
+
queries.push({
|
|
114
|
+
query: "widget-behavior",
|
|
115
|
+
target: target.target,
|
|
116
|
+
projection_id: target.projection_id || null,
|
|
117
|
+
widget_id: targetWidgetId(target),
|
|
118
|
+
command
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return queries;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function artifactLoadOrderFromGeneratorTargets(generatorTargets = []) {
|
|
125
|
+
return stableOrderedUnion((generatorTargets || [])
|
|
126
|
+
.map((target) => componentBehaviorArtifactPath(target))
|
|
127
|
+
.filter(Boolean));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function flattenVerificationTargets(verificationTargets = null) {
|
|
131
|
+
if (!verificationTargets || typeof verificationTargets !== "object") {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
const entries = [];
|
|
135
|
+
for (const [key, value] of Object.entries(verificationTargets)) {
|
|
136
|
+
if (Array.isArray(value)) {
|
|
137
|
+
for (const item of value) {
|
|
138
|
+
if (item) entries.push(item);
|
|
139
|
+
}
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (key === "output_verification_targets" && Array.isArray(value)) {
|
|
143
|
+
for (const output of value) {
|
|
144
|
+
entries.push(...flattenVerificationTargets(output?.verification_targets || null));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return stableOrderedUnion(entries);
|
|
149
|
+
}
|
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
severityRank,
|
|
6
|
+
stableSortedStrings
|
|
7
|
+
} from "./common.js";
|
|
8
|
+
import {
|
|
9
|
+
buildMaintainedOutputGroups,
|
|
10
|
+
normalizeSeamSummary
|
|
11
|
+
} from "./maintained-shared.js";
|
|
12
|
+
export function buildMaintainedImpacts({ diffArtifact, maintainedBoundaryArtifact, sliceArtifact, verificationTargets = null }) {
|
|
13
|
+
const diffMaintained = diffArtifact?.affected_maintained_surfaces || null;
|
|
14
|
+
const boundaryFiles = maintainedBoundaryArtifact?.maintained_files_in_scope || [];
|
|
15
|
+
const boundarySeams = maintainedBoundaryArtifact?.seams || [];
|
|
16
|
+
const boundaryOutputs = maintainedBoundaryArtifact?.outputs || [];
|
|
17
|
+
const proofStories = maintainedBoundaryArtifact?.proof_stories || [];
|
|
18
|
+
const likelyFiles = stableSortedStrings([
|
|
19
|
+
...(diffMaintained?.maintained_files_in_scope || []),
|
|
20
|
+
...boundaryFiles
|
|
21
|
+
]);
|
|
22
|
+
const likelySeams = (diffMaintained?.affected_seams || []).length > 0
|
|
23
|
+
? diffMaintained.affected_seams
|
|
24
|
+
: boundarySeams;
|
|
25
|
+
const likelyStories = diffMaintained?.proof_stories || proofStories;
|
|
26
|
+
const reviewSensitive = likelyStories.some((story) => {
|
|
27
|
+
const boundary = story.review_boundary || {};
|
|
28
|
+
return boundary.automation_class && boundary.automation_class !== "safe";
|
|
29
|
+
}) || likelySeams.some((seam) => seam.status && seam.status !== "aligned");
|
|
30
|
+
const normalizedSeams = likelySeams.map((seam) => normalizeSeamSummary(seam));
|
|
31
|
+
const affectedOutputs = buildMaintainedOutputGroups(
|
|
32
|
+
diffMaintained?.outputs || boundaryOutputs,
|
|
33
|
+
normalizedSeams,
|
|
34
|
+
{
|
|
35
|
+
verificationTargetsFallback: verificationTargets
|
|
36
|
+
}
|
|
37
|
+
).map((output) => ({
|
|
38
|
+
output_id: output.output_id,
|
|
39
|
+
label: output.label,
|
|
40
|
+
kind: output.kind,
|
|
41
|
+
root_paths: output.root_paths,
|
|
42
|
+
ownership_boundary: output.ownership_boundary,
|
|
43
|
+
write_scope: output.write_scope,
|
|
44
|
+
verification_targets: output.verification_targets,
|
|
45
|
+
maintained_files_in_scope: output.maintained_files_in_scope,
|
|
46
|
+
human_owned_seams: output.human_owned_seams,
|
|
47
|
+
affected_seams: output.seams,
|
|
48
|
+
proof_stories: output.proof_stories,
|
|
49
|
+
highest_severity: output.summary.highest_severity,
|
|
50
|
+
status_counts: output.summary.status_counts
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
maintained_code_likely_impacted: Boolean(
|
|
55
|
+
diffMaintained?.ownership_interpretation?.maintained_code_impact ||
|
|
56
|
+
likelySeams.length > 0 ||
|
|
57
|
+
likelyFiles.length > 0 ||
|
|
58
|
+
(sliceArtifact?.ownership_boundary && boundaryFiles.length > 0)
|
|
59
|
+
),
|
|
60
|
+
impact_scope: diffMaintained?.ownership_interpretation?.generated_only_impact
|
|
61
|
+
? "generated_only"
|
|
62
|
+
: reviewSensitive
|
|
63
|
+
? "review_sensitive"
|
|
64
|
+
: likelyFiles.length > 0
|
|
65
|
+
? "maintained_code"
|
|
66
|
+
: "generated_only",
|
|
67
|
+
review_sensitive: reviewSensitive,
|
|
68
|
+
maintained_files_in_scope: likelyFiles,
|
|
69
|
+
human_owned_seams: stableSortedStrings([
|
|
70
|
+
...(maintainedBoundaryArtifact?.human_owned_seams || []),
|
|
71
|
+
...likelySeams.map((seam) => seam.label),
|
|
72
|
+
...likelyStories.flatMap((story) => story.human_owned_seams || [])
|
|
73
|
+
]),
|
|
74
|
+
affected_outputs: affectedOutputs,
|
|
75
|
+
affected_seams: normalizedSeams,
|
|
76
|
+
proof_stories: likelyStories.map((story) => normalizeProofStorySummary(story))
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function verificationTargetsForOutput(outputId, maintainedBoundaryArtifact, fallbackVerificationTargets = null) {
|
|
81
|
+
const output = (maintainedBoundaryArtifact?.outputs || []).find((entry) => entry.output_id === outputId);
|
|
82
|
+
return output?.verification_targets || fallbackVerificationTargets || null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const DEPENDENCY_TOKEN_STOPWORDS = new Set([
|
|
86
|
+
"and",
|
|
87
|
+
"cap",
|
|
88
|
+
"proj",
|
|
89
|
+
"projection",
|
|
90
|
+
"journey",
|
|
91
|
+
"workflow",
|
|
92
|
+
"entity",
|
|
93
|
+
"shape",
|
|
94
|
+
"input",
|
|
95
|
+
"output",
|
|
96
|
+
"rule",
|
|
97
|
+
"ui",
|
|
98
|
+
"web",
|
|
99
|
+
"api",
|
|
100
|
+
"db",
|
|
101
|
+
"shared",
|
|
102
|
+
"runtime",
|
|
103
|
+
"contract",
|
|
104
|
+
"contracts",
|
|
105
|
+
"proof",
|
|
106
|
+
"package",
|
|
107
|
+
"maintained",
|
|
108
|
+
"bundle",
|
|
109
|
+
"screen",
|
|
110
|
+
"detail",
|
|
111
|
+
"list",
|
|
112
|
+
"view",
|
|
113
|
+
"ver"
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
export function existingGraphRoot(graph) {
|
|
117
|
+
const root = graph?.root || null;
|
|
118
|
+
return root && fs.existsSync(root) ? root : null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function readableFilePath(root, relativePath) {
|
|
122
|
+
if (!root || !relativePath) return null;
|
|
123
|
+
let current = root;
|
|
124
|
+
while (true) {
|
|
125
|
+
const candidate = path.join(current, relativePath);
|
|
126
|
+
if (fs.existsSync(candidate)) {
|
|
127
|
+
return candidate;
|
|
128
|
+
}
|
|
129
|
+
const parent = path.dirname(current);
|
|
130
|
+
if (parent === current) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
current = parent;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function loadMaintainedModuleContents(root, maintainedModules = []) {
|
|
138
|
+
return maintainedModules
|
|
139
|
+
.map((relativePath) => readableFilePath(root, relativePath))
|
|
140
|
+
.filter(Boolean)
|
|
141
|
+
.map((absolutePath) => ({
|
|
142
|
+
absolutePath,
|
|
143
|
+
contents: fs.readFileSync(absolutePath, "utf8").toLowerCase()
|
|
144
|
+
}));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function dependencyTokens(emittedDependencies = []) {
|
|
148
|
+
return stableSortedStrings(
|
|
149
|
+
emittedDependencies.flatMap((id) =>
|
|
150
|
+
String(id || "")
|
|
151
|
+
.toLowerCase()
|
|
152
|
+
.split(/[^a-z0-9]+/)
|
|
153
|
+
.filter((token) => token.length > 2 && !DEPENDENCY_TOKEN_STOPWORDS.has(token))
|
|
154
|
+
)
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function verificationCoverageForSeamKind(seamKind, verificationTargets) {
|
|
159
|
+
const generatedChecks = verificationTargets?.generated_checks || [];
|
|
160
|
+
const maintainedChecks = verificationTargets?.maintained_app_checks || [];
|
|
161
|
+
if (seamKind === "ui_presenter" || seamKind === "workflow_affordance") {
|
|
162
|
+
return maintainedChecks.length > 0 || generatedChecks.some((check) => check.includes("runtime") || check.includes("compile"));
|
|
163
|
+
}
|
|
164
|
+
if (seamKind === "policy_interpretation") {
|
|
165
|
+
return maintainedChecks.length > 0 || generatedChecks.some((check) => check.includes("runtime"));
|
|
166
|
+
}
|
|
167
|
+
if (seamKind === "verification_harness") {
|
|
168
|
+
return maintainedChecks.length > 0;
|
|
169
|
+
}
|
|
170
|
+
return maintainedChecks.length > 0 || generatedChecks.length > 0 || (verificationTargets?.verification_ids || []).length > 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function buildSeamProbeReport(graph, seam, { verificationTargets = null, diffBacked = false, outputRecord = null } = {}) {
|
|
174
|
+
const maintainedModules = seam?.maintained_modules || [];
|
|
175
|
+
const proofStories = seam?.proof_stories || [];
|
|
176
|
+
const emittedDependencies = seam?.emitted_dependencies || [];
|
|
177
|
+
const graphRoot = existingGraphRoot(graph);
|
|
178
|
+
const existingModules = maintainedModules.filter((relativePath) => readableFilePath(graphRoot, relativePath));
|
|
179
|
+
const proofStoryFiles = proofStories.filter((story) => readableFilePath(graphRoot, story?.relativePath));
|
|
180
|
+
const outputMaintainedFiles = new Set([...(outputRecord?.maintained_files_in_scope || []), ...maintainedModules]);
|
|
181
|
+
const proofStoryMaintainedFiles = stableSortedStrings(proofStories.flatMap((story) => story?.maintained_files || []));
|
|
182
|
+
const resolvedDependencies = emittedDependencies.filter((id) => id === "maintained-proof-package" || (graph ? Boolean(summarizeById(graph, id)) : false));
|
|
183
|
+
const moduleContents = loadMaintainedModuleContents(graphRoot, maintainedModules);
|
|
184
|
+
const corroborationTokens = dependencyTokens(emittedDependencies);
|
|
185
|
+
const corroboratedTokens = corroborationTokens.filter((token) => moduleContents.some((entry) => entry.contents.includes(token)));
|
|
186
|
+
const probeList = [
|
|
187
|
+
{
|
|
188
|
+
probe_id: "maintained_modules_present",
|
|
189
|
+
status: maintainedModules.length > 0 ? "pass" : "fail",
|
|
190
|
+
detail: maintainedModules.length > 0
|
|
191
|
+
? `${maintainedModules.length} maintained module(s) are attached to this seam.`
|
|
192
|
+
: "No maintained modules are attached to this seam."
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
probe_id: "proof_story_present",
|
|
196
|
+
status: proofStories.length > 0 ? "pass" : "fail",
|
|
197
|
+
detail: proofStories.length > 0
|
|
198
|
+
? `${proofStories.length} proof stor${proofStories.length === 1 ? "y is" : "ies are"} attached to this seam.`
|
|
199
|
+
: "No proof story is attached to this seam."
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
probe_id: "emitted_dependencies_resolved",
|
|
203
|
+
status: emittedDependencies.length === resolvedDependencies.length ? "pass" : "fail",
|
|
204
|
+
detail: emittedDependencies.length === resolvedDependencies.length
|
|
205
|
+
? `All ${emittedDependencies.length} emitted dependenc${emittedDependencies.length === 1 ? "y resolves" : "ies resolve"} in the current graph.`
|
|
206
|
+
: `${resolvedDependencies.length} of ${emittedDependencies.length} emitted dependencies resolve in the current graph.`
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
probe_id: "verification_targets_present",
|
|
210
|
+
status: ((verificationTargets?.generated_checks || []).length + (verificationTargets?.maintained_app_checks || []).length + (verificationTargets?.verification_ids || []).length) > 0 ? "pass" : "fail",
|
|
211
|
+
detail: verificationTargets
|
|
212
|
+
? "Verification targets are attached to this seam's output."
|
|
213
|
+
: "No verification targets are attached to this seam's output."
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
probe_id: "maintained_modules_exist",
|
|
217
|
+
status: !graphRoot
|
|
218
|
+
? "skip"
|
|
219
|
+
: maintainedModules.length === 0
|
|
220
|
+
? "skip"
|
|
221
|
+
: existingModules.length === maintainedModules.length ? "pass" : "fail",
|
|
222
|
+
detail: !graphRoot
|
|
223
|
+
? "Workspace root is unavailable, so maintained module existence was not verified."
|
|
224
|
+
: maintainedModules.length === 0
|
|
225
|
+
? "No maintained modules are attached to this seam, so file existence was not verified."
|
|
226
|
+
: existingModules.length === maintainedModules.length
|
|
227
|
+
? `All ${maintainedModules.length} maintained module file${maintainedModules.length === 1 ? "" : "s"} exist on disk.`
|
|
228
|
+
: `${existingModules.length} of ${maintainedModules.length} maintained module files exist on disk.`
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
probe_id: "proof_story_files_exist",
|
|
232
|
+
status: !graphRoot
|
|
233
|
+
? "skip"
|
|
234
|
+
: proofStories.length === 0
|
|
235
|
+
? "skip"
|
|
236
|
+
: proofStoryFiles.length === proofStories.length ? "pass" : "fail",
|
|
237
|
+
detail: !graphRoot
|
|
238
|
+
? "Workspace root is unavailable, so proof story files were not verified."
|
|
239
|
+
: proofStories.length === 0
|
|
240
|
+
? "No proof stories are attached to this seam, so proof file existence was not verified."
|
|
241
|
+
: proofStoryFiles.length === proofStories.length
|
|
242
|
+
? `All ${proofStories.length} proof stor${proofStories.length === 1 ? "y file exists" : "y files exist"} on disk.`
|
|
243
|
+
: `${proofStoryFiles.length} of ${proofStories.length} proof story files exist on disk.`
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
probe_id: "proof_story_maintained_files_in_scope",
|
|
247
|
+
status: proofStoryMaintainedFiles.length === 0
|
|
248
|
+
? "skip"
|
|
249
|
+
: proofStoryMaintainedFiles.every((relativePath) => outputMaintainedFiles.has(relativePath)) ? "pass" : "fail",
|
|
250
|
+
detail: proofStoryMaintainedFiles.length === 0
|
|
251
|
+
? "Proof stories do not declare maintained files for this seam."
|
|
252
|
+
: proofStoryMaintainedFiles.every((relativePath) => outputMaintainedFiles.has(relativePath))
|
|
253
|
+
? "All maintained files named by proof stories are still in the seam or output scope."
|
|
254
|
+
: "At least one maintained file named by a proof story is no longer in the seam or output scope."
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
probe_id: "emitted_dependency_tokens_corroborated",
|
|
258
|
+
status: !graphRoot
|
|
259
|
+
? "skip"
|
|
260
|
+
: corroborationTokens.length === 0
|
|
261
|
+
? "skip"
|
|
262
|
+
: corroboratedTokens.length > 0 ? "pass" : "fail",
|
|
263
|
+
detail: !graphRoot
|
|
264
|
+
? "Workspace root is unavailable, so maintained-module token corroboration was not verified."
|
|
265
|
+
: corroborationTokens.length === 0
|
|
266
|
+
? "No dependency-specific tokens were available for lightweight maintained-module corroboration."
|
|
267
|
+
: corroboratedTokens.length > 0
|
|
268
|
+
? `Maintained modules corroborate dependency tokens: ${corroboratedTokens.join(", ")}.`
|
|
269
|
+
: `Maintained modules do not currently corroborate any dependency tokens from: ${corroborationTokens.join(", ")}.`
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
probe_id: "verification_targets_cover_seam_kind",
|
|
273
|
+
status: verificationTargets
|
|
274
|
+
? (verificationCoverageForSeamKind(seam?.kind || null, verificationTargets) ? "pass" : "fail")
|
|
275
|
+
: "skip",
|
|
276
|
+
detail: !verificationTargets
|
|
277
|
+
? "No verification targets are attached to this seam's output, so seam-kind coverage was not verified."
|
|
278
|
+
: verificationCoverageForSeamKind(seam?.kind || null, verificationTargets)
|
|
279
|
+
? `Verification targets cover the seam kind '${seam?.kind || "unknown"}'.`
|
|
280
|
+
: `Verification targets do not yet clearly cover the seam kind '${seam?.kind || "unknown"}'.`
|
|
281
|
+
}
|
|
282
|
+
];
|
|
283
|
+
let checkStatus = "aligned";
|
|
284
|
+
if (seam?.status === "no_go") {
|
|
285
|
+
checkStatus = "no_go";
|
|
286
|
+
} else if (diffBacked && ["manual_decision", "review_required"].includes(seam?.status)) {
|
|
287
|
+
checkStatus = "stale";
|
|
288
|
+
} else if (probeList.some((probe) => probe.status === "fail")) {
|
|
289
|
+
checkStatus = "unverifiable";
|
|
290
|
+
} else if (["manual_decision", "review_required"].includes(seam?.status)) {
|
|
291
|
+
checkStatus = "guarded";
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
seam_id: seam?.seam_id || null,
|
|
296
|
+
output_id: seam?.output_id || null,
|
|
297
|
+
check_status: checkStatus,
|
|
298
|
+
probes: probeList
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function conformanceStateFromSeamCheck(seam, seamCheck) {
|
|
303
|
+
if (seamCheck?.check_status === "no_go") return "no_go";
|
|
304
|
+
if (seamCheck?.check_status === "stale") return "drift_suspected";
|
|
305
|
+
if (seamCheck?.check_status === "unverifiable") return "unverifiable";
|
|
306
|
+
if ((seam?.status || null) === "manual_decision" || (seam?.status || null) === "review_required" || seamCheck?.check_status === "guarded") {
|
|
307
|
+
return "review_required";
|
|
308
|
+
}
|
|
309
|
+
return "aligned";
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function importProposalDependencyIds(proposalSurface = {}) {
|
|
313
|
+
return stableSortedStrings([
|
|
314
|
+
...(proposalSurface.requirements?.related_capabilities || []),
|
|
315
|
+
...(proposalSurface.requirements?.related_rules || []),
|
|
316
|
+
...(proposalSurface.requirements?.related_workflows || []),
|
|
317
|
+
...(proposalSurface.requirements?.related_docs || []),
|
|
318
|
+
...((proposalSurface.projection_impacts || []).map((impact) => impact.projection_id)),
|
|
319
|
+
...((proposalSurface.ui_impacts || []).map((impact) => impact.projection_id)),
|
|
320
|
+
...((proposalSurface.workflow_impacts || []).map((impact) => impact.id))
|
|
321
|
+
]);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function buildImportProposalMaintainedImpacts(proposalSurface, maintainedBoundaryArtifact) {
|
|
325
|
+
const dependencyIds = importProposalDependencyIds(proposalSurface);
|
|
326
|
+
const seamIdsFromCandidates = new Set((proposalSurface.maintained_seam_candidates || []).map((candidate) => candidate.seam_id).filter(Boolean));
|
|
327
|
+
const matchedSeams = ((maintainedBoundaryArtifact?.seams || []).filter((seam) =>
|
|
328
|
+
seamIdsFromCandidates.size > 0
|
|
329
|
+
? seamIdsFromCandidates.has(seam.seam_id)
|
|
330
|
+
: (seam.emitted_dependencies || []).some((dependency) => dependencyIds.includes(dependency))
|
|
331
|
+
))
|
|
332
|
+
.map((seam) => normalizeSeamSummary(seam));
|
|
333
|
+
const matchedOutputs = buildMaintainedOutputGroups(
|
|
334
|
+
(maintainedBoundaryArtifact?.outputs || []).filter((output) =>
|
|
335
|
+
(output.seams || []).some((seam) => matchedSeams.some((matched) => matched.seam_id === seam.seam_id))
|
|
336
|
+
),
|
|
337
|
+
matchedSeams,
|
|
338
|
+
{
|
|
339
|
+
verificationTargetsFallback: null
|
|
340
|
+
}
|
|
341
|
+
).map((output) => compactMaintainedOutputSummary({
|
|
342
|
+
...output,
|
|
343
|
+
affected_seams: output.seams,
|
|
344
|
+
highest_severity: output.summary.highest_severity
|
|
345
|
+
}));
|
|
346
|
+
const candidateSummary = summarizeImportMaintainedSeamCandidates(proposalSurface, matchedSeams);
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
dependency_ids: dependencyIds,
|
|
350
|
+
maintained_seam_candidates: proposalSurface.maintained_seam_candidates || [],
|
|
351
|
+
maintained_seam_candidate_summary: candidateSummary,
|
|
352
|
+
affected_outputs: matchedOutputs,
|
|
353
|
+
affected_seams: matchedSeams.map((seam) => compactMaintainedSeamSummary(seam)),
|
|
354
|
+
highest_severity: matchedSeams.sort((a, b) => severityRank(b.status) - severityRank(a.status))[0]?.status || "aligned"
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export function summarizeImportMaintainedSeamCandidates(proposalSurface = {}, matchedSeams = []) {
|
|
359
|
+
const candidates = proposalSurface.maintained_seam_candidates || [];
|
|
360
|
+
const highestConfidence = candidates.reduce((max, candidate) => Math.max(max, Number(candidate.confidence || 0)), 0);
|
|
361
|
+
const topCandidate = [...candidates].sort((a, b) => Number(b.confidence || 0) - Number(a.confidence || 0))[0] || null;
|
|
362
|
+
const matchedOutputs = stableSortedStrings(matchedSeams.map((seam) => seam.output_id).filter(Boolean));
|
|
363
|
+
const status = candidates.length > 0
|
|
364
|
+
? "clear_candidate"
|
|
365
|
+
: matchedSeams.length > 0
|
|
366
|
+
? "matched_without_explicit_candidate"
|
|
367
|
+
: "no_candidate";
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
status,
|
|
371
|
+
candidate_count: candidates.length,
|
|
372
|
+
matched_seam_count: matchedSeams.length,
|
|
373
|
+
matched_output_ids: matchedOutputs,
|
|
374
|
+
highest_confidence: candidates.length > 0 ? highestConfidence : null,
|
|
375
|
+
top_candidate: topCandidate
|
|
376
|
+
? {
|
|
377
|
+
seam_id: topCandidate.seam_id || null,
|
|
378
|
+
output_id: topCandidate.output_id || null,
|
|
379
|
+
status: topCandidate.status || null,
|
|
380
|
+
confidence: Number(topCandidate.confidence || 0)
|
|
381
|
+
}
|
|
382
|
+
: null,
|
|
383
|
+
review_guidance: candidates.length > 0
|
|
384
|
+
? "Review the candidate maintained seam mapping before selective adoption."
|
|
385
|
+
: matchedSeams.length > 0
|
|
386
|
+
? "Proposal dependencies overlap maintained seams, but there is no explicit candidate mapping yet."
|
|
387
|
+
: "No conservative maintained seam candidate was inferred for this proposal surface."
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export function buildImportMaintainedRisk(proposalSurfaces = [], maintainedBoundaryArtifact = null) {
|
|
392
|
+
const enrichedProposalSurfaces = proposalSurfaces.map((surface) => {
|
|
393
|
+
const maintainedImpacts = buildImportProposalMaintainedImpacts(surface, maintainedBoundaryArtifact);
|
|
394
|
+
return {
|
|
395
|
+
...surface,
|
|
396
|
+
maintained_impacts: maintainedImpacts
|
|
397
|
+
};
|
|
398
|
+
});
|
|
399
|
+
const allSeams = enrichedProposalSurfaces.flatMap((surface) => surface.maintained_impacts?.affected_seams || []);
|
|
400
|
+
const seamIds = new Set(allSeams.map((seam) => seam.seam_id));
|
|
401
|
+
const normalizedSeams = (maintainedBoundaryArtifact?.seams || [])
|
|
402
|
+
.filter((seam) => seamIds.has(seam.seam_id))
|
|
403
|
+
.map((seam) => normalizeSeamSummary(seam));
|
|
404
|
+
const outputIds = new Set(allSeams.map((seam) => seam.output_id));
|
|
405
|
+
const affectedOutputs = (maintainedBoundaryArtifact?.outputs || [])
|
|
406
|
+
.filter((output) => outputIds.has(output.output_id))
|
|
407
|
+
.map((output) => ({
|
|
408
|
+
...output,
|
|
409
|
+
affected_seams: (output.seams || []).filter((seam) => seamIds.has(seam.seam_id)),
|
|
410
|
+
highest_severity: [...(output.seams || []).filter((seam) => seamIds.has(seam.seam_id))]
|
|
411
|
+
.sort((a, b) => severityRank(b.status) - severityRank(a.status))[0]?.status || "aligned"
|
|
412
|
+
}));
|
|
413
|
+
const maintainedRisk = buildMaintainedRiskSummary({
|
|
414
|
+
maintainedImpacts: {
|
|
415
|
+
affected_outputs: affectedOutputs,
|
|
416
|
+
affected_seams: normalizedSeams,
|
|
417
|
+
maintained_files_in_scope: stableSortedStrings(normalizedSeams.flatMap((seam) => seam.maintained_modules || []))
|
|
418
|
+
},
|
|
419
|
+
maintainedBoundary: maintainedBoundaryArtifact
|
|
420
|
+
});
|
|
421
|
+
const proposalSurfaceSummaries = enrichedProposalSurfaces.map((surface) => ({
|
|
422
|
+
id: surface.id,
|
|
423
|
+
bundle: surface.bundle || null,
|
|
424
|
+
kind: surface.kind || null,
|
|
425
|
+
...surface.maintained_impacts?.maintained_seam_candidate_summary
|
|
426
|
+
}));
|
|
427
|
+
const surfacesWithCandidates = proposalSurfaceSummaries.filter((surface) => surface.status === "clear_candidate");
|
|
428
|
+
const surfacesWithoutCandidates = proposalSurfaceSummaries.filter((surface) => surface.status === "no_candidate");
|
|
429
|
+
const maintainedSeamReviewSummary = {
|
|
430
|
+
status: surfacesWithCandidates.length > 0
|
|
431
|
+
? (surfacesWithoutCandidates.length > 0 ? "mixed" : "clear_candidate")
|
|
432
|
+
: "no_candidate",
|
|
433
|
+
proposal_surface_count: proposalSurfaceSummaries.length,
|
|
434
|
+
surfaces_with_candidates_count: surfacesWithCandidates.length,
|
|
435
|
+
surfaces_without_candidates_count: surfacesWithoutCandidates.length,
|
|
436
|
+
candidate_count: proposalSurfaceSummaries.reduce((sum, surface) => sum + (surface.candidate_count || 0), 0),
|
|
437
|
+
top_candidate: surfacesWithCandidates
|
|
438
|
+
.sort((a, b) => (b.highest_confidence || 0) - (a.highest_confidence || 0))[0]?.top_candidate || null,
|
|
439
|
+
proposal_surfaces: proposalSurfaceSummaries
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
return {
|
|
443
|
+
proposal_surfaces: enrichedProposalSurfaces,
|
|
444
|
+
maintained_risk: {
|
|
445
|
+
...maintainedRisk,
|
|
446
|
+
maintained_seam_review_summary: maintainedSeamReviewSummary
|
|
447
|
+
},
|
|
448
|
+
maintained_seam_review_summary: maintainedSeamReviewSummary
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
export function compactMaintainedSeamSummary(seam) {
|
|
453
|
+
return {
|
|
454
|
+
seam_id: seam?.seam_id || null,
|
|
455
|
+
seam_family_id: seam?.seam_family_id || null,
|
|
456
|
+
seam_family_label: seam?.seam_family_label || null,
|
|
457
|
+
output_id: seam?.output_id || null,
|
|
458
|
+
kind: seam?.kind || null,
|
|
459
|
+
status: seam?.status || null,
|
|
460
|
+
ownership_class: seam?.ownership_class || null
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export function compactMaintainedOutputSummary(output) {
|
|
465
|
+
const verificationTargets = output?.verification_targets || null;
|
|
466
|
+
return {
|
|
467
|
+
output_id: output?.output_id || null,
|
|
468
|
+
kind: output?.kind || null,
|
|
469
|
+
highest_severity: output?.highest_severity || output?.summary?.highest_severity || "aligned",
|
|
470
|
+
affected_seam_count: output?.affected_seams?.length || output?.summary?.affected_seam_count || 0,
|
|
471
|
+
affected_seam_family_count: output?.summary?.affected_seam_family_count || 0,
|
|
472
|
+
maintained_file_count: output?.maintained_files_in_scope?.length || output?.summary?.maintained_file_count || 0,
|
|
473
|
+
verification_targets: verificationTargets
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export function buildMaintainedRiskSummary({ maintainedImpacts = null, maintainedBoundary = null, diffSummary = null } = {}) {
|
|
478
|
+
const affectedOutputs = maintainedImpacts?.affected_outputs || (maintainedBoundary?.outputs || []).map((output) => ({
|
|
479
|
+
output_id: output.output_id,
|
|
480
|
+
kind: output.kind,
|
|
481
|
+
highest_severity: output?.summary?.highest_severity || "aligned",
|
|
482
|
+
affected_seams: output.seams || [],
|
|
483
|
+
maintained_files_in_scope: output.maintained_files_in_scope || [],
|
|
484
|
+
verification_targets: output.verification_targets || null
|
|
485
|
+
}));
|
|
486
|
+
const affectedSeams = maintainedImpacts?.affected_seams || (maintainedBoundary?.seams || []).map((seam) => normalizeSeamSummary(seam));
|
|
487
|
+
const maintainedFilesInScope = stableSortedStrings(
|
|
488
|
+
maintainedImpacts?.maintained_files_in_scope ||
|
|
489
|
+
maintainedBoundary?.maintained_files_in_scope ||
|
|
490
|
+
affectedOutputs.flatMap((output) => output.maintained_files_in_scope || []) ||
|
|
491
|
+
affectedSeams.flatMap((seam) => seam.maintained_modules || [])
|
|
492
|
+
);
|
|
493
|
+
const compactOutputs = affectedOutputs
|
|
494
|
+
.map((output) => compactMaintainedOutputSummary(output))
|
|
495
|
+
.filter((output) => output.output_id || output.affected_seam_count > 0 || output.maintained_file_count > 0)
|
|
496
|
+
.sort((a, b) => String(a.output_id || "").localeCompare(String(b.output_id || "")));
|
|
497
|
+
const compactSeams = affectedSeams
|
|
498
|
+
.map((seam) => compactMaintainedSeamSummary(seam))
|
|
499
|
+
.filter((seam) => seam.seam_id || seam.output_id || seam.kind)
|
|
500
|
+
.sort((a, b) => {
|
|
501
|
+
const severityCompare = severityRank(b.status) - severityRank(a.status);
|
|
502
|
+
return severityCompare !== 0 ? severityCompare : String(a.seam_id || "").localeCompare(String(b.seam_id || ""));
|
|
503
|
+
});
|
|
504
|
+
const derivedStatusCounts = {
|
|
505
|
+
aligned: compactSeams.filter((seam) => seam.status === "aligned").length,
|
|
506
|
+
review_required: compactSeams.filter((seam) => seam.status === "review_required").length,
|
|
507
|
+
manual_decision: compactSeams.filter((seam) => seam.status === "manual_decision").length,
|
|
508
|
+
no_go: compactSeams.filter((seam) => seam.status === "no_go").length
|
|
509
|
+
};
|
|
510
|
+
const statusCounts = {
|
|
511
|
+
aligned: derivedStatusCounts.aligned || maintainedBoundary?.summary?.aligned_count || 0,
|
|
512
|
+
review_required: derivedStatusCounts.review_required || maintainedBoundary?.summary?.review_required_count || 0,
|
|
513
|
+
manual_decision: derivedStatusCounts.manual_decision || maintainedBoundary?.summary?.manual_decision_count || 0,
|
|
514
|
+
no_go: derivedStatusCounts.no_go || maintainedBoundary?.summary?.no_go_count || 0
|
|
515
|
+
};
|
|
516
|
+
const highestSeverity = compactSeams[0]?.status
|
|
517
|
+
|| [...compactOutputs].sort((a, b) => severityRank(b.highest_severity) - severityRank(a.highest_severity))[0]?.highest_severity
|
|
518
|
+
|| (statusCounts.no_go > 0 ? "no_go" : statusCounts.manual_decision > 0 ? "manual_decision" : statusCounts.review_required > 0 ? "review_required" : null)
|
|
519
|
+
|| diffSummary?.highest_maintained_severity
|
|
520
|
+
|| "aligned";
|
|
521
|
+
const outputVerificationTargets = compactOutputs.map((output) => ({
|
|
522
|
+
output_id: output.output_id,
|
|
523
|
+
verification_targets: output.verification_targets || null
|
|
524
|
+
}));
|
|
525
|
+
const affectedSeamFamilies = stableSortedStrings(compactSeams.map((seam) => seam.seam_family_id).filter(Boolean));
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
affected_output_count: compactOutputs.length || diffSummary?.affected_output_count || 0,
|
|
529
|
+
affected_seam_count: compactSeams.length || diffSummary?.affected_seam_count || 0,
|
|
530
|
+
affected_seam_family_count: affectedSeamFamilies.length,
|
|
531
|
+
highest_severity: highestSeverity,
|
|
532
|
+
status_counts: statusCounts,
|
|
533
|
+
affected_seam_families: affectedSeamFamilies,
|
|
534
|
+
affected_outputs: compactOutputs,
|
|
535
|
+
affected_seams: compactSeams,
|
|
536
|
+
maintained_files_in_scope: maintainedFilesInScope,
|
|
537
|
+
output_verification_targets: outputVerificationTargets
|
|
538
|
+
};
|
|
539
|
+
}
|