easyeda-mcp-pro 0.9.0 → 0.11.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.
@@ -184,8 +184,8 @@ export declare const PhysicalConstraintSchema: z.ZodObject<{
184
184
  board: "board";
185
185
  net: "net";
186
186
  interface: "interface";
187
- device: "device";
188
187
  block: "block";
188
+ device: "device";
189
189
  zone: "zone";
190
190
  }>;
191
191
  targetRef: z.ZodOptional<z.ZodString>;
@@ -525,8 +525,8 @@ export declare const CircuitIRSchema: z.ZodObject<{
525
525
  board: "board";
526
526
  net: "net";
527
527
  interface: "interface";
528
- device: "device";
529
528
  block: "block";
529
+ device: "device";
530
530
  zone: "zone";
531
531
  }>;
532
532
  targetRef: z.ZodOptional<z.ZodString>;
@@ -3,35 +3,35 @@ export const PROFILE_DEFINITIONS = {
3
3
  name: 'core',
4
4
  label: 'Core',
5
5
  description: 'High-confidence tools: diagnostics, EasyEDA API inventory, schematic, BOM, DRC/ERC, board layers/stackup, Gerber export',
6
- approxToolCount: '39',
6
+ approxToolCount: '41',
7
7
  isDefault: true,
8
8
  },
9
9
  pro: {
10
10
  name: 'pro',
11
11
  label: 'Pro',
12
12
  description: 'Adds pick-and-place, PDF, netlist export for manufacturing workflows',
13
- approxToolCount: '42',
13
+ approxToolCount: '44',
14
14
  isDefault: false,
15
15
  },
16
16
  full: {
17
17
  name: 'full',
18
18
  label: 'Full',
19
19
  description: 'Adds controlled documented EasyEDA API method calls for full runtime control without raw JavaScript execution',
20
- approxToolCount: '49',
20
+ approxToolCount: '51',
21
21
  isDefault: false,
22
22
  },
23
23
  dev: {
24
24
  name: 'dev',
25
25
  label: 'Dev',
26
26
  description: 'Adds diagnostics probes for bridge methods and live component runtime shape inspection',
27
- approxToolCount: '53',
27
+ approxToolCount: '55',
28
28
  isDefault: false,
29
29
  },
30
30
  experimental: {
31
31
  name: 'experimental',
32
32
  label: 'Experimental',
33
33
  description: 'MCP Apps, Tasks, simulation, autorouter, AI action plans',
34
- approxToolCount: '51',
34
+ approxToolCount: '53',
35
35
  isDefault: false,
36
36
  },
37
37
  };
@@ -1 +1 @@
1
- export declare const SERVER_VERSION = "0.9.0";
1
+ export declare const SERVER_VERSION = "0.11.0";
@@ -1,2 +1,2 @@
1
- export const SERVER_VERSION = '0.9.0'; // x-release-please-version
1
+ export const SERVER_VERSION = '0.11.0'; // x-release-please-version
2
2
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/config/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC,CAAC,2BAA2B"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/config/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC,CAAC,2BAA2B"}
@@ -102,6 +102,78 @@ export const PcbConstraintCodeMap = {
102
102
  severity: 'error',
103
103
  remediationHint: 'Define explicit creepage and clearance distances for high-voltage nets. Minimum 4mm clearance for 250V AC is recommended.',
104
104
  },
105
+ DRILL_FILE_MISSING: {
106
+ code: 'PCB_DRILL_FILE_MISSING',
107
+ message: 'NC drill file is missing from the manufacturing package',
108
+ severity: 'error',
109
+ remediationHint: 'Re-export the manufacturing package with Excellon/NC drill files included',
110
+ },
111
+ COPPER_EDGE_CLEARANCE: {
112
+ code: 'PCB_COPPER_EDGE_CLEARANCE',
113
+ message: 'Copper is too close to the board edge',
114
+ severity: 'error',
115
+ remediationHint: 'Move copper, zones, vias, and pads away from the routed board edge or increase the copper-to-edge clearance rule',
116
+ },
117
+ DRILL_TOO_SMALL: {
118
+ code: 'PCB_DRILL_TOO_SMALL',
119
+ message: 'Minimum drill diameter is below common fabrication capability',
120
+ severity: 'warning',
121
+ remediationHint: 'Increase finished drill diameter or confirm the selected fabricator supports the requested drill size',
122
+ },
123
+ ANNULAR_RING_TOO_SMALL: {
124
+ code: 'PCB_ANNULAR_RING_TOO_SMALL',
125
+ message: 'Annular ring is below recommended manufacturing minimum',
126
+ severity: 'warning',
127
+ remediationHint: 'Increase pad/via diameter or reduce drill size to maintain enough annular ring for registration tolerance',
128
+ },
129
+ SOLDERMASK_SLIVER: {
130
+ code: 'PCB_SOLDERMASK_SLIVER',
131
+ message: 'Soldermask sliver is below recommended minimum',
132
+ severity: 'warning',
133
+ remediationHint: 'Increase pad spacing, adjust soldermask expansion, or remove soldermask dams intentionally where supported',
134
+ },
135
+ SILKSCREEN_OVER_PAD: {
136
+ code: 'PCB_SILKSCREEN_OVER_PAD',
137
+ message: 'Silkscreen overlaps exposed copper pads',
138
+ severity: 'warning',
139
+ remediationHint: 'Move or clip silkscreen references/graphics so they do not print over solderable pads',
140
+ },
141
+ TOOLING_HOLE_MISSING: {
142
+ code: 'PCB_TOOLING_HOLE_MISSING',
143
+ message: 'Tooling holes are missing for assembly/panel fixture alignment',
144
+ severity: 'warning',
145
+ remediationHint: 'Add tooling holes or confirm the assembler will provide panel-level tooling holes during panelization',
146
+ },
147
+ POLARITY_MARK_MISSING: {
148
+ code: 'PCB_POLARITY_MARK_MISSING',
149
+ message: 'Polarity or orientation marks are missing for polarized components',
150
+ severity: 'warning',
151
+ remediationHint: 'Add clear silkscreen/courtyard polarity markers for diodes, LEDs, electrolytic capacitors, IC pin 1, and connectors',
152
+ },
153
+ COMPONENT_SPACING_VIOLATION: {
154
+ code: 'PCB_COMPONENT_SPACING_VIOLATION',
155
+ message: 'Component spacing/courtyard violations detected',
156
+ severity: 'warning',
157
+ remediationHint: 'Increase component-to-component spacing or review courtyard overlap for assembly nozzle, hand-solder, and rework access',
158
+ },
159
+ TESTPOINT_COVERAGE_LOW: {
160
+ code: 'PCB_TESTPOINT_COVERAGE_LOW',
161
+ message: 'Critical nets are missing test points',
162
+ severity: 'warning',
163
+ remediationHint: 'Add accessible test pads for power rails, ground, reset, boot, programming, clocks, and key interfaces',
164
+ },
165
+ PROGRAMMING_HEADER_MISSING: {
166
+ code: 'PCB_PROGRAMMING_HEADER_MISSING',
167
+ message: 'Programming/debug header is required but missing',
168
+ severity: 'error',
169
+ remediationHint: 'Add a programming/debug connector or documented test pads for SWD/JTAG/UART/BOOT/RESET access',
170
+ },
171
+ FAB_NOTES_MISSING: {
172
+ code: 'PCB_FAB_NOTES_MISSING',
173
+ message: 'Fabrication notes are missing from the handoff package',
174
+ severity: 'warning',
175
+ remediationHint: 'Add fabrication notes covering board thickness, copper weight, finish, soldermask color, impedance requirements, panelization, and special instructions',
176
+ },
105
177
  };
106
178
  // ── Factory helpers ────────────────────────────────────────────────────────
107
179
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/pcb-constraints/errors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/pcb-constraints/errors.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import type { ConstraintSeverity } from '../circuit/types.js';
7
7
  /** Machine-readable code identifying a PCB constraint check. */
8
- export type PcbConstraintCode = 'PCB_MISSING_OUTLINE' | 'PCB_OUTLINE_TOO_SMALL' | 'PCB_MISSING_STACKUP' | 'PCB_LAYER_COUNT_MISMATCH' | 'PCB_MISSING_MOUNTING_HOLES' | 'PCB_MISSING_NET_CLASSES' | 'PCB_INVALID_CLEARANCE' | 'PCB_KEEPOUT_VIOLATION' | 'PCB_MISSING_PLACEMENT_ZONES' | 'PCB_MISSING_MANUFACTURING_CONSTRAINTS' | 'PCB_FIDUCIAL_REQUIRED' | 'PCB_HIGH_VOLTAGE_CLEARANCE' | 'PCB_CONSTRAINT_INTERNAL';
8
+ export type PcbConstraintCode = 'PCB_MISSING_OUTLINE' | 'PCB_OUTLINE_TOO_SMALL' | 'PCB_MISSING_STACKUP' | 'PCB_LAYER_COUNT_MISMATCH' | 'PCB_MISSING_MOUNTING_HOLES' | 'PCB_MISSING_NET_CLASSES' | 'PCB_INVALID_CLEARANCE' | 'PCB_KEEPOUT_VIOLATION' | 'PCB_MISSING_PLACEMENT_ZONES' | 'PCB_MISSING_MANUFACTURING_CONSTRAINTS' | 'PCB_FIDUCIAL_REQUIRED' | 'PCB_HIGH_VOLTAGE_CLEARANCE' | 'PCB_DRILL_FILE_MISSING' | 'PCB_COPPER_EDGE_CLEARANCE' | 'PCB_DRILL_TOO_SMALL' | 'PCB_ANNULAR_RING_TOO_SMALL' | 'PCB_SOLDERMASK_SLIVER' | 'PCB_SILKSCREEN_OVER_PAD' | 'PCB_TOOLING_HOLE_MISSING' | 'PCB_POLARITY_MARK_MISSING' | 'PCB_COMPONENT_SPACING_VIOLATION' | 'PCB_TESTPOINT_COVERAGE_LOW' | 'PCB_PROGRAMMING_HEADER_MISSING' | 'PCB_FAB_NOTES_MISSING' | 'PCB_CONSTRAINT_INTERNAL';
9
9
  /**
10
10
  * A single PCB constraint issue (error or warning).
11
11
  *
@@ -74,6 +74,44 @@ export interface PcbConstraintInput {
74
74
  hasFiducials?: boolean;
75
75
  /** Whether test pads are defined. */
76
76
  hasTestPads?: boolean;
77
+ /** Whether the drill/NC drill file is present in the manufacturing package. */
78
+ hasDrillFile?: boolean;
79
+ /** Minimum copper-to-board-edge clearance observed in mm. */
80
+ minCopperToEdgeMm?: number;
81
+ /** Number of copper-to-edge clearance violations. */
82
+ copperToEdgeViolationCount?: number;
83
+ /** Minimum finished/mechanical drill diameter observed in mm. */
84
+ minDrillMm?: number;
85
+ /** Minimum annular ring observed in mm. */
86
+ minAnnularRingMm?: number;
87
+ /** Minimum soldermask web/sliver observed in mm. */
88
+ minSolderMaskSliverMm?: number;
89
+ /** Count of soldermask sliver violations. */
90
+ solderMaskSliverViolationCount?: number;
91
+ /** Count of silkscreen objects overlapping copper pads. */
92
+ silkscreenOverPadCount?: number;
93
+ /** Number of SMT components requiring assembly alignment. */
94
+ smtComponentCount?: number;
95
+ /** Count of global/local fiducials. Overrides hasFiducials when provided. */
96
+ fiducialCount?: number;
97
+ /** Count of tooling holes for panel/fixture assembly. */
98
+ toolingHoleCount?: number;
99
+ /** Number of polarized/orientation-sensitive components. */
100
+ polarizedComponentCount?: number;
101
+ /** Number of polarized components with explicit polarity/orientation marks. */
102
+ polarityMarkCount?: number;
103
+ /** Count of component courtyard/spacing violations. */
104
+ componentSpacingViolationCount?: number;
105
+ /** Critical nets that should be accessible for bring-up/test. */
106
+ criticalNetNames?: string[];
107
+ /** Nets that have test pads/probes. */
108
+ testPointNets?: string[];
109
+ /** Whether a programming/debug header is present when required. */
110
+ hasProgrammingHeader?: boolean;
111
+ /** Whether this design requires a programming/debug header. */
112
+ requiresProgrammingHeader?: boolean;
113
+ /** Whether fabrication notes are present in the handoff package. */
114
+ hasFabricationNotes?: boolean;
77
115
  /** Whether the design contains high-voltage domains. */
78
116
  hasHighVoltage?: boolean;
79
117
  /** Manufacturing process (lead-free, lead-based, mixed). */
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * PCB constraint validation rules.
3
3
  *
4
- * Implements constraint checks that operate on {@link PcbConstraintInput}
4
+ * Implements production constraint checks that operate on {@link PcbConstraintInput}
5
5
  * and produce structured {@link PcbConstraintIssue}s.
6
6
  *
7
7
  * Rules:
@@ -17,6 +17,17 @@
17
17
  * 10. Missing manufacturing constraints (warning)
18
18
  * 11. Fiducials recommended for SMT (warning)
19
19
  * 12. High-voltage creepage/clearance (error)
20
+ * 13. Missing drill file (error)
21
+ * 14. Copper-to-edge clearance violations (error)
22
+ * 15. Drill/annular-ring manufacturing risk (warning)
23
+ * 16. Soldermask sliver risk (warning)
24
+ * 17. Silkscreen over exposed pads (warning)
25
+ * 18. Tooling holes for assembly/panel fixtures (warning)
26
+ * 19. Polarity/orientation marks (warning)
27
+ * 20. Component spacing/courtyard violations (warning)
28
+ * 21. Critical-net testpoint coverage (warning)
29
+ * 22. Programming/debug header access (error)
30
+ * 23. Fabrication notes completeness (warning)
20
31
  *
21
32
  * @module
22
33
  */
@@ -24,7 +35,7 @@ import type { PcbConstraintInput, PcbConstraintResult } from './types.js';
24
35
  /**
25
36
  * Run all PCB constraint validation rules against board data.
26
37
  *
27
- * Orchestrates 12 rules:
38
+ * Orchestrates 23 rules:
28
39
  * 1. Missing outline — error if board outline is not defined
29
40
  * 2. Outline too small — warning if dimensions are below 5mm
30
41
  * 3. Missing stackup — warning if no detailed layer stack
@@ -37,6 +48,17 @@ import type { PcbConstraintInput, PcbConstraintResult } from './types.js';
37
48
  * 10. Missing mfg constraints — warning if process/quantity unspecified
38
49
  * 11. Fiducials recommended — warning if SMT board without fiducials
39
50
  * 12. High-voltage clearance — error if HV domain without proper clearance
51
+ * 13. Drill file — error if NC drill file is missing
52
+ * 14. Copper edge clearance — error if copper is too close to board edge
53
+ * 15. Drill/annular ring — warning if process dimensions are risky
54
+ * 16. Soldermask sliver — warning if soldermask web is too small
55
+ * 17. Silkscreen over pad — warning if silkscreen overlaps exposed pads
56
+ * 18. Tooling holes — warning if assembly tooling holes are missing
57
+ * 19. Polarity marks — warning if orientation-sensitive parts lack marks
58
+ * 20. Component spacing — warning if courtyard/spacing violations exist
59
+ * 21. Testpoint coverage — warning if critical nets lack test access
60
+ * 22. Programming header — error if required debug/programming access is missing
61
+ * 23. Fabrication notes — warning if handoff notes are missing
40
62
  */
41
63
  export declare function validatePcbConstraints(input: PcbConstraintInput): PcbConstraintResult;
42
64
  /**
@@ -60,6 +82,25 @@ export declare function fromPcbIntent(pcb: {
60
82
  mountingHoles?: unknown[];
61
83
  fiducials?: unknown[];
62
84
  testPads?: unknown[];
85
+ hasDrillFile?: boolean;
86
+ minCopperToEdgeMm?: number;
87
+ copperToEdgeViolationCount?: number;
88
+ minDrillMm?: number;
89
+ minAnnularRingMm?: number;
90
+ minSolderMaskSliverMm?: number;
91
+ solderMaskSliverViolationCount?: number;
92
+ silkscreenOverPadCount?: number;
93
+ smtComponentCount?: number;
94
+ fiducialCount?: number;
95
+ toolingHoleCount?: number;
96
+ polarizedComponentCount?: number;
97
+ polarityMarkCount?: number;
98
+ componentSpacingViolationCount?: number;
99
+ criticalNetNames?: string[];
100
+ testPointNets?: string[];
101
+ hasProgrammingHeader?: boolean;
102
+ requiresProgrammingHeader?: boolean;
103
+ hasFabricationNotes?: boolean;
63
104
  manufacturingProcess?: string;
64
105
  manufacturing?: {
65
106
  process?: string;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * PCB constraint validation rules.
3
3
  *
4
- * Implements constraint checks that operate on {@link PcbConstraintInput}
4
+ * Implements production constraint checks that operate on {@link PcbConstraintInput}
5
5
  * and produce structured {@link PcbConstraintIssue}s.
6
6
  *
7
7
  * Rules:
@@ -17,10 +17,31 @@
17
17
  * 10. Missing manufacturing constraints (warning)
18
18
  * 11. Fiducials recommended for SMT (warning)
19
19
  * 12. High-voltage creepage/clearance (error)
20
+ * 13. Missing drill file (error)
21
+ * 14. Copper-to-edge clearance violations (error)
22
+ * 15. Drill/annular-ring manufacturing risk (warning)
23
+ * 16. Soldermask sliver risk (warning)
24
+ * 17. Silkscreen over exposed pads (warning)
25
+ * 18. Tooling holes for assembly/panel fixtures (warning)
26
+ * 19. Polarity/orientation marks (warning)
27
+ * 20. Component spacing/courtyard violations (warning)
28
+ * 21. Critical-net testpoint coverage (warning)
29
+ * 22. Programming/debug header access (error)
30
+ * 23. Fabrication notes completeness (warning)
20
31
  *
21
32
  * @module
22
33
  */
23
34
  import { pcbError, pcbWarning } from './errors.js';
35
+ const MIN_COPPER_TO_EDGE_MM = 0.25;
36
+ const MIN_DRILL_MM = 0.2;
37
+ const MIN_ANNULAR_RING_MM = 0.1;
38
+ const MIN_SOLDERMASK_SLIVER_MM = 0.1;
39
+ const MIN_SMT_FIDUCIAL_COUNT = 3;
40
+ const MIN_TOOLING_HOLE_COUNT = 2;
41
+ const MIN_TESTPOINT_COVERAGE_RATIO = 0.8;
42
+ function normalizedNetName(name) {
43
+ return name.trim().toUpperCase();
44
+ }
24
45
  // ── Rule: missing board outline ────────────────────────────────────────────
25
46
  function checkMissingOutline(input) {
26
47
  const issues = [];
@@ -155,6 +176,191 @@ function checkHighVoltageClearance(input) {
155
176
  }
156
177
  return issues;
157
178
  }
179
+ // ── Production review: drill file completeness ─────────────────────────────
180
+ function checkDrillFile(input) {
181
+ if (input.hasDrillFile !== false)
182
+ return [];
183
+ return [
184
+ pcbError('PCB_DRILL_FILE_MISSING', 'NC drill file is missing from the manufacturing package', {
185
+ remediationHint: 'Re-export the manufacturing package with Excellon/NC drill files included',
186
+ details: { hasDrillFile: input.hasDrillFile },
187
+ }),
188
+ ];
189
+ }
190
+ // ── Production review: copper-to-edge clearance ────────────────────────────
191
+ function checkCopperToEdgeClearance(input) {
192
+ const issues = [];
193
+ const violationCount = input.copperToEdgeViolationCount ?? 0;
194
+ const belowMinimum = input.minCopperToEdgeMm !== undefined && input.minCopperToEdgeMm < MIN_COPPER_TO_EDGE_MM;
195
+ if (violationCount > 0 || belowMinimum) {
196
+ issues.push(pcbError('PCB_COPPER_EDGE_CLEARANCE', `Copper-to-edge clearance is below ${MIN_COPPER_TO_EDGE_MM}mm${input.minCopperToEdgeMm !== undefined ? ` (observed ${input.minCopperToEdgeMm}mm)` : ''}`, {
197
+ remediationHint: 'Move copper, zones, vias, and pads away from the routed board edge or increase the copper-to-edge clearance rule',
198
+ details: {
199
+ minCopperToEdgeMm: input.minCopperToEdgeMm,
200
+ copperToEdgeViolationCount: violationCount,
201
+ recommendedMinimumMm: MIN_COPPER_TO_EDGE_MM,
202
+ },
203
+ }));
204
+ }
205
+ return issues;
206
+ }
207
+ // ── Production review: drill and annular ring capability ───────────────────
208
+ function checkDrillManufacturability(input) {
209
+ const issues = [];
210
+ if (input.minDrillMm !== undefined && input.minDrillMm < MIN_DRILL_MM) {
211
+ issues.push(pcbWarning('PCB_DRILL_TOO_SMALL', `Minimum drill diameter ${input.minDrillMm}mm is below common ${MIN_DRILL_MM}mm fabrication capability`, {
212
+ remediationHint: 'Increase finished drill diameter or confirm the selected fabricator supports the requested drill size',
213
+ details: { minDrillMm: input.minDrillMm, recommendedMinimumMm: MIN_DRILL_MM },
214
+ }));
215
+ }
216
+ if (input.minAnnularRingMm !== undefined && input.minAnnularRingMm < MIN_ANNULAR_RING_MM) {
217
+ issues.push(pcbWarning('PCB_ANNULAR_RING_TOO_SMALL', `Minimum annular ring ${input.minAnnularRingMm}mm is below recommended ${MIN_ANNULAR_RING_MM}mm`, {
218
+ remediationHint: 'Increase pad/via diameter or reduce drill size to maintain enough annular ring for registration tolerance',
219
+ details: {
220
+ minAnnularRingMm: input.minAnnularRingMm,
221
+ recommendedMinimumMm: MIN_ANNULAR_RING_MM,
222
+ },
223
+ }));
224
+ }
225
+ return issues;
226
+ }
227
+ // ── Production review: soldermask sliver ───────────────────────────────────
228
+ function checkSolderMaskSliver(input) {
229
+ const violationCount = input.solderMaskSliverViolationCount ?? 0;
230
+ const belowMinimum = input.minSolderMaskSliverMm !== undefined &&
231
+ input.minSolderMaskSliverMm < MIN_SOLDERMASK_SLIVER_MM;
232
+ if (!belowMinimum && violationCount === 0)
233
+ return [];
234
+ return [
235
+ pcbWarning('PCB_SOLDERMASK_SLIVER', `Soldermask sliver is below ${MIN_SOLDERMASK_SLIVER_MM}mm${input.minSolderMaskSliverMm !== undefined ? ` (observed ${input.minSolderMaskSliverMm}mm)` : ''}`, {
236
+ remediationHint: 'Increase pad spacing, adjust soldermask expansion, or remove soldermask dams intentionally where supported',
237
+ details: {
238
+ minSolderMaskSliverMm: input.minSolderMaskSliverMm,
239
+ solderMaskSliverViolationCount: violationCount,
240
+ recommendedMinimumMm: MIN_SOLDERMASK_SLIVER_MM,
241
+ },
242
+ }),
243
+ ];
244
+ }
245
+ // ── Production review: silkscreen over pads ────────────────────────────────
246
+ function checkSilkscreenOverPads(input) {
247
+ const count = input.silkscreenOverPadCount ?? 0;
248
+ if (count <= 0)
249
+ return [];
250
+ return [
251
+ pcbWarning('PCB_SILKSCREEN_OVER_PAD', `${count} silkscreen object${count === 1 ? '' : 's'} overlap exposed copper pads`, {
252
+ remediationHint: 'Move or clip silkscreen references/graphics so they do not print over solderable pads',
253
+ details: { silkscreenOverPadCount: count },
254
+ }),
255
+ ];
256
+ }
257
+ // ── Production review: assembly fixtures/fiducials ─────────────────────────
258
+ function checkAssemblyAlignmentFeatures(input) {
259
+ const issues = [];
260
+ const smtCount = input.smtComponentCount ?? 0;
261
+ const fiducialCount = input.fiducialCount ?? (input.hasFiducials ? MIN_SMT_FIDUCIAL_COUNT : 0);
262
+ const toolingHoleCount = input.toolingHoleCount ?? 0;
263
+ if (smtCount > 0 && fiducialCount < MIN_SMT_FIDUCIAL_COUNT) {
264
+ issues.push(pcbWarning('PCB_FIDUCIAL_REQUIRED', `SMT assembly has ${fiducialCount} fiducial(s); at least ${MIN_SMT_FIDUCIAL_COUNT} are recommended`, {
265
+ remediationHint: 'Add at least 3 fiducial marks (global, near board corners) for pick-and-place machine alignment',
266
+ details: {
267
+ smtComponentCount: smtCount,
268
+ fiducialCount,
269
+ recommendedMinimum: MIN_SMT_FIDUCIAL_COUNT,
270
+ },
271
+ }));
272
+ }
273
+ if (smtCount > 0 && toolingHoleCount < MIN_TOOLING_HOLE_COUNT) {
274
+ issues.push(pcbWarning('PCB_TOOLING_HOLE_MISSING', `Assembly tooling holes are below recommended count (${toolingHoleCount}/${MIN_TOOLING_HOLE_COUNT})`, {
275
+ remediationHint: 'Add tooling holes or confirm the assembler will provide panel-level tooling holes during panelization',
276
+ details: {
277
+ smtComponentCount: smtCount,
278
+ toolingHoleCount,
279
+ recommendedMinimum: MIN_TOOLING_HOLE_COUNT,
280
+ },
281
+ }));
282
+ }
283
+ return issues;
284
+ }
285
+ // ── Production review: polarity/orientation marks ──────────────────────────
286
+ function checkPolarityMarks(input) {
287
+ const polarizedCount = input.polarizedComponentCount ?? 0;
288
+ const markedCount = input.polarityMarkCount ?? 0;
289
+ const missingCount = Math.max(0, polarizedCount - markedCount);
290
+ if (missingCount === 0)
291
+ return [];
292
+ return [
293
+ pcbWarning('PCB_POLARITY_MARK_MISSING', `${missingCount} polarized/orientation-sensitive component${missingCount === 1 ? '' : 's'} lack polarity/orientation marks`, {
294
+ remediationHint: 'Add clear silkscreen/courtyard polarity markers for diodes, LEDs, electrolytic capacitors, IC pin 1, and connectors',
295
+ details: {
296
+ polarizedComponentCount: polarizedCount,
297
+ polarityMarkCount: markedCount,
298
+ missingCount,
299
+ },
300
+ }),
301
+ ];
302
+ }
303
+ // ── Production review: component spacing/courtyard ─────────────────────────
304
+ function checkComponentSpacing(input) {
305
+ const count = input.componentSpacingViolationCount ?? 0;
306
+ if (count <= 0)
307
+ return [];
308
+ return [
309
+ pcbWarning('PCB_COMPONENT_SPACING_VIOLATION', `${count} component spacing/courtyard violation${count === 1 ? '' : 's'} detected`, {
310
+ remediationHint: 'Increase component-to-component spacing or review courtyard overlap for assembly nozzle, hand-solder, and rework access',
311
+ details: { componentSpacingViolationCount: count },
312
+ }),
313
+ ];
314
+ }
315
+ // ── Production review: testpoint coverage ──────────────────────────────────
316
+ function checkTestpointCoverage(input) {
317
+ const criticalNets = [...new Set((input.criticalNetNames ?? []).map(normalizedNetName))];
318
+ if (criticalNets.length === 0)
319
+ return [];
320
+ const testPointNets = new Set((input.testPointNets ?? []).map(normalizedNetName));
321
+ const missing = criticalNets.filter((net) => !testPointNets.has(net));
322
+ const coverage = (criticalNets.length - missing.length) / criticalNets.length;
323
+ if (coverage < MIN_TESTPOINT_COVERAGE_RATIO) {
324
+ return [
325
+ pcbWarning('PCB_TESTPOINT_COVERAGE_LOW', `Critical-net testpoint coverage is ${(coverage * 100).toFixed(0)}%; ${missing.length} critical net(s) are missing test access`, {
326
+ remediationHint: 'Add accessible test pads for power rails, ground, reset, boot, programming, clocks, and key interfaces',
327
+ details: {
328
+ criticalNetCount: criticalNets.length,
329
+ testPointNetCount: testPointNets.size,
330
+ missingCriticalNets: missing,
331
+ coverageRatio: coverage,
332
+ recommendedMinimumRatio: MIN_TESTPOINT_COVERAGE_RATIO,
333
+ },
334
+ }),
335
+ ];
336
+ }
337
+ return [];
338
+ }
339
+ // ── Production review: programming/debug access ────────────────────────────
340
+ function checkProgrammingHeader(input) {
341
+ if (!input.requiresProgrammingHeader || input.hasProgrammingHeader !== false)
342
+ return [];
343
+ return [
344
+ pcbError('PCB_PROGRAMMING_HEADER_MISSING', 'Programming/debug header is required but missing', {
345
+ remediationHint: 'Add a programming/debug connector or documented test pads for SWD/JTAG/UART/BOOT/RESET access',
346
+ details: {
347
+ requiresProgrammingHeader: true,
348
+ hasProgrammingHeader: input.hasProgrammingHeader,
349
+ },
350
+ }),
351
+ ];
352
+ }
353
+ // ── Production review: fabrication notes ───────────────────────────────────
354
+ function checkFabricationNotes(input) {
355
+ if (input.hasFabricationNotes !== false)
356
+ return [];
357
+ return [
358
+ pcbWarning('PCB_FAB_NOTES_MISSING', 'Fabrication notes are missing from the handoff package', {
359
+ remediationHint: 'Add fabrication notes covering board thickness, copper weight, finish, soldermask color, impedance requirements, panelization, and special instructions',
360
+ details: { hasFabricationNotes: input.hasFabricationNotes },
361
+ }),
362
+ ];
363
+ }
158
364
  const RULES = [
159
365
  checkMissingOutline,
160
366
  checkOutlineTooSmall,
@@ -168,12 +374,23 @@ const RULES = [
168
374
  checkMissingManufacturingConstraints,
169
375
  checkFiducials,
170
376
  checkHighVoltageClearance,
377
+ checkDrillFile,
378
+ checkCopperToEdgeClearance,
379
+ checkDrillManufacturability,
380
+ checkSolderMaskSliver,
381
+ checkSilkscreenOverPads,
382
+ checkAssemblyAlignmentFeatures,
383
+ checkPolarityMarks,
384
+ checkComponentSpacing,
385
+ checkTestpointCoverage,
386
+ checkProgrammingHeader,
387
+ checkFabricationNotes,
171
388
  ];
172
389
  // ── Main entry point ───────────────────────────────────────────────────────
173
390
  /**
174
391
  * Run all PCB constraint validation rules against board data.
175
392
  *
176
- * Orchestrates 12 rules:
393
+ * Orchestrates 23 rules:
177
394
  * 1. Missing outline — error if board outline is not defined
178
395
  * 2. Outline too small — warning if dimensions are below 5mm
179
396
  * 3. Missing stackup — warning if no detailed layer stack
@@ -186,6 +403,17 @@ const RULES = [
186
403
  * 10. Missing mfg constraints — warning if process/quantity unspecified
187
404
  * 11. Fiducials recommended — warning if SMT board without fiducials
188
405
  * 12. High-voltage clearance — error if HV domain without proper clearance
406
+ * 13. Drill file — error if NC drill file is missing
407
+ * 14. Copper edge clearance — error if copper is too close to board edge
408
+ * 15. Drill/annular ring — warning if process dimensions are risky
409
+ * 16. Soldermask sliver — warning if soldermask web is too small
410
+ * 17. Silkscreen over pad — warning if silkscreen overlaps exposed pads
411
+ * 18. Tooling holes — warning if assembly tooling holes are missing
412
+ * 19. Polarity marks — warning if orientation-sensitive parts lack marks
413
+ * 20. Component spacing — warning if courtyard/spacing violations exist
414
+ * 21. Testpoint coverage — warning if critical nets lack test access
415
+ * 22. Programming header — error if required debug/programming access is missing
416
+ * 23. Fabrication notes — warning if handoff notes are missing
189
417
  */
190
418
  export function validatePcbConstraints(input) {
191
419
  const errors = [];
@@ -300,6 +528,25 @@ export function fromPcbIntent(pcb) {
300
528
  hasPlacementZones: !!(pcb.placementZones && pcb.placementZones.length > 0),
301
529
  hasFiducials: !!(pcb.fiducials && pcb.fiducials.length > 0),
302
530
  hasTestPads: !!(pcb.testPads && pcb.testPads.length > 0),
531
+ hasDrillFile: pcb.hasDrillFile,
532
+ minCopperToEdgeMm: pcb.minCopperToEdgeMm,
533
+ copperToEdgeViolationCount: pcb.copperToEdgeViolationCount,
534
+ minDrillMm: pcb.minDrillMm,
535
+ minAnnularRingMm: pcb.minAnnularRingMm,
536
+ minSolderMaskSliverMm: pcb.minSolderMaskSliverMm,
537
+ solderMaskSliverViolationCount: pcb.solderMaskSliverViolationCount,
538
+ silkscreenOverPadCount: pcb.silkscreenOverPadCount,
539
+ smtComponentCount: pcb.smtComponentCount,
540
+ fiducialCount: pcb.fiducialCount ?? pcb.fiducials?.length,
541
+ toolingHoleCount: pcb.toolingHoleCount,
542
+ polarizedComponentCount: pcb.polarizedComponentCount,
543
+ polarityMarkCount: pcb.polarityMarkCount,
544
+ componentSpacingViolationCount: pcb.componentSpacingViolationCount,
545
+ criticalNetNames: pcb.criticalNetNames,
546
+ testPointNets: pcb.testPointNets,
547
+ hasProgrammingHeader: pcb.hasProgrammingHeader,
548
+ requiresProgrammingHeader: pcb.requiresProgrammingHeader,
549
+ hasFabricationNotes: pcb.hasFabricationNotes,
303
550
  hasHighVoltage: pcb.highVoltage ?? false,
304
551
  manufacturingProcess: pcb.manufacturingProcess ?? pcb.manufacturing?.process,
305
552
  hasQuantity: !!(pcb.quantity && pcb.quantity > 0) ||