@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.
Files changed (129) hide show
  1. package/dist/adapters.d.ts +1 -1
  2. package/dist/adapters.js +3 -0
  3. package/dist/approvals.d.ts +4 -1
  4. package/dist/approvals.js +7 -4
  5. package/dist/baseline-coverage.js +3 -3
  6. package/dist/baselines-config.d.ts +3 -0
  7. package/dist/baselines-config.js +3 -0
  8. package/dist/baselines.d.ts +7 -0
  9. package/dist/baselines.js +90 -21
  10. package/dist/bounded-file.d.ts +2 -0
  11. package/dist/bounded-file.js +38 -0
  12. package/dist/cli-main.js +64 -3
  13. package/dist/design-commands/context.d.ts +9 -0
  14. package/dist/design-commands/context.js +1 -0
  15. package/dist/design-commands/index.d.ts +3 -0
  16. package/dist/design-commands/index.js +7 -0
  17. package/dist/design-commands/map-command.d.ts +4 -0
  18. package/dist/design-commands/map-command.js +685 -0
  19. package/dist/design-commands/release-command.d.ts +3 -0
  20. package/dist/design-commands/release-command.js +239 -0
  21. package/dist/design-releases/artifact-paths.d.ts +6 -0
  22. package/dist/design-releases/artifact-paths.js +82 -0
  23. package/dist/design-releases/assemble.d.ts +3 -0
  24. package/dist/design-releases/assemble.js +176 -0
  25. package/dist/design-releases/check.d.ts +16 -0
  26. package/dist/design-releases/check.js +323 -0
  27. package/dist/design-releases/context.d.ts +2 -0
  28. package/dist/design-releases/context.js +119 -0
  29. package/dist/design-releases/contract-graph.d.ts +13 -0
  30. package/dist/design-releases/contract-graph.js +75 -0
  31. package/dist/design-releases/fidelity.d.ts +23 -0
  32. package/dist/design-releases/fidelity.js +328 -0
  33. package/dist/design-releases/finalize.d.ts +4 -0
  34. package/dist/design-releases/finalize.js +201 -0
  35. package/dist/design-releases/index.d.ts +8 -0
  36. package/dist/design-releases/index.js +7 -0
  37. package/dist/design-releases/live.d.ts +22 -0
  38. package/dist/design-releases/live.js +97 -0
  39. package/dist/design-releases/snippets.d.ts +5 -0
  40. package/dist/design-releases/snippets.js +89 -0
  41. package/dist/design-releases/store.d.ts +10 -0
  42. package/dist/design-releases/store.js +205 -0
  43. package/dist/design-releases/types.d.ts +161 -0
  44. package/dist/design-releases/types.js +1 -0
  45. package/dist/design-releases/validation.d.ts +6 -0
  46. package/dist/design-releases/validation.js +384 -0
  47. package/dist/design-releases/verify.d.ts +3 -0
  48. package/dist/design-releases/verify.js +148 -0
  49. package/dist/image-codec.d.ts +3 -0
  50. package/dist/image-codec.js +78 -0
  51. package/dist/image-evidence.d.ts +15 -0
  52. package/dist/image-evidence.js +121 -0
  53. package/dist/index.d.ts +5 -0
  54. package/dist/index.js +5 -0
  55. package/dist/inspection.js +13 -5
  56. package/dist/instance/config-schema.d.ts +4 -0
  57. package/dist/instance/config-schema.js +7 -0
  58. package/dist/map-penpot/content-validation.d.ts +3 -0
  59. package/dist/map-penpot/content-validation.js +57 -0
  60. package/dist/map-penpot/content.d.ts +22 -0
  61. package/dist/map-penpot/content.js +173 -0
  62. package/dist/map-penpot/details.d.ts +4 -0
  63. package/dist/map-penpot/details.js +84 -0
  64. package/dist/map-penpot/evidence-error.d.ts +5 -0
  65. package/dist/map-penpot/evidence-error.js +9 -0
  66. package/dist/map-penpot/icons.d.ts +5 -0
  67. package/dist/map-penpot/icons.js +79 -0
  68. package/dist/map-penpot/import-content.d.ts +2 -0
  69. package/dist/map-penpot/import-content.js +150 -0
  70. package/dist/map-penpot/import.d.ts +5 -0
  71. package/dist/map-penpot/import.js +255 -0
  72. package/dist/map-penpot/index.d.ts +11 -0
  73. package/dist/map-penpot/index.js +10 -0
  74. package/dist/map-penpot/path-geometry.d.ts +5 -0
  75. package/dist/map-penpot/path-geometry.js +51 -0
  76. package/dist/map-penpot/presentation.d.ts +20 -0
  77. package/dist/map-penpot/presentation.js +205 -0
  78. package/dist/map-penpot/projection.d.ts +8 -0
  79. package/dist/map-penpot/projection.js +176 -0
  80. package/dist/map-penpot/publish.d.ts +4 -0
  81. package/dist/map-penpot/publish.js +301 -0
  82. package/dist/map-penpot/release-maps.d.ts +21 -0
  83. package/dist/map-penpot/release-maps.js +104 -0
  84. package/dist/map-penpot/snippets.d.ts +4 -0
  85. package/dist/map-penpot/snippets.js +164 -0
  86. package/dist/map-penpot/status-labels.d.ts +7 -0
  87. package/dist/map-penpot/status-labels.js +7 -0
  88. package/dist/map-penpot/types.d.ts +161 -0
  89. package/dist/map-penpot/types.js +1 -0
  90. package/dist/maps/annotation-layout.d.ts +24 -0
  91. package/dist/maps/annotation-layout.js +85 -0
  92. package/dist/maps/context.d.ts +13 -0
  93. package/dist/maps/context.js +207 -0
  94. package/dist/maps/index.d.ts +9 -0
  95. package/dist/maps/index.js +9 -0
  96. package/dist/maps/layout.d.ts +35 -0
  97. package/dist/maps/layout.js +364 -0
  98. package/dist/maps/proposal.d.ts +5 -0
  99. package/dist/maps/proposal.js +67 -0
  100. package/dist/maps/render.d.ts +4 -0
  101. package/dist/maps/render.js +331 -0
  102. package/dist/maps/review.d.ts +16 -0
  103. package/dist/maps/review.js +90 -0
  104. package/dist/maps/store.d.ts +21 -0
  105. package/dist/maps/store.js +222 -0
  106. package/dist/maps/types.d.ts +206 -0
  107. package/dist/maps/types.js +1 -0
  108. package/dist/maps/validation.d.ts +10 -0
  109. package/dist/maps/validation.js +375 -0
  110. package/dist/skill-routing.js +4 -0
  111. package/dist/version.d.ts +1 -1
  112. package/dist/version.js +1 -1
  113. package/package.json +2 -1
  114. package/skills/penpair-design-release/SKILL.md +55 -0
  115. package/skills/penpair-design-release/agents/openai.yaml +6 -0
  116. package/skills/penpair-design-release/references/workflow.md +55 -0
  117. package/skills/penpair-diagrams/SKILL.md +52 -0
  118. package/skills/penpair-diagrams/agents/openai.yaml +6 -0
  119. package/skills/penpair-diagrams/references/workflow.md +84 -0
  120. package/skills/penpair-handoff/SKILL.md +7 -0
  121. package/skills/penpair-handoff/references/handoff-flow.md +7 -1
  122. package/skills/penpair-integration-map/SKILL.md +44 -0
  123. package/skills/penpair-integration-map/agents/openai.yaml +6 -0
  124. package/skills/penpair-integration-map/references/workflow.md +33 -0
  125. package/skills/penpair-journey-map/SKILL.md +42 -0
  126. package/skills/penpair-journey-map/agents/openai.yaml +6 -0
  127. package/skills/penpair-journey-map/references/workflow.md +35 -0
  128. package/skills/penpair-verify/SKILL.md +7 -0
  129. 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>;