@penpair/core 0.3.6 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters.d.ts +1 -1
- package/dist/adapters.js +3 -0
- package/dist/approvals.d.ts +4 -1
- package/dist/approvals.js +7 -4
- package/dist/baseline-coverage.js +3 -3
- package/dist/baselines-config.d.ts +3 -0
- package/dist/baselines-config.js +3 -0
- package/dist/baselines.d.ts +7 -0
- package/dist/baselines.js +90 -21
- package/dist/bounded-file.d.ts +2 -0
- package/dist/bounded-file.js +38 -0
- package/dist/cli-main.js +64 -3
- package/dist/design-commands/context.d.ts +9 -0
- package/dist/design-commands/context.js +1 -0
- package/dist/design-commands/index.d.ts +3 -0
- package/dist/design-commands/index.js +7 -0
- package/dist/design-commands/map-command.d.ts +4 -0
- package/dist/design-commands/map-command.js +685 -0
- package/dist/design-commands/release-command.d.ts +3 -0
- package/dist/design-commands/release-command.js +239 -0
- package/dist/design-releases/artifact-paths.d.ts +6 -0
- package/dist/design-releases/artifact-paths.js +82 -0
- package/dist/design-releases/assemble.d.ts +3 -0
- package/dist/design-releases/assemble.js +176 -0
- package/dist/design-releases/check.d.ts +16 -0
- package/dist/design-releases/check.js +323 -0
- package/dist/design-releases/context.d.ts +2 -0
- package/dist/design-releases/context.js +119 -0
- package/dist/design-releases/contract-graph.d.ts +13 -0
- package/dist/design-releases/contract-graph.js +75 -0
- package/dist/design-releases/fidelity.d.ts +23 -0
- package/dist/design-releases/fidelity.js +328 -0
- package/dist/design-releases/finalize.d.ts +4 -0
- package/dist/design-releases/finalize.js +201 -0
- package/dist/design-releases/index.d.ts +8 -0
- package/dist/design-releases/index.js +7 -0
- package/dist/design-releases/live.d.ts +22 -0
- package/dist/design-releases/live.js +97 -0
- package/dist/design-releases/snippets.d.ts +5 -0
- package/dist/design-releases/snippets.js +89 -0
- package/dist/design-releases/store.d.ts +10 -0
- package/dist/design-releases/store.js +205 -0
- package/dist/design-releases/types.d.ts +161 -0
- package/dist/design-releases/types.js +1 -0
- package/dist/design-releases/validation.d.ts +6 -0
- package/dist/design-releases/validation.js +384 -0
- package/dist/design-releases/verify.d.ts +3 -0
- package/dist/design-releases/verify.js +148 -0
- package/dist/image-codec.d.ts +3 -0
- package/dist/image-codec.js +78 -0
- package/dist/image-evidence.d.ts +15 -0
- package/dist/image-evidence.js +121 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/inspection.js +13 -5
- package/dist/instance/config-schema.d.ts +4 -0
- package/dist/instance/config-schema.js +7 -0
- package/dist/map-penpot/content-validation.d.ts +3 -0
- package/dist/map-penpot/content-validation.js +57 -0
- package/dist/map-penpot/content.d.ts +22 -0
- package/dist/map-penpot/content.js +173 -0
- package/dist/map-penpot/details.d.ts +4 -0
- package/dist/map-penpot/details.js +84 -0
- package/dist/map-penpot/evidence-error.d.ts +5 -0
- package/dist/map-penpot/evidence-error.js +9 -0
- package/dist/map-penpot/icons.d.ts +5 -0
- package/dist/map-penpot/icons.js +79 -0
- package/dist/map-penpot/import-content.d.ts +2 -0
- package/dist/map-penpot/import-content.js +150 -0
- package/dist/map-penpot/import.d.ts +5 -0
- package/dist/map-penpot/import.js +255 -0
- package/dist/map-penpot/index.d.ts +11 -0
- package/dist/map-penpot/index.js +10 -0
- package/dist/map-penpot/path-geometry.d.ts +5 -0
- package/dist/map-penpot/path-geometry.js +51 -0
- package/dist/map-penpot/presentation.d.ts +20 -0
- package/dist/map-penpot/presentation.js +205 -0
- package/dist/map-penpot/projection.d.ts +8 -0
- package/dist/map-penpot/projection.js +176 -0
- package/dist/map-penpot/publish.d.ts +4 -0
- package/dist/map-penpot/publish.js +301 -0
- package/dist/map-penpot/release-maps.d.ts +21 -0
- package/dist/map-penpot/release-maps.js +104 -0
- package/dist/map-penpot/snippets.d.ts +4 -0
- package/dist/map-penpot/snippets.js +164 -0
- package/dist/map-penpot/status-labels.d.ts +7 -0
- package/dist/map-penpot/status-labels.js +7 -0
- package/dist/map-penpot/types.d.ts +161 -0
- package/dist/map-penpot/types.js +1 -0
- package/dist/maps/annotation-layout.d.ts +24 -0
- package/dist/maps/annotation-layout.js +85 -0
- package/dist/maps/context.d.ts +13 -0
- package/dist/maps/context.js +207 -0
- package/dist/maps/index.d.ts +9 -0
- package/dist/maps/index.js +9 -0
- package/dist/maps/layout.d.ts +35 -0
- package/dist/maps/layout.js +364 -0
- package/dist/maps/proposal.d.ts +5 -0
- package/dist/maps/proposal.js +67 -0
- package/dist/maps/render.d.ts +4 -0
- package/dist/maps/render.js +331 -0
- package/dist/maps/review.d.ts +16 -0
- package/dist/maps/review.js +90 -0
- package/dist/maps/store.d.ts +21 -0
- package/dist/maps/store.js +222 -0
- package/dist/maps/types.d.ts +206 -0
- package/dist/maps/types.js +1 -0
- package/dist/maps/validation.d.ts +10 -0
- package/dist/maps/validation.js +375 -0
- package/dist/skill-routing.js +4 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -1
- package/skills/penpair-design-release/SKILL.md +55 -0
- package/skills/penpair-design-release/agents/openai.yaml +6 -0
- package/skills/penpair-design-release/references/workflow.md +55 -0
- package/skills/penpair-diagrams/SKILL.md +52 -0
- package/skills/penpair-diagrams/agents/openai.yaml +6 -0
- package/skills/penpair-diagrams/references/workflow.md +84 -0
- package/skills/penpair-handoff/SKILL.md +7 -0
- package/skills/penpair-handoff/references/handoff-flow.md +7 -1
- package/skills/penpair-integration-map/SKILL.md +44 -0
- package/skills/penpair-integration-map/agents/openai.yaml +6 -0
- package/skills/penpair-integration-map/references/workflow.md +33 -0
- package/skills/penpair-journey-map/SKILL.md +42 -0
- package/skills/penpair-journey-map/agents/openai.yaml +6 -0
- package/skills/penpair-journey-map/references/workflow.md +35 -0
- package/skills/penpair-verify/SKILL.md +7 -0
- package/skills/penpair-verify/references/browser-evidence.md +11 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { MapIcon, MapDrawingPlan } from "../maps/types.js";
|
|
2
|
+
import type { ExecuteOptions } from "../client.js";
|
|
3
|
+
import type { OperationOptions, OperationReport } from "../operation.js";
|
|
4
|
+
import type { ExportResult } from "../export.js";
|
|
5
|
+
export interface NativeDrawingNode {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
summary?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
icon?: MapIcon;
|
|
11
|
+
cardTitle?: string;
|
|
12
|
+
cardSummary?: string;
|
|
13
|
+
annotationId?: string;
|
|
14
|
+
kind: string;
|
|
15
|
+
requirementIds?: string[];
|
|
16
|
+
detail?: {
|
|
17
|
+
mapId?: string;
|
|
18
|
+
contractHash?: string;
|
|
19
|
+
sourceId: string;
|
|
20
|
+
path: string;
|
|
21
|
+
sha256: string;
|
|
22
|
+
fileId: string;
|
|
23
|
+
pageId: string;
|
|
24
|
+
frameId: string;
|
|
25
|
+
};
|
|
26
|
+
origin?: "observed" | "proposed" | "owner-decision" | "unknown";
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
}
|
|
32
|
+
export interface NativeDrawingEdge {
|
|
33
|
+
labelBox?: {
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
};
|
|
39
|
+
points?: {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
}[];
|
|
43
|
+
id: string;
|
|
44
|
+
from: string;
|
|
45
|
+
to: string;
|
|
46
|
+
label: string;
|
|
47
|
+
}
|
|
48
|
+
export interface MapDrawing {
|
|
49
|
+
mapId: string;
|
|
50
|
+
contractHash: string;
|
|
51
|
+
title: string;
|
|
52
|
+
goal?: string;
|
|
53
|
+
summary?: string;
|
|
54
|
+
description?: string;
|
|
55
|
+
headerSummary?: string;
|
|
56
|
+
header?: MapDrawingPlan["header"];
|
|
57
|
+
annotations?: MapDrawingPlan["annotations"];
|
|
58
|
+
fontFamily?: string;
|
|
59
|
+
origin?: {
|
|
60
|
+
x: number;
|
|
61
|
+
y: number;
|
|
62
|
+
};
|
|
63
|
+
nodes: NativeDrawingNode[];
|
|
64
|
+
edges: NativeDrawingEdge[];
|
|
65
|
+
}
|
|
66
|
+
export interface MapTarget {
|
|
67
|
+
fileId: string;
|
|
68
|
+
pageId: string;
|
|
69
|
+
boardId?: string;
|
|
70
|
+
}
|
|
71
|
+
export interface ProjectionShape {
|
|
72
|
+
shapeId: string;
|
|
73
|
+
logicalId: string;
|
|
74
|
+
role: "node" | "label" | "edge" | "edge-label" | "title" | "badge" | "legend" | "map-summary" | "map-full-summary" | "map-goal" | "map-description" | "summary" | "full-label" | "full-summary" | "description" | "annotation-panel" | "annotation-id" | "annotation-heading" | "icon-well" | "icon" | "port-in" | "port-out";
|
|
75
|
+
label: string | null;
|
|
76
|
+
x: number;
|
|
77
|
+
y: number;
|
|
78
|
+
width: number;
|
|
79
|
+
height: number;
|
|
80
|
+
content: string | null;
|
|
81
|
+
appearance?: unknown;
|
|
82
|
+
iconKey?: string | null;
|
|
83
|
+
interactions?: {
|
|
84
|
+
trigger: string;
|
|
85
|
+
type: string;
|
|
86
|
+
destinationId: string | null;
|
|
87
|
+
destinationPageId?: string | null;
|
|
88
|
+
destinationMapHash?: string | null;
|
|
89
|
+
destinationMapId?: string | null;
|
|
90
|
+
}[] | null;
|
|
91
|
+
from?: string;
|
|
92
|
+
to?: string;
|
|
93
|
+
}
|
|
94
|
+
export interface NativeMapProjection {
|
|
95
|
+
schemaVersion: 1;
|
|
96
|
+
kind: "native-map-projection";
|
|
97
|
+
fileId: string;
|
|
98
|
+
pageId: string;
|
|
99
|
+
boardId: string | null;
|
|
100
|
+
boardBounds: {
|
|
101
|
+
x: number;
|
|
102
|
+
y: number;
|
|
103
|
+
width: number;
|
|
104
|
+
height: number;
|
|
105
|
+
} | null;
|
|
106
|
+
mapId: string;
|
|
107
|
+
contractHash: string | null;
|
|
108
|
+
fingerprint: string;
|
|
109
|
+
shapes: ProjectionShape[];
|
|
110
|
+
conflicts: string[];
|
|
111
|
+
}
|
|
112
|
+
export interface PublishMapOptions extends OperationOptions {
|
|
113
|
+
/** Required for writes; the standard operation journal persists the pre-write snapshot. */
|
|
114
|
+
journal?: OperationOptions["journal"];
|
|
115
|
+
/** Last independently observed projection. Required before replacing an existing map. */
|
|
116
|
+
expected?: NativeMapProjection;
|
|
117
|
+
/** Export remains evidence awaiting human/agent visual inspection. */
|
|
118
|
+
export?: boolean;
|
|
119
|
+
}
|
|
120
|
+
export interface PublishMapResult {
|
|
121
|
+
report: OperationReport;
|
|
122
|
+
projection: NativeMapProjection;
|
|
123
|
+
image?: ExportResult;
|
|
124
|
+
visualReviewRequired: boolean;
|
|
125
|
+
}
|
|
126
|
+
export interface NativeMapProposal {
|
|
127
|
+
schemaVersion: 1;
|
|
128
|
+
kind: "native-map-proposal";
|
|
129
|
+
baseContractHash: string;
|
|
130
|
+
observedProjectionHash: string;
|
|
131
|
+
mapId: string;
|
|
132
|
+
title?: string;
|
|
133
|
+
metadataChanges?: {
|
|
134
|
+
title?: string;
|
|
135
|
+
goal?: string;
|
|
136
|
+
summary?: string | null;
|
|
137
|
+
description?: string | null;
|
|
138
|
+
};
|
|
139
|
+
changes: {
|
|
140
|
+
nodeId: string;
|
|
141
|
+
label?: string;
|
|
142
|
+
summary?: string | null;
|
|
143
|
+
description?: string | null;
|
|
144
|
+
icon?: MapIcon | null;
|
|
145
|
+
}[];
|
|
146
|
+
edgeChanges: {
|
|
147
|
+
edgeId: string;
|
|
148
|
+
from: string;
|
|
149
|
+
to: string;
|
|
150
|
+
label: string;
|
|
151
|
+
}[];
|
|
152
|
+
presentation: {
|
|
153
|
+
nodeId: string;
|
|
154
|
+
x: number;
|
|
155
|
+
y: number;
|
|
156
|
+
width: number;
|
|
157
|
+
height: number;
|
|
158
|
+
}[];
|
|
159
|
+
findings: string[];
|
|
160
|
+
}
|
|
161
|
+
export type ProjectionReadOptions = ExecuteOptions;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { MapDrawingAnnotation } from "./types.js";
|
|
2
|
+
type AnnotationContent = Pick<MapDrawingAnnotation, "nodeId" | "title" | "goal" | "summary" | "description" | "width">;
|
|
3
|
+
export interface MapAnnotationLayout {
|
|
4
|
+
idBox: {
|
|
5
|
+
y: number;
|
|
6
|
+
height: number;
|
|
7
|
+
};
|
|
8
|
+
titleBox?: {
|
|
9
|
+
y: number;
|
|
10
|
+
height: number;
|
|
11
|
+
};
|
|
12
|
+
fields: Array<{
|
|
13
|
+
field: "goal" | "summary" | "description";
|
|
14
|
+
headingY: number;
|
|
15
|
+
headingHeight: number;
|
|
16
|
+
y: number;
|
|
17
|
+
height: number;
|
|
18
|
+
}>;
|
|
19
|
+
height: number;
|
|
20
|
+
}
|
|
21
|
+
/** Conservative word wrapping for Inter notes; native text uses the same reserved boxes. */
|
|
22
|
+
export declare function mapNoteTextHeight(text: string, width: number, fontSize?: number, lineHeight?: number): number;
|
|
23
|
+
export declare function mapAnnotationLayout(annotation: AnnotationContent): MapAnnotationLayout;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
function wordWidth(word, fontSize) {
|
|
2
|
+
return Array.from(word).reduce((width, character) => {
|
|
3
|
+
const factor = /\s/.test(character)
|
|
4
|
+
? 0.35
|
|
5
|
+
: /[il.,'!:;]/.test(character)
|
|
6
|
+
? 0.35
|
|
7
|
+
: /[MW@#]/.test(character)
|
|
8
|
+
? 1
|
|
9
|
+
: /[A-Z]/.test(character)
|
|
10
|
+
? 0.78
|
|
11
|
+
: /[a-z0-9]/.test(character)
|
|
12
|
+
? 0.62
|
|
13
|
+
: 1;
|
|
14
|
+
return width + factor * fontSize;
|
|
15
|
+
}, 0);
|
|
16
|
+
}
|
|
17
|
+
/** Conservative word wrapping for Inter notes; native text uses the same reserved boxes. */
|
|
18
|
+
export function mapNoteTextHeight(text, width, fontSize = 14, lineHeight = 20) {
|
|
19
|
+
let lines = 0;
|
|
20
|
+
for (const paragraph of text.split(/\r?\n/)) {
|
|
21
|
+
let lineWidth = 0;
|
|
22
|
+
let paragraphLines = 1;
|
|
23
|
+
for (const token of paragraph.split(/(\s+)/).filter(Boolean)) {
|
|
24
|
+
const tokenWidth = wordWidth(token, fontSize);
|
|
25
|
+
if (lineWidth && lineWidth + tokenWidth > width) {
|
|
26
|
+
paragraphLines++;
|
|
27
|
+
lineWidth = 0;
|
|
28
|
+
}
|
|
29
|
+
if (!lineWidth && /^\s+$/.test(token)) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
paragraphLines += Math.max(0, Math.ceil(tokenWidth / width) - 1);
|
|
33
|
+
lineWidth += tokenWidth > width ? tokenWidth % width : tokenWidth;
|
|
34
|
+
}
|
|
35
|
+
lines += paragraphLines;
|
|
36
|
+
}
|
|
37
|
+
return Math.max(1, lines) * lineHeight;
|
|
38
|
+
}
|
|
39
|
+
export function mapAnnotationLayout(annotation) {
|
|
40
|
+
const width = annotation.width - 40;
|
|
41
|
+
const idBox = {
|
|
42
|
+
y: 20,
|
|
43
|
+
height: 16,
|
|
44
|
+
};
|
|
45
|
+
let y = idBox.y + idBox.height + 8;
|
|
46
|
+
const titleBox = annotation.nodeId
|
|
47
|
+
? {
|
|
48
|
+
y,
|
|
49
|
+
height: mapNoteTextHeight(annotation.title, width, 16, 24),
|
|
50
|
+
}
|
|
51
|
+
: undefined;
|
|
52
|
+
if (titleBox) {
|
|
53
|
+
y += titleBox.height + 12;
|
|
54
|
+
}
|
|
55
|
+
const fields = [];
|
|
56
|
+
for (const field of [
|
|
57
|
+
"goal",
|
|
58
|
+
"summary",
|
|
59
|
+
"description",
|
|
60
|
+
]) {
|
|
61
|
+
const value = annotation[field];
|
|
62
|
+
if (value === undefined) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const height = mapNoteTextHeight(value, width);
|
|
66
|
+
fields.push({
|
|
67
|
+
field,
|
|
68
|
+
headingY: y,
|
|
69
|
+
headingHeight: 16,
|
|
70
|
+
y: y + 20,
|
|
71
|
+
height,
|
|
72
|
+
});
|
|
73
|
+
y += 20 + height + 14;
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
idBox,
|
|
77
|
+
...(titleBox
|
|
78
|
+
? {
|
|
79
|
+
titleBox,
|
|
80
|
+
}
|
|
81
|
+
: {}),
|
|
82
|
+
fields,
|
|
83
|
+
height: y + 6,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ArtifactReview, MapContext, MapContract } from "./types.js";
|
|
2
|
+
export interface MapContextOptions {
|
|
3
|
+
reviews?: ArtifactReview[];
|
|
4
|
+
sourceHashes?: Record<string, string>;
|
|
5
|
+
journeyHash?: string;
|
|
6
|
+
journeyRequirementIds?: string[];
|
|
7
|
+
detailMaps?: Record<string, MapContract>;
|
|
8
|
+
detailReadiness?: Record<string, boolean>;
|
|
9
|
+
reviewEvidenceValid?: boolean;
|
|
10
|
+
maxItems?: number;
|
|
11
|
+
maxBytes?: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function buildMapContext(raw: MapContract, options?: MapContextOptions): MapContext;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { findArtifactReview } from "./review.js";
|
|
2
|
+
import { hashMapContract, validateMapContract, validateMapDetail } from "./validation.js";
|
|
3
|
+
export function buildMapContext(raw, options = {}) {
|
|
4
|
+
const contract = validateMapContract(raw);
|
|
5
|
+
const mapHash = hashMapContract(contract);
|
|
6
|
+
const maxItems = options.maxItems ?? 100;
|
|
7
|
+
const maxBytes = options.maxBytes ?? 128 * 1024;
|
|
8
|
+
if (!Number.isInteger(maxItems) ||
|
|
9
|
+
maxItems < 1 ||
|
|
10
|
+
maxItems > 1000 ||
|
|
11
|
+
!Number.isInteger(maxBytes) ||
|
|
12
|
+
maxBytes < 4096 ||
|
|
13
|
+
maxBytes > 1024 * 1024) {
|
|
14
|
+
throw new Error("Map context requires 1–1000 items and a 4 KiB–1 MiB byte budget");
|
|
15
|
+
}
|
|
16
|
+
const reviewed = !!findArtifactReview(options.reviews ?? [], contract.kind, contract.id, mapHash) &&
|
|
17
|
+
options.reviewEvidenceValid !== false;
|
|
18
|
+
const staleSources = contract.sources.filter((source) => options.sourceHashes &&
|
|
19
|
+
Object.hasOwn(options.sourceHashes, source.id) &&
|
|
20
|
+
options.sourceHashes[source.id] !== source.sha256);
|
|
21
|
+
const unknownSources = contract.sources.filter((source) => !options.sourceHashes || !Object.hasOwn(options.sourceHashes, source.id));
|
|
22
|
+
const staleJourney = !!contract.journey &&
|
|
23
|
+
options.journeyHash !== undefined &&
|
|
24
|
+
options.journeyHash !== contract.journey.sha256;
|
|
25
|
+
const missingJourney = contract.kind === "integration-map" && !contract.journey;
|
|
26
|
+
const unknownJourney = !!contract.journey &&
|
|
27
|
+
(options.journeyHash === undefined || options.journeyRequirementIds === undefined);
|
|
28
|
+
const mappedRequirements = new Set([
|
|
29
|
+
...contract.nodes,
|
|
30
|
+
...contract.edges,
|
|
31
|
+
].flatMap((entry) => entry.requirementIds ?? []));
|
|
32
|
+
const unmappedRequirements = contract.requirements.filter((requirement) => !mappedRequirements.has(requirement.id));
|
|
33
|
+
const requirementIds = new Set(contract.requirements.map((requirement) => requirement.id));
|
|
34
|
+
const missingJourneyRequirements = contract.kind === "integration-map"
|
|
35
|
+
? (options.journeyRequirementIds ?? []).filter((id) => !requirementIds.has(id))
|
|
36
|
+
: [];
|
|
37
|
+
const details = contract.nodes
|
|
38
|
+
.filter((node) => node.kind === "detail-link")
|
|
39
|
+
.map((node) => {
|
|
40
|
+
const source = contract.sources.find((entry) => entry.id === node.sourceIds[0]);
|
|
41
|
+
const supplied = options.detailMaps?.[source.id];
|
|
42
|
+
const child = supplied ? validateMapDetail(contract, node, supplied) : undefined;
|
|
43
|
+
return {
|
|
44
|
+
nodeId: node.id,
|
|
45
|
+
sourceId: source.id,
|
|
46
|
+
path: source.path,
|
|
47
|
+
sha256: source.sha256,
|
|
48
|
+
fileId: source.fileId,
|
|
49
|
+
pageId: source.pageId,
|
|
50
|
+
frameId: source.frameId,
|
|
51
|
+
...(child
|
|
52
|
+
? {
|
|
53
|
+
mapId: child.id,
|
|
54
|
+
mapHash: hashMapContract(child),
|
|
55
|
+
}
|
|
56
|
+
: {}),
|
|
57
|
+
ready: !!child && options.detailReadiness?.[source.id] === true,
|
|
58
|
+
review: "separate-artifact",
|
|
59
|
+
contents: "not-inlined",
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
const unreadyDetails = details.filter((detail) => !detail.ready);
|
|
63
|
+
const blockers = contract.findings.filter((finding) => finding.severity === "blocking" && finding.status === "open");
|
|
64
|
+
const unknowns = [
|
|
65
|
+
...contract.nodes,
|
|
66
|
+
...contract.edges,
|
|
67
|
+
...contract.requirements,
|
|
68
|
+
].filter((entry) => entry.origin === "unknown");
|
|
69
|
+
const freshness = staleSources.length || staleJourney
|
|
70
|
+
? "stale"
|
|
71
|
+
: unknownSources.length || unknownJourney
|
|
72
|
+
? "unassessed"
|
|
73
|
+
: "matches-supplied-inputs";
|
|
74
|
+
const reasons = [];
|
|
75
|
+
if (!reviewed) {
|
|
76
|
+
reasons.push("Exact current contract owner review is missing or its evidence is invalid");
|
|
77
|
+
}
|
|
78
|
+
if (staleSources.length) {
|
|
79
|
+
reasons.push(`${staleSources.length} changed source(s): ${staleSources
|
|
80
|
+
.slice(0, 5)
|
|
81
|
+
.map((source) => source.id)
|
|
82
|
+
.join(", ")}`);
|
|
83
|
+
}
|
|
84
|
+
if (unknownSources.length || unknownJourney) {
|
|
85
|
+
reasons.push("Source freshness is unassessed for one or more declared references");
|
|
86
|
+
}
|
|
87
|
+
if (staleJourney || missingJourney) {
|
|
88
|
+
reasons.push(staleJourney
|
|
89
|
+
? "Referenced journey contract changed"
|
|
90
|
+
: "Integration map requires an explicit journey contract reference");
|
|
91
|
+
}
|
|
92
|
+
if (blockers.length) {
|
|
93
|
+
reasons.push(`${blockers.length} unresolved blocking finding(s): ${blockers
|
|
94
|
+
.slice(0, 5)
|
|
95
|
+
.map((finding) => finding.id)
|
|
96
|
+
.join(", ")}`);
|
|
97
|
+
}
|
|
98
|
+
if (unknowns.length) {
|
|
99
|
+
reasons.push(`${unknowns.length} unresolved unknown semantic element(s)`);
|
|
100
|
+
}
|
|
101
|
+
if (unmappedRequirements.length) {
|
|
102
|
+
reasons.push(`${unmappedRequirements.length} unmapped local requirement(s): ${unmappedRequirements
|
|
103
|
+
.slice(0, 5)
|
|
104
|
+
.map((requirement) => requirement.id)
|
|
105
|
+
.join(", ")}`);
|
|
106
|
+
}
|
|
107
|
+
if (missingJourneyRequirements.length) {
|
|
108
|
+
reasons.push(`${missingJourneyRequirements.length} omitted journey requirement(s): ${missingJourneyRequirements.slice(0, 5).join(", ")}`);
|
|
109
|
+
}
|
|
110
|
+
if (unreadyDetails.length) {
|
|
111
|
+
reasons.push(`${unreadyDetails.length} linked detail contract(s) require separate source/readiness/review checks; contents are not inlined`);
|
|
112
|
+
}
|
|
113
|
+
const context = {
|
|
114
|
+
schemaVersion: 1,
|
|
115
|
+
kind: "map-context",
|
|
116
|
+
mapKind: contract.kind,
|
|
117
|
+
mapId: contract.id,
|
|
118
|
+
mapHash,
|
|
119
|
+
title: contract.title,
|
|
120
|
+
goal: contract.goal,
|
|
121
|
+
...(contract.summary === undefined
|
|
122
|
+
? {}
|
|
123
|
+
: {
|
|
124
|
+
summary: contract.summary,
|
|
125
|
+
}),
|
|
126
|
+
...(contract.description === undefined
|
|
127
|
+
? {}
|
|
128
|
+
: {
|
|
129
|
+
description: contract.description,
|
|
130
|
+
}),
|
|
131
|
+
reviewed,
|
|
132
|
+
ready: reviewed &&
|
|
133
|
+
freshness === "matches-supplied-inputs" &&
|
|
134
|
+
!missingJourney &&
|
|
135
|
+
!blockers.length &&
|
|
136
|
+
!unknowns.length &&
|
|
137
|
+
!unmappedRequirements.length &&
|
|
138
|
+
!missingJourneyRequirements.length &&
|
|
139
|
+
!unreadyDetails.length,
|
|
140
|
+
freshness,
|
|
141
|
+
reasons,
|
|
142
|
+
nodes: contract.nodes.slice(0, maxItems),
|
|
143
|
+
edges: contract.edges.slice(0, maxItems),
|
|
144
|
+
requirements: contract.requirements.slice(0, maxItems),
|
|
145
|
+
findings: [
|
|
146
|
+
...blockers,
|
|
147
|
+
...contract.findings.filter((finding) => !blockers.includes(finding)),
|
|
148
|
+
].slice(0, maxItems),
|
|
149
|
+
sources: contract.sources.slice(0, maxItems),
|
|
150
|
+
details: details.slice(0, maxItems),
|
|
151
|
+
truncated: [
|
|
152
|
+
contract.nodes,
|
|
153
|
+
contract.edges,
|
|
154
|
+
contract.requirements,
|
|
155
|
+
contract.findings,
|
|
156
|
+
contract.sources,
|
|
157
|
+
details,
|
|
158
|
+
].some((entries) => entries.length > maxItems),
|
|
159
|
+
implementation: "not-assessed",
|
|
160
|
+
};
|
|
161
|
+
const collections = [
|
|
162
|
+
"nodes",
|
|
163
|
+
"edges",
|
|
164
|
+
"requirements",
|
|
165
|
+
"findings",
|
|
166
|
+
"sources",
|
|
167
|
+
"details",
|
|
168
|
+
];
|
|
169
|
+
const markTruncated = () => {
|
|
170
|
+
context.truncated = true;
|
|
171
|
+
context.ready = false;
|
|
172
|
+
const reason = "Context is truncated; inspect the complete contract before deciding readiness";
|
|
173
|
+
if (!context.reasons.includes(reason)) {
|
|
174
|
+
context.reasons.push(reason);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
if (context.truncated) {
|
|
178
|
+
markTruncated();
|
|
179
|
+
}
|
|
180
|
+
while (true) {
|
|
181
|
+
const visibleNodes = new Set(context.nodes.map((node) => node.id));
|
|
182
|
+
const visibleEdges = context.edges.filter((edge) => visibleNodes.has(edge.from) && visibleNodes.has(edge.to));
|
|
183
|
+
if (visibleEdges.length !== context.edges.length) {
|
|
184
|
+
markTruncated();
|
|
185
|
+
context.edges = visibleEdges;
|
|
186
|
+
}
|
|
187
|
+
if (Buffer.byteLength(JSON.stringify(context)) <= maxBytes) {
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
markTruncated();
|
|
191
|
+
const largest = collections.reduce((previous, key) => JSON.stringify(context[key]).length > JSON.stringify(context[previous]).length
|
|
192
|
+
? key
|
|
193
|
+
: previous);
|
|
194
|
+
if (!context[largest].length) {
|
|
195
|
+
const textKey = [
|
|
196
|
+
"goal",
|
|
197
|
+
"summary",
|
|
198
|
+
"description",
|
|
199
|
+
].reduce((previous, key) => (context[key]?.length ?? 0) > (context[previous]?.length ?? 0) ? key : previous);
|
|
200
|
+
context[textKey] = (context[textKey] ?? "").slice(0, Math.floor((context[textKey]?.length ?? 0) / 2));
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
context[largest].splice(Math.floor(context[largest].length / 2));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return context;
|
|
207
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export { validateMapContract, validateMapDetail, hashMapContract, maxMapBytes, } from "./validation.js";
|
|
3
|
+
export { renderMapMarkdown, renderMapMermaid, buildMapDrawingPlan } from "./render.js";
|
|
4
|
+
export { mapAnnotationLayout, mapNoteTextHeight, type MapAnnotationLayout, } from "./annotation-layout.js";
|
|
5
|
+
export { routeMapEdges, mapLabelHeight, mapDrawingGeometryProblems, type MapRoutingNode, type MapRoutingEdge, type MapEdgeRoute, } from "./layout.js";
|
|
6
|
+
export { createArtifactReview, validateArtifactReview, findArtifactReview, type ArtifactReviewInput, } from "./review.js";
|
|
7
|
+
export { createMapProposal, validateMapProposal, acceptMapProposal } from "./proposal.js";
|
|
8
|
+
export { buildMapContext, type MapContextOptions } from "./context.js";
|
|
9
|
+
export { readMapContract, writeMapContract, readMapProposal, writeMapProposal, readArtifactReviews, writeArtifactReview, verifyArtifactReviewEvidence, observeMapSources, type MapWriteOptions, type MapSourceObservation, } from "./store.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./types.js";
|
|
2
|
+
export { validateMapContract, validateMapDetail, hashMapContract, maxMapBytes, } from "./validation.js";
|
|
3
|
+
export { renderMapMarkdown, renderMapMermaid, buildMapDrawingPlan } from "./render.js";
|
|
4
|
+
export { mapAnnotationLayout, mapNoteTextHeight, } from "./annotation-layout.js";
|
|
5
|
+
export { routeMapEdges, mapLabelHeight, mapDrawingGeometryProblems, } from "./layout.js";
|
|
6
|
+
export { createArtifactReview, validateArtifactReview, findArtifactReview, } from "./review.js";
|
|
7
|
+
export { createMapProposal, validateMapProposal, acceptMapProposal } from "./proposal.js";
|
|
8
|
+
export { buildMapContext } from "./context.js";
|
|
9
|
+
export { readMapContract, writeMapContract, readMapProposal, writeMapProposal, readArtifactReviews, writeArtifactReview, verifyArtifactReviewEvidence, observeMapSources, } from "./store.js";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { MapContract } from "./types.js";
|
|
2
|
+
export interface MapRoutingNode {
|
|
3
|
+
id: string;
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
export interface MapRoutingEdge {
|
|
10
|
+
id: string;
|
|
11
|
+
from: string;
|
|
12
|
+
to: string;
|
|
13
|
+
label: string;
|
|
14
|
+
}
|
|
15
|
+
export interface MapEdgeRoute {
|
|
16
|
+
points: Array<{
|
|
17
|
+
x: number;
|
|
18
|
+
y: number;
|
|
19
|
+
}>;
|
|
20
|
+
labelBox: {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
width: number;
|
|
24
|
+
height: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export declare function mapDrawingGeometryProblems(nodes: MapRoutingNode[], edges: Array<MapEdgeRoute & {
|
|
28
|
+
id: string;
|
|
29
|
+
}>): string[];
|
|
30
|
+
export declare function mapLabelHeight(text: string, width: number): number;
|
|
31
|
+
export declare function mapEdgeLabelWidth(label: string): number;
|
|
32
|
+
/** Labels occupy dedicated gaps; routes stay outside those boxes and all card interiors. */
|
|
33
|
+
export declare function routeMapEdges<T extends MapRoutingEdge>(nodes: MapRoutingNode[], edges: T[]): Array<T & MapEdgeRoute>;
|
|
34
|
+
/** Ignore DFS return edges for placement only; every connector remains in the drawing. */
|
|
35
|
+
export declare function mapNodeLayers(contract: MapContract): Map<string, number>;
|