@mergesignal/shared 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deriveScanNarrative.d.ts.map +1 -1
- package/dist/deriveScanNarrative.js +41 -8
- package/dist/deriveScanNarrative.test.js +4 -0
- package/dist/fixtures/repoIntelligenceFixtures.d.ts.map +1 -1
- package/dist/fixtures/repoIntelligenceFixtures.js +8 -1
- package/dist/presentation/compose/narrativeCompose.d.ts.map +1 -1
- package/dist/presentation/compose/narrativeCompose.js +15 -101
- package/dist/presentation/dto/scanCardPresentation.d.ts +2 -0
- package/dist/presentation/dto/scanCardPresentation.d.ts.map +1 -1
- package/dist/presentation/dto/scanDetailsPresentation.d.ts +2 -0
- package/dist/presentation/dto/scanDetailsPresentation.d.ts.map +1 -1
- package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts +11 -0
- package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts.map +1 -0
- package/dist/presentation/fixtures/presentationPersonaFixtures.js +197 -0
- package/dist/presentation/index.d.ts +2 -0
- package/dist/presentation/index.d.ts.map +1 -1
- package/dist/presentation/index.js +1 -0
- package/dist/presentation/intent/applyPostureVocabularyGuard.d.ts +5 -0
- package/dist/presentation/intent/applyPostureVocabularyGuard.d.ts.map +1 -0
- package/dist/presentation/intent/applyPostureVocabularyGuard.js +44 -0
- package/dist/presentation/intent/derivePresentationInterpretation.d.ts +4 -0
- package/dist/presentation/intent/derivePresentationInterpretation.d.ts.map +1 -0
- package/dist/presentation/intent/derivePresentationInterpretation.js +202 -0
- package/dist/presentation/intent/formatPresentationCopy.d.ts +7 -0
- package/dist/presentation/intent/formatPresentationCopy.d.ts.map +1 -0
- package/dist/presentation/intent/formatPresentationCopy.js +153 -0
- package/dist/presentation/intent/packageDisplayName.d.ts +4 -0
- package/dist/presentation/intent/packageDisplayName.d.ts.map +1 -0
- package/dist/presentation/intent/packageDisplayName.js +17 -0
- package/dist/presentation/intent/postureVocabularyGuard.test.d.ts +2 -0
- package/dist/presentation/intent/postureVocabularyGuard.test.d.ts.map +1 -0
- package/dist/presentation/intent/postureVocabularyGuard.test.js +23 -0
- package/dist/presentation/intent/presentationIntent.d.ts +14 -0
- package/dist/presentation/intent/presentationIntent.d.ts.map +1 -0
- package/dist/presentation/intent/presentationIntent.js +1 -0
- package/dist/presentation/intent/presentationIntent.test.d.ts +2 -0
- package/dist/presentation/intent/presentationIntent.test.d.ts.map +1 -0
- package/dist/presentation/intent/presentationIntent.test.js +29 -0
- package/dist/presentation/intent/verificationFocusLabels.d.ts +2 -0
- package/dist/presentation/intent/verificationFocusLabels.d.ts.map +1 -0
- package/dist/presentation/intent/verificationFocusLabels.js +45 -0
- package/dist/presentation/presentation.test.js +3 -0
- package/dist/presentation/presentationPersonas.test.d.ts +2 -0
- package/dist/presentation/presentationPersonas.test.d.ts.map +1 -0
- package/dist/presentation/presentationPersonas.test.js +166 -0
- package/dist/presentation/presenters/presentScanCard.d.ts.map +1 -1
- package/dist/presentation/presenters/presentScanCard.js +1 -0
- package/dist/presentation/presenters/presentScanDetails.d.ts.map +1 -1
- package/dist/presentation/presenters/presentScanDetails.js +7 -1
- package/dist/presentation/profile/derivePresentationProfile.d.ts.map +1 -1
- package/dist/presentation/profile/derivePresentationProfile.js +18 -27
- package/dist/presentation/profile/presentationProfile.d.ts +2 -0
- package/dist/presentation/profile/presentationProfile.d.ts.map +1 -1
- package/dist/repoIntelligenceSchema.d.ts +1 -1
- package/dist/scanNarrativeFacts.d.ts +31 -7
- package/dist/scanNarrativeFacts.d.ts.map +1 -1
- package/dist/scanNarrativeFacts.js +1 -0
- package/dist/scanSurfaceCopy.d.ts +8 -0
- package/dist/scanSurfaceCopy.d.ts.map +1 -1
- package/dist/scanSurfaceCopy.js +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { formatCardAreaLabels } from "../../formatCardAreaLabels.js";
|
|
2
|
+
import { mergeVerificationFocusLabels } from "./verificationFocusLabels.js";
|
|
3
|
+
const TOOLING_CLASSES = new Set([
|
|
4
|
+
"tooling",
|
|
5
|
+
"test",
|
|
6
|
+
"lint",
|
|
7
|
+
"format",
|
|
8
|
+
"build",
|
|
9
|
+
"ci",
|
|
10
|
+
]);
|
|
11
|
+
const RUNTIME_IMPACT_RANK = {
|
|
12
|
+
confirmed: 3,
|
|
13
|
+
possible: 2,
|
|
14
|
+
unknown: 1,
|
|
15
|
+
none: 0,
|
|
16
|
+
};
|
|
17
|
+
const EVIDENCE_RANK = {
|
|
18
|
+
high: 3,
|
|
19
|
+
medium: 2,
|
|
20
|
+
low: 1,
|
|
21
|
+
};
|
|
22
|
+
function isRuntimeEligible(row) {
|
|
23
|
+
if (row.suppressRuntimeNarrative)
|
|
24
|
+
return false;
|
|
25
|
+
if (row.runtimeImpact === "confirmed" || row.runtimeImpact === "possible") {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
if (row.dependencyClass === "runtime")
|
|
29
|
+
return true;
|
|
30
|
+
if (row.usagePathCount > 0)
|
|
31
|
+
return true;
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
function isToolingOriented(row) {
|
|
35
|
+
if (row.suppressRuntimeNarrative)
|
|
36
|
+
return true;
|
|
37
|
+
if (row.dependencyClass && TOOLING_CLASSES.has(row.dependencyClass)) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
function compareRuntimePackages(a, b, order) {
|
|
43
|
+
const impactA = RUNTIME_IMPACT_RANK[a.runtimeImpact ?? "none"] ?? 0;
|
|
44
|
+
const impactB = RUNTIME_IMPACT_RANK[b.runtimeImpact ?? "none"] ?? 0;
|
|
45
|
+
if (impactB !== impactA)
|
|
46
|
+
return impactB - impactA;
|
|
47
|
+
const evA = EVIDENCE_RANK[a.evidenceStrength ?? "low"] ?? 0;
|
|
48
|
+
const evB = EVIDENCE_RANK[b.evidenceStrength ?? "low"] ?? 0;
|
|
49
|
+
if (evB !== evA)
|
|
50
|
+
return evB - evA;
|
|
51
|
+
return order.indexOf(a.packageName) - order.indexOf(b.packageName);
|
|
52
|
+
}
|
|
53
|
+
function pickAnchorPackage(rows, changedOrder) {
|
|
54
|
+
if (rows.length === 0)
|
|
55
|
+
return null;
|
|
56
|
+
const sorted = [...rows].sort((a, b) => compareRuntimePackages(a, b, changedOrder));
|
|
57
|
+
return sorted[0]?.packageName ?? null;
|
|
58
|
+
}
|
|
59
|
+
function pickToolingAnchor(rows, changedOrder) {
|
|
60
|
+
if (rows.length === 0)
|
|
61
|
+
return null;
|
|
62
|
+
const sorted = [...rows].sort((a, b) => changedOrder.indexOf(a.packageName) - changedOrder.indexOf(b.packageName));
|
|
63
|
+
return sorted[0]?.packageName ?? null;
|
|
64
|
+
}
|
|
65
|
+
function isToolingPatchSet(rows, facts) {
|
|
66
|
+
if (rows.length === 0)
|
|
67
|
+
return false;
|
|
68
|
+
const narrow = !facts.blastRadius || facts.blastRadius.level === "narrow";
|
|
69
|
+
if (!narrow)
|
|
70
|
+
return false;
|
|
71
|
+
return rows.every((row) => row.suppressRuntimeNarrative &&
|
|
72
|
+
(row.expectedImpact === "typecheck" ||
|
|
73
|
+
row.expectedImpact === "build_time"));
|
|
74
|
+
}
|
|
75
|
+
function runtimeSurfaceLabelFromFacts(facts, anchorRow) {
|
|
76
|
+
const area = facts.affectedAreas[0];
|
|
77
|
+
if (area) {
|
|
78
|
+
const formatted = formatCardAreaLabels([area.label], 1);
|
|
79
|
+
return formatted[0] ?? area.label;
|
|
80
|
+
}
|
|
81
|
+
if (anchorRow?.packageRole === "http_framework") {
|
|
82
|
+
return "API request handling";
|
|
83
|
+
}
|
|
84
|
+
if (anchorRow?.packageRole === "auth") {
|
|
85
|
+
return "authentication flows";
|
|
86
|
+
}
|
|
87
|
+
if (anchorRow?.packageRole === "queue") {
|
|
88
|
+
return "background job processing";
|
|
89
|
+
}
|
|
90
|
+
return "application code";
|
|
91
|
+
}
|
|
92
|
+
function collectVerificationLabels(rows) {
|
|
93
|
+
return mergeVerificationFocusLabels(rows.map((r) => r.verificationFocus));
|
|
94
|
+
}
|
|
95
|
+
function showLimitedEvidence(rows) {
|
|
96
|
+
return rows.some((r) => r.evidenceStrength === "low");
|
|
97
|
+
}
|
|
98
|
+
function dominantExpectedImpact(rows) {
|
|
99
|
+
for (const row of rows) {
|
|
100
|
+
if (row.expectedImpact && row.expectedImpact !== "unknown") {
|
|
101
|
+
return row.expectedImpact;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
function emptyInterpretation(intent) {
|
|
107
|
+
return {
|
|
108
|
+
intent,
|
|
109
|
+
anchorPackage: null,
|
|
110
|
+
suppressRuntimeNarrative: intent.startsWith("tooling"),
|
|
111
|
+
allowRuntimeNarrative: intent.includes("runtime"),
|
|
112
|
+
showLimitedEvidence: false,
|
|
113
|
+
expectedImpactKey: null,
|
|
114
|
+
verificationLabels: [],
|
|
115
|
+
runtimeSurfaceLabel: null,
|
|
116
|
+
runtimePackages: [],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export function derivePresentationInterpretation(facts) {
|
|
120
|
+
const changedOrder = facts.changedPackages.all;
|
|
121
|
+
const semantics = facts.changedPackageSemantics.length > 0
|
|
122
|
+
? facts.changedPackageSemantics
|
|
123
|
+
: facts.packageSemantics && facts.changedPackages.primary
|
|
124
|
+
? [
|
|
125
|
+
{
|
|
126
|
+
packageName: facts.changedPackages.primary,
|
|
127
|
+
dependencyClass: facts.packageSemantics.dependencyClass,
|
|
128
|
+
packageRole: facts.packageSemantics.packageRole,
|
|
129
|
+
runtimeImpact: facts.packageSemantics.runtimeImpact,
|
|
130
|
+
expectedImpact: facts.packageSemantics.expectedImpact,
|
|
131
|
+
suppressRuntimeNarrative: facts.packageSemantics.suppressRuntimeNarrative,
|
|
132
|
+
evidenceStrength: facts.packageSemantics.evidenceStrength,
|
|
133
|
+
verificationFocus: facts.packageSemantics.verificationFocus,
|
|
134
|
+
usagePathCount: 0,
|
|
135
|
+
usageAreaCount: 0,
|
|
136
|
+
},
|
|
137
|
+
]
|
|
138
|
+
: [];
|
|
139
|
+
const tier1Ok = facts.availability.tiersPresent.tier1 &&
|
|
140
|
+
facts.availability.repoIntelligenceParse === "ok";
|
|
141
|
+
if (!tier1Ok) {
|
|
142
|
+
return emptyInterpretation("limited_context");
|
|
143
|
+
}
|
|
144
|
+
if (!facts.changedPackages.primary) {
|
|
145
|
+
return emptyInterpretation("no_changed_packages");
|
|
146
|
+
}
|
|
147
|
+
const runtimeEligible = semantics.filter(isRuntimeEligible);
|
|
148
|
+
if (runtimeEligible.length > 0) {
|
|
149
|
+
const anchorPackage = pickAnchorPackage(runtimeEligible, changedOrder);
|
|
150
|
+
const anchorRow = runtimeEligible.find((r) => r.packageName === anchorPackage);
|
|
151
|
+
const runtimeSurfaceLabel = runtimeSurfaceLabelFromFacts(facts, anchorRow);
|
|
152
|
+
const verificationLabels = collectVerificationLabels(runtimeEligible);
|
|
153
|
+
let intent = "runtime_upgrade";
|
|
154
|
+
if (runtimeEligible.length > 1) {
|
|
155
|
+
intent = "multi_runtime_upgrade";
|
|
156
|
+
}
|
|
157
|
+
else if (runtimeEligible.some((r) => r.packageRole === "auth")) {
|
|
158
|
+
intent = "auth_runtime_upgrade";
|
|
159
|
+
}
|
|
160
|
+
else if (runtimeEligible.some((r) => r.packageRole === "queue")) {
|
|
161
|
+
intent = "queue_runtime_upgrade";
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
intent,
|
|
165
|
+
anchorPackage,
|
|
166
|
+
suppressRuntimeNarrative: false,
|
|
167
|
+
allowRuntimeNarrative: true,
|
|
168
|
+
showLimitedEvidence: showLimitedEvidence(runtimeEligible),
|
|
169
|
+
expectedImpactKey: dominantExpectedImpact(runtimeEligible),
|
|
170
|
+
verificationLabels,
|
|
171
|
+
runtimeSurfaceLabel,
|
|
172
|
+
runtimePackages: runtimeEligible.map((r) => r.packageName),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const toolingOnly = semantics.length > 0 && semantics.every(isToolingOriented);
|
|
176
|
+
if (toolingOnly) {
|
|
177
|
+
const anchorPackage = pickToolingAnchor(semantics, changedOrder);
|
|
178
|
+
const isPatch = isToolingPatchSet(semantics, facts);
|
|
179
|
+
const intent = isPatch
|
|
180
|
+
? "tooling_patch"
|
|
181
|
+
: "tooling_upgrade";
|
|
182
|
+
const anchorRow = semantics.find((r) => r.packageName === anchorPackage);
|
|
183
|
+
return {
|
|
184
|
+
intent,
|
|
185
|
+
anchorPackage,
|
|
186
|
+
suppressRuntimeNarrative: true,
|
|
187
|
+
allowRuntimeNarrative: false,
|
|
188
|
+
showLimitedEvidence: showLimitedEvidence(semantics),
|
|
189
|
+
expectedImpactKey: anchorRow?.expectedImpact ?? null,
|
|
190
|
+
verificationLabels: collectVerificationLabels(semantics),
|
|
191
|
+
runtimeSurfaceLabel: null,
|
|
192
|
+
runtimePackages: [],
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
...emptyInterpretation("unknown_upgrade"),
|
|
197
|
+
anchorPackage: facts.changedPackages.primary,
|
|
198
|
+
verificationLabels: collectVerificationLabels(semantics),
|
|
199
|
+
showLimitedEvidence: showLimitedEvidence(semantics),
|
|
200
|
+
expectedImpactKey: dominantExpectedImpact(semantics),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ScanNarrativeFacts } from "../../scanNarrativeFacts.js";
|
|
2
|
+
import type { PresentationStatus } from "../dto/types.js";
|
|
3
|
+
import type { PresentationInterpretation } from "./presentationIntent.js";
|
|
4
|
+
export declare function formatPresentationHeadline(interpretation: PresentationInterpretation, facts: ScanNarrativeFacts, status: PresentationStatus | undefined): string;
|
|
5
|
+
export declare function formatPresentationKeyPoints(interpretation: PresentationInterpretation, facts: ScanNarrativeFacts, status: PresentationStatus | undefined, max: number): string[];
|
|
6
|
+
export declare function formatPresentationVerification(interpretation: PresentationInterpretation, status: PresentationStatus | undefined, max: number): string[];
|
|
7
|
+
//# sourceMappingURL=formatPresentationCopy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatPresentationCopy.d.ts","sourceRoot":"","sources":["../../../src/presentation/intent/formatPresentationCopy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAM1D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAa1E,wBAAgB,0BAA0B,CACxC,cAAc,EAAE,0BAA0B,EAC1C,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACrC,MAAM,CAmDR;AA0BD,wBAAgB,2BAA2B,CACzC,cAAc,EAAE,0BAA0B,EAC1C,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAmEV;AAuBD,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,0BAA0B,EAC1C,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAmBV"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
|
|
2
|
+
import { applyPostureVocabularyGuardHeadline, applyPostureVocabularyGuardLines, } from "./applyPostureVocabularyGuard.js";
|
|
3
|
+
import { formatPackageDisplayName } from "./packageDisplayName.js";
|
|
4
|
+
function anchorSemantics(facts, interpretation) {
|
|
5
|
+
const name = interpretation.anchorPackage;
|
|
6
|
+
if (!name)
|
|
7
|
+
return null;
|
|
8
|
+
return (facts.changedPackageSemantics.find((r) => r.packageName === name) ?? null);
|
|
9
|
+
}
|
|
10
|
+
export function formatPresentationHeadline(interpretation, facts, status) {
|
|
11
|
+
const anchor = interpretation.anchorPackage ?? facts.changedPackages.primary;
|
|
12
|
+
const row = anchorSemantics(facts, interpretation);
|
|
13
|
+
const pkgDisplay = formatPackageDisplayName(anchor ?? "dependency", row?.packageRole);
|
|
14
|
+
const copy = scanSurfaceCopy.presentation;
|
|
15
|
+
let headline;
|
|
16
|
+
switch (interpretation.intent) {
|
|
17
|
+
case "limited_context":
|
|
18
|
+
headline = copy.limitedContextHeadline;
|
|
19
|
+
break;
|
|
20
|
+
case "no_changed_packages":
|
|
21
|
+
headline = copy.noChangedPackagesHeadline;
|
|
22
|
+
break;
|
|
23
|
+
case "tooling_patch":
|
|
24
|
+
headline = copy.toolingPatchHeadline.replace("{package}", pkgDisplay);
|
|
25
|
+
break;
|
|
26
|
+
case "tooling_upgrade":
|
|
27
|
+
headline = copy.toolingUpgradeHeadline.replace("{package}", pkgDisplay);
|
|
28
|
+
break;
|
|
29
|
+
case "multi_runtime_upgrade":
|
|
30
|
+
headline = copy.multiRuntimeHeadline;
|
|
31
|
+
break;
|
|
32
|
+
case "auth_runtime_upgrade":
|
|
33
|
+
case "queue_runtime_upgrade":
|
|
34
|
+
case "runtime_upgrade": {
|
|
35
|
+
const surface = interpretation.runtimeSurfaceLabel ?? "application code";
|
|
36
|
+
headline = copy.runtimeUpgradeHeadline
|
|
37
|
+
.replace("{package}", pkgDisplay)
|
|
38
|
+
.replace("{surface}", surface);
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
case "unknown_upgrade":
|
|
42
|
+
if (status === "safe") {
|
|
43
|
+
headline = copy.safeNeutralHeadline.replace("{package}", pkgDisplay);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
headline = copy.defaultUpgradeHeadline.replace("{package}", pkgDisplay);
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
default:
|
|
50
|
+
headline = copy.safeNeutralHeadline.replace("{package}", pkgDisplay);
|
|
51
|
+
}
|
|
52
|
+
return applyPostureVocabularyGuardHeadline(headline, status, interpretation.anchorPackage);
|
|
53
|
+
}
|
|
54
|
+
function formatExpectedImpactKeyPoint(interpretation) {
|
|
55
|
+
if (interpretation.intent !== "tooling_patch" &&
|
|
56
|
+
interpretation.intent !== "tooling_upgrade") {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const key = interpretation.expectedImpactKey;
|
|
60
|
+
let workflows = "build and typecheck workflows";
|
|
61
|
+
if (key === "test_time")
|
|
62
|
+
workflows = "test workflows";
|
|
63
|
+
else if (key === "development_only")
|
|
64
|
+
workflows = "development workflows";
|
|
65
|
+
else if (key === "build_time")
|
|
66
|
+
workflows = "build workflows";
|
|
67
|
+
return scanSurfaceCopy.presentation.expectedImpactLimitedKeyPoint.replace("{workflows}", workflows);
|
|
68
|
+
}
|
|
69
|
+
function formatLimitedEvidenceKeyPoint() {
|
|
70
|
+
return scanSurfaceCopy.presentation.limitedEvidenceKeyPoint;
|
|
71
|
+
}
|
|
72
|
+
export function formatPresentationKeyPoints(interpretation, facts, status, max) {
|
|
73
|
+
const points = [];
|
|
74
|
+
if (interpretation.intent === "limited_context") {
|
|
75
|
+
points.push(scanSurfaceCopy.presentation.limitedContextKeyPoint);
|
|
76
|
+
if (facts.changedPackages.primary) {
|
|
77
|
+
points.push(scanSurfaceCopy.presentation.changedPackageKeyPoint.replace("{package}", facts.changedPackages.primary));
|
|
78
|
+
}
|
|
79
|
+
return applyPostureVocabularyGuardLines(points, status).slice(0, max);
|
|
80
|
+
}
|
|
81
|
+
if (interpretation.showLimitedEvidence) {
|
|
82
|
+
points.push(formatLimitedEvidenceKeyPoint());
|
|
83
|
+
}
|
|
84
|
+
if (interpretation.suppressRuntimeNarrative) {
|
|
85
|
+
points.push(scanSurfaceCopy.presentation.noRuntimePathsKeyPoint);
|
|
86
|
+
const impact = formatExpectedImpactKeyPoint(interpretation);
|
|
87
|
+
if (impact)
|
|
88
|
+
points.push(impact);
|
|
89
|
+
}
|
|
90
|
+
else if (interpretation.allowRuntimeNarrative) {
|
|
91
|
+
const usage = facts.packageUsage.find((u) => u.packageName === interpretation.anchorPackage);
|
|
92
|
+
const paths = [...(usage?.paths ?? []), ...(usage?.criticalPaths ?? [])];
|
|
93
|
+
if (paths.length > 0) {
|
|
94
|
+
points.push(scanSurfaceCopy.presentation.usedInRuntimePathsKeyPoint.replace("{count}", String(paths.length)));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
points.push(scanSurfaceCopy.presentation.runtimeUsageKeyPoint);
|
|
98
|
+
}
|
|
99
|
+
if (facts.affectedAreas.length > 0) {
|
|
100
|
+
const labels = facts.affectedAreas
|
|
101
|
+
.slice(0, 2)
|
|
102
|
+
.map((a) => a.label)
|
|
103
|
+
.join(", ");
|
|
104
|
+
points.push(scanSurfaceCopy.presentation.affectedAreasKeyPoint.replace("{areas}", labels));
|
|
105
|
+
}
|
|
106
|
+
const blast = facts.blastRadius;
|
|
107
|
+
if (blast?.level === "wide") {
|
|
108
|
+
points.push(scanSurfaceCopy.presentation.blastRadiusWideKeyPoint);
|
|
109
|
+
}
|
|
110
|
+
else if (blast?.level === "moderate") {
|
|
111
|
+
points.push(scanSurfaceCopy.presentation.blastRadiusLevelKeyPoint.replace("{level}", "Moderate"));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return applyPostureVocabularyGuardLines(points, status).slice(0, max);
|
|
115
|
+
}
|
|
116
|
+
const AUTH_VERIFICATION = [
|
|
117
|
+
"routes",
|
|
118
|
+
"middleware/hooks",
|
|
119
|
+
"auth flow",
|
|
120
|
+
"serialization",
|
|
121
|
+
"error handling",
|
|
122
|
+
];
|
|
123
|
+
const QUEUE_VERIFICATION = [
|
|
124
|
+
"workers",
|
|
125
|
+
"queue",
|
|
126
|
+
"serialization",
|
|
127
|
+
"error handling",
|
|
128
|
+
];
|
|
129
|
+
const RUNTIME_DEFAULT_VERIFICATION = [
|
|
130
|
+
"routes",
|
|
131
|
+
"middleware/hooks",
|
|
132
|
+
"error handling",
|
|
133
|
+
];
|
|
134
|
+
export function formatPresentationVerification(interpretation, status, max) {
|
|
135
|
+
let actions = [];
|
|
136
|
+
if (interpretation.verificationLabels.length > 0) {
|
|
137
|
+
actions = [...interpretation.verificationLabels];
|
|
138
|
+
}
|
|
139
|
+
else if (interpretation.intent === "auth_runtime_upgrade") {
|
|
140
|
+
actions = [...AUTH_VERIFICATION];
|
|
141
|
+
}
|
|
142
|
+
else if (interpretation.intent === "queue_runtime_upgrade") {
|
|
143
|
+
actions = [...QUEUE_VERIFICATION];
|
|
144
|
+
}
|
|
145
|
+
else if (interpretation.allowRuntimeNarrative) {
|
|
146
|
+
actions = [...RUNTIME_DEFAULT_VERIFICATION];
|
|
147
|
+
}
|
|
148
|
+
else if (interpretation.intent === "tooling_patch" ||
|
|
149
|
+
interpretation.intent === "tooling_upgrade") {
|
|
150
|
+
actions = ["CI", "typecheck", "build"];
|
|
151
|
+
}
|
|
152
|
+
return applyPostureVocabularyGuardLines(actions, status).slice(0, max);
|
|
153
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PackageRoleKind } from "../../scanNarrativeFacts.js";
|
|
2
|
+
/** Display name from package role when engine classified it; else title-case package name. */
|
|
3
|
+
export declare function formatPackageDisplayName(packageName: string, packageRole?: PackageRoleKind | null): string;
|
|
4
|
+
//# sourceMappingURL=packageDisplayName.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packageDisplayName.d.ts","sourceRoot":"","sources":["../../../src/presentation/intent/packageDisplayName.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AASnE,8FAA8F;AAC9F,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,eAAe,GAAG,IAAI,GACnC,MAAM,CAOR"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const ROLE_DISPLAY = {
|
|
2
|
+
typechecker: "TypeScript",
|
|
3
|
+
linter: "ESLint",
|
|
4
|
+
formatter: "Prettier",
|
|
5
|
+
test_runner: "Test runner",
|
|
6
|
+
};
|
|
7
|
+
/** Display name from package role when engine classified it; else title-case package name. */
|
|
8
|
+
export function formatPackageDisplayName(packageName, packageRole) {
|
|
9
|
+
if (packageRole && ROLE_DISPLAY[packageRole]) {
|
|
10
|
+
return ROLE_DISPLAY[packageRole];
|
|
11
|
+
}
|
|
12
|
+
if (!packageName)
|
|
13
|
+
return packageName;
|
|
14
|
+
if (packageName.length <= 3)
|
|
15
|
+
return packageName.toUpperCase();
|
|
16
|
+
return packageName.charAt(0).toUpperCase() + packageName.slice(1);
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postureVocabularyGuard.test.d.ts","sourceRoot":"","sources":["../../../src/presentation/intent/postureVocabularyGuard.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { applyPostureVocabularyGuardHeadline, applyPostureVocabularyGuardLines, guardHeadlineForPosture, } from "./applyPostureVocabularyGuard.js";
|
|
3
|
+
describe("applyPostureVocabularyGuard", () => {
|
|
4
|
+
it("substitutes safe headline when review language present", () => {
|
|
5
|
+
const out = guardHeadlineForPosture("typescript dependency upgrade needs review", "safe", "typescript");
|
|
6
|
+
expect(out.toLowerCase()).not.toContain("needs review");
|
|
7
|
+
expect(out.toLowerCase()).toContain("typescript");
|
|
8
|
+
});
|
|
9
|
+
it("filters safe-forbidden key points", () => {
|
|
10
|
+
const lines = applyPostureVocabularyGuardLines(["No runtime usage paths detected", "High risk upgrade path"], "safe");
|
|
11
|
+
expect(lines).toContain("No runtime usage paths detected");
|
|
12
|
+
expect(lines.some((l) => /high risk/i.test(l))).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
it("filters needs_review forbidden verification", () => {
|
|
15
|
+
const lines = applyPostureVocabularyGuardLines(["Confirm CI passes", "No action required"], "needs_review");
|
|
16
|
+
expect(lines).toContain("Confirm CI passes");
|
|
17
|
+
expect(lines.some((l) => /no action required/i.test(l))).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
it("allows review headline for needs_review posture", () => {
|
|
20
|
+
const out = applyPostureVocabularyGuardHeadline("lodash dependency upgrade needs review", "needs_review", "lodash");
|
|
21
|
+
expect(out).toContain("needs review");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PackageExpectedImpact } from "../../scanNarrativeFacts.js";
|
|
2
|
+
export type PresentationIntent = "limited_context" | "no_changed_packages" | "tooling_patch" | "tooling_upgrade" | "runtime_upgrade" | "auth_runtime_upgrade" | "queue_runtime_upgrade" | "multi_runtime_upgrade" | "unknown_upgrade";
|
|
3
|
+
export type PresentationInterpretation = {
|
|
4
|
+
intent: PresentationIntent;
|
|
5
|
+
anchorPackage: string | null;
|
|
6
|
+
suppressRuntimeNarrative: boolean;
|
|
7
|
+
allowRuntimeNarrative: boolean;
|
|
8
|
+
showLimitedEvidence: boolean;
|
|
9
|
+
expectedImpactKey: PackageExpectedImpact | null;
|
|
10
|
+
verificationLabels: string[];
|
|
11
|
+
runtimeSurfaceLabel: string | null;
|
|
12
|
+
runtimePackages: string[];
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=presentationIntent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentationIntent.d.ts","sourceRoot":"","sources":["../../../src/presentation/intent/presentationIntent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AAEzE,MAAM,MAAM,kBAAkB,GAC1B,iBAAiB,GACjB,qBAAqB,GACrB,eAAe,GACf,iBAAiB,GACjB,iBAAiB,GACjB,sBAAsB,GACtB,uBAAuB,GACvB,uBAAuB,GACvB,iBAAiB,CAAC;AAEtB,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,wBAAwB,EAAE,OAAO,CAAC;IAClC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,iBAAiB,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAChD,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentationIntent.test.d.ts","sourceRoot":"","sources":["../../../src/presentation/intent/presentationIntent.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { deriveScanNarrative } from "../../deriveScanNarrative.js";
|
|
3
|
+
import { scanResultMixedTypescriptFastify, scanResultTypescriptPatch, scanResultFastifyRuntime, } from "../fixtures/presentationPersonaFixtures.js";
|
|
4
|
+
import { derivePresentationInterpretation } from "./derivePresentationInterpretation.js";
|
|
5
|
+
describe("derivePresentationInterpretation", () => {
|
|
6
|
+
it("typescript patch: tooling_patch intent from engine semantics", () => {
|
|
7
|
+
const facts = deriveScanNarrative(scanResultTypescriptPatch);
|
|
8
|
+
const interp = derivePresentationInterpretation(facts);
|
|
9
|
+
// Engine: suppressRuntimeNarrative=true, expectedImpact=typecheck, dependencyClass=tooling
|
|
10
|
+
expect(interp.intent).toBe("tooling_patch");
|
|
11
|
+
expect(interp.anchorPackage).toBe("typescript");
|
|
12
|
+
expect(interp.suppressRuntimeNarrative).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
it("fastify: runtime_upgrade from runtimeImpact=confirmed", () => {
|
|
15
|
+
const facts = deriveScanNarrative(scanResultFastifyRuntime);
|
|
16
|
+
const interp = derivePresentationInterpretation(facts);
|
|
17
|
+
expect(interp.intent).toBe("runtime_upgrade");
|
|
18
|
+
expect(interp.anchorPackage).toBe("fastify");
|
|
19
|
+
expect(interp.allowRuntimeNarrative).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
it("mixed typescript+fastify: runtime dominates tooling (anchor fastify)", () => {
|
|
22
|
+
const facts = deriveScanNarrative(scanResultMixedTypescriptFastify);
|
|
23
|
+
const interp = derivePresentationInterpretation(facts);
|
|
24
|
+
expect(interp.intent).toBe("runtime_upgrade");
|
|
25
|
+
expect(interp.anchorPackage).toBe("fastify");
|
|
26
|
+
expect(interp.runtimePackages).toContain("fastify");
|
|
27
|
+
expect(interp.runtimePackages).not.toContain("typescript");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verificationFocusLabels.d.ts","sourceRoot":"","sources":["../../../src/presentation/intent/verificationFocusLabels.ts"],"names":[],"mappings":"AA2BA,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,MAAM,EAAE,EAAE,EACtB,GAAG,SAAI,GACN,MAAM,EAAE,CAeV"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const FOCUS_LABELS = {
|
|
2
|
+
ci: "CI",
|
|
3
|
+
typecheck: "typecheck",
|
|
4
|
+
build: "build",
|
|
5
|
+
routes: "routes",
|
|
6
|
+
middleware: "middleware/hooks",
|
|
7
|
+
hooks: "middleware/hooks",
|
|
8
|
+
auth_flow: "auth flow",
|
|
9
|
+
auth: "auth flow",
|
|
10
|
+
serialization: "serialization",
|
|
11
|
+
error_handling: "error handling",
|
|
12
|
+
handlers: "handlers",
|
|
13
|
+
plugins: "plugins",
|
|
14
|
+
session: "session",
|
|
15
|
+
workers: "workers",
|
|
16
|
+
queue: "queue",
|
|
17
|
+
test_suite: "test suite",
|
|
18
|
+
lint: "lint",
|
|
19
|
+
format: "format",
|
|
20
|
+
};
|
|
21
|
+
function labelVerificationFocus(focus) {
|
|
22
|
+
const key = focus.trim().toLowerCase();
|
|
23
|
+
if (!key)
|
|
24
|
+
return "";
|
|
25
|
+
return FOCUS_LABELS[key] ?? focus.replace(/_/g, " ");
|
|
26
|
+
}
|
|
27
|
+
export function mergeVerificationFocusLabels(focusLists, max = 6) {
|
|
28
|
+
const seen = new Set();
|
|
29
|
+
const out = [];
|
|
30
|
+
for (const list of focusLists) {
|
|
31
|
+
for (const f of list) {
|
|
32
|
+
const label = labelVerificationFocus(f);
|
|
33
|
+
if (!label)
|
|
34
|
+
continue;
|
|
35
|
+
const norm = label.toLowerCase();
|
|
36
|
+
if (seen.has(norm))
|
|
37
|
+
continue;
|
|
38
|
+
seen.add(norm);
|
|
39
|
+
out.push(label);
|
|
40
|
+
if (out.length >= max)
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
@@ -64,6 +64,9 @@ describe("presentScanCard personas", () => {
|
|
|
64
64
|
const card = presentScanCard(bundle);
|
|
65
65
|
expect(card.density).toBe("minimal");
|
|
66
66
|
expect(card.status).toBe("safe");
|
|
67
|
+
expect(card.presentationIntent).toBe("tooling_patch");
|
|
68
|
+
expect(card.headline).toMatch(/patch upgrade/i);
|
|
69
|
+
expect(card.headline).not.toMatch(/needs review/i);
|
|
67
70
|
expect(card.changedPackages).toContain("typescript");
|
|
68
71
|
expect(card.verificationActions.length).toBeGreaterThan(0);
|
|
69
72
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentationPersonas.test.d.ts","sourceRoot":"","sources":["../../src/presentation/presentationPersonas.test.ts"],"names":[],"mappings":""}
|