@hybridlabor-api/aos 4.4.2-beta.2 → 4.4.2-beta.4

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.
@@ -47,11 +47,25 @@ const AGENTS = [
47
47
  ];
48
48
 
49
49
  const MODULE_DIRS = ['memB', 'bdb-synapse', 'bdb-os-remote', 'bdb-dev-creator-extension',
50
+ 'bdb-hardware-pcb',
50
51
  'bdb-dev-tool-installer',
51
52
  'bdb-agent-orchestrator', // AO
52
53
  'bdb-os-agent-workspace', // AO's archived predecessor, if still lying around
54
+ 'bdb-dev-optimized-agent-skills', // AOS's own core payload cache, when a "from source" or non-npx run left one behind
53
55
  ].map((m) => h('.agents', m));
54
56
 
57
+ // installer.js's detectInstallState() treats any of these as proof AOS is
58
+ // still installed, independently of the manifest -- a leftover copy after
59
+ // this uninstall runs was reported as "Current & Up-to-date" on the very
60
+ // next launch, with the manifest already gone. Keep this list in exact sync
61
+ // with the `legacyMarkers` array in installer.js's detectInstallState().
62
+ const LEGACY_MARKERS = [
63
+ h('.agents', 'AGENTS.md'),
64
+ h('.gemini', 'config', 'skills', 'startcycle', 'SKILL.md'),
65
+ h('.agents', 'skills', 'startcycle', 'SKILL.md'),
66
+ h('.claude', 'skills', 'startcycle', 'SKILL.md'),
67
+ ];
68
+
55
69
  const sha256 = (file) => {
56
70
  try { return createHash('sha256').update(readFileSync(file)).digest('hex'); }
57
71
  catch { return null; }
@@ -77,13 +91,14 @@ function plan() {
77
91
  .filter((a) => existsSync(a.plist));
78
92
 
79
93
  const modules = MODULE_DIRS.filter(existsSync);
94
+ const legacy = LEGACY_MARKERS.filter(existsSync);
80
95
  const data = PURGE ? DATA_PATHS.filter((d) => existsSync(d.path)) : [];
81
96
 
82
- return { manifest, ours, edited, gone, agents, modules, data };
97
+ return { manifest, ours, edited, gone, agents, modules, legacy, data };
83
98
  }
84
99
 
85
100
  function describe(p) {
86
- const { manifest, ours, edited, gone, agents, modules, data } = p;
101
+ const { manifest, ours, edited, gone, agents, modules, legacy, data } = p;
87
102
 
88
103
  if (!manifest) {
89
104
  console.log('Kein Installations-Manifest unter ' + tilde(MANIFEST) + '.');
@@ -110,6 +125,11 @@ function describe(p) {
110
125
  console.log('\nModule:');
111
126
  for (const m of modules) console.log(` ${tilde(m)}`);
112
127
  }
128
+ if (legacy.length) {
129
+ console.log(`\nInstallations-Marker (${legacy.length}) -- ohne diese hält die nächste`);
130
+ console.log(' Installation AOS für bereits aktuell installiert:');
131
+ for (const l of legacy) console.log(` ${tilde(l)}`);
132
+ }
113
133
 
114
134
  console.log('\nBleibt erhalten:');
115
135
  if (!PURGE) for (const d of DATA_PATHS.filter((d) => existsSync(d.path))) console.log(` ${tilde(d.path).padEnd(18)} ${d.what}`);
@@ -140,7 +160,7 @@ function dirSize(p) {
140
160
 
141
161
  // ------------------------------------------------------------------ execute
142
162
  function execute(p) {
143
- const { ours, edited, agents, modules, data } = p;
163
+ const { ours, edited, agents, modules, legacy, data } = p;
144
164
  const stamp = new Date().toISOString().replace(/[:.]/g, '').slice(0, 15);
145
165
  let removed = 0, backed = 0;
146
166
 
@@ -161,6 +181,9 @@ function execute(p) {
161
181
  for (const m of modules) { try { rmSync(m, { recursive: true, force: true }); } catch { /* in use */ } }
162
182
  if (modules.length) console.log(` ${modules.length} Module entfernt`);
163
183
 
184
+ for (const l of legacy) { try { rmSync(l); } catch { /* already gone */ } }
185
+ if (legacy.length) console.log(` ${legacy.length} Installations-Marker entfernt`);
186
+
164
187
  // Only the BDB hook entries leave settings.json; everything else in it is
165
188
  // the user's and must survive an uninstall exactly as it survives an install.
166
189
  const settings = h('.claude', 'settings.json');
@@ -181,6 +181,16 @@
181
181
  | `triage` | Move issues and external PRs through a state machine of triage roles, categorise, verify, and write agent-ready briefs. |
182
182
  | `typescript-pro` | Master TypeScript with advanced types, generics, and strict type safety. Handles complex type systems, decorators, and enterprise-grade patterns. |
183
183
 
184
+ #### 🔩 Engineering & Hardware
185
+ | Skill Name | Description |
186
+ |------------|-------------|
187
+ | `godmode-hardware-pcb` | Architectural authority for electrical schematics, PCB layout, KiCad projects, and OpenSCAD enclosures — trace geometry, impedance, stackup, and DFM/DRC/ERC sign-off. Use when designing or reviewing physical hardware before it goes to fabrication. |
188
+ | `code-first-hardware-design` | Programmatic schematic capture, circuit synthesis (SKiDL, text netlists, S-expressions), and parametric 3D CAD enclosure co-design (OpenSCAD/BOSL2). Use when generating circuits in code, exporting netlists, scripting KiCad schematics, or designing 3D enclosures. |
189
+ | `pcb-constraint-definition` | Translates high-level hardware requirements into formal engineering constraints, layer stackup calculations, netclasses, and custom DRC rules for KiCad. Use when defining board constraints, stackup, impedance matching, power budgeting, or netclasses. |
190
+ | `pcb-layout-routing-automation` | Floorplanning, component placement rules, high-speed differential pair routing, return path continuity, thermal via arrays, and keepout enforcement for KiCad PCB layouts. Use when placing footprints, routing traces, creating ground planes, or managing thermal/RF keepouts. |
191
+ | `pcb-validation-dfm-signoff` | Automated DRC/ERC verification, SI/PI screening, fab house DFM/DFA compliance, and production release sign-off for KiCad projects. Use when running final design rule checks, auditing manufacturing limits, generating Gerbers/BOM/CPL, or issuing DFM sign-off reports. |
192
+ | `schematic-datasheet-analysis` | Electrical rule auditing, datasheet grounding, pinout validation, power tree tracing, and negative evidence analysis for KiCad schematics. Use when analyzing schematics, auditing pinmux/logic levels, checking component ratings, or validating datasheets. |
193
+
184
194
  #### 📦 Other Utilities
185
195
  | Skill Name | Description |
186
196
  |------------|-------------|
package/installer.js CHANGED
@@ -694,6 +694,32 @@ function flushSessionManifest() {
694
694
  if (_sessionManifest) saveInstallManifest(_sessionManifest);
695
695
  }
696
696
 
697
+ // skills/global_legacy/ has not existed in the shipped payload for a long
698
+ // time -- the three `if (dir === 'global_legacy')` copy branches elsewhere in
699
+ // this file have been dead code ever since, since that name never appears in
700
+ // a fresh fs.readdirSync(skillsBase). Nothing populates a fresh
701
+ // targetLegacyDir any more, but nothing ever removed an OLD one either: a
702
+ // real Windows install this session still had 126 stale legacy skill copies
703
+ // sitting in .codex/skills/legacy, indexed alongside the current top-level
704
+ // copies of the same skills by the harness's own skill picker -- the
705
+ // "duplicate skills" a live test reported. Since the source category is
706
+ // permanently gone, an existing legacy dir is unconditionally obsolete, not
707
+ // merely unmanaged: retire it instead of recreating an eternally-empty
708
+ // placeholder for it.
709
+ function retireObsoleteLegacyDir(targetLegacyDir) {
710
+ if (!targetLegacyDir || !fs.existsSync(targetLegacyDir)) return;
711
+ if (fs.existsSync(path.join(srcDir, 'skills', 'global_legacy'))) {
712
+ fs.mkdirSync(targetLegacyDir, { recursive: true });
713
+ return;
714
+ }
715
+ try {
716
+ fs.rmSync(targetLegacyDir, { recursive: true, force: true });
717
+ log.step(`Removed retired legacy skill copies at ${targetLegacyDir} (global_legacy has not shipped in a long time).`);
718
+ } catch (e) {
719
+ logDebug(e, 'retire legacy dir');
720
+ }
721
+ }
722
+
697
723
  function resolveMcpsArg(availableMcps) {
698
724
  const requested = mcpsArg.split(',').map(s => s.trim()).filter(Boolean);
699
725
  const wantsNone = requested.some(r => ['none', 'core', 'core-only'].includes(r.toLowerCase()));
@@ -900,6 +926,7 @@ function detectInstallState() {
900
926
  // The archived predecessor is deliberately NOT detected: a copy of it
901
927
  // should be removed, not carried forward into another install.
902
928
  { id: 'creator', dir: path.join(basePath, 'bdb-dev-creator-extension') },
929
+ { id: 'hardware', dir: path.join(basePath, 'bdb-hardware-pcb') },
903
930
  { id: 'installer', dir: path.join(basePath, 'bdb-dev-tool-installer') }
904
931
  ];
905
932
 
@@ -910,13 +937,23 @@ function detectInstallState() {
910
937
  }
911
938
 
912
939
  const currentVersion = pkg.version || '3.9.6';
913
- const updateAvailable = isInstalled && (localVersion !== currentVersion);
940
+ // Was a bare !== -- any version string difference counted as "update
941
+ // available", downgrade included. A real Windows session ran `@latest`
942
+ // (resolving to the actual latest stable, 4.4.1) against a machine
943
+ // already on 4.4.2-beta.3 and the installer silently treated dropping two
944
+ // versions the same as a normal update -- no distinction, no warning.
945
+ // isNewerVersion() already exists and already handles prerelease
946
+ // ordering correctly; this just uses it in both directions instead of
947
+ // only for the npm-registry freshness check it was written for.
948
+ const versionChanged = isInstalled && localVersion !== currentVersion;
949
+ const isDowngrade = versionChanged && isNewerVersion(currentVersion, localVersion);
950
+ const updateAvailable = versionChanged && !isDowngrade;
914
951
 
915
952
  // Extend: also load the file-level install manifest so the caller can
916
953
  // seed manifest-aware writes during this same session.
917
954
  const installManifest = loadInstallManifest();
918
955
 
919
- return { isInstalled, localVersion, currentVersion, updateAvailable, installedModules, manifest, installManifest };
956
+ return { isInstalled, localVersion, currentVersion, updateAvailable, isDowngrade, installedModules, manifest, installManifest };
920
957
  }
921
958
 
922
959
  function saveManifest(data = {}) {
@@ -1928,6 +1965,25 @@ async function installCreatorExtension() {
1928
1965
  }
1929
1966
  }
1930
1967
 
1968
+ async function installHardwarePcb() {
1969
+ const hwDir = path.join(moduleBasePath(), 'bdb-hardware-pcb');
1970
+ if (!downloadOrUpdateModule('@hybridlabor-api/bdb-hardware-pcb', hwDir, 'BDB Hardware & PCB (KiCad + OpenSCAD)')) {
1971
+ log.warn('Skipping Hardware & PCB setup: the module could not be downloaded.');
1972
+ return;
1973
+ }
1974
+ if (DRY_RUN) {
1975
+ log.step('[dry-run] would run BDB Hardware & PCB setup');
1976
+ return;
1977
+ }
1978
+ const installerScript = path.join(hwDir, 'installer.js');
1979
+ if (fs.existsSync(installerScript)) {
1980
+ const setupResult = spawnSync('node', [installerScript, '--auto'], { stdio: 'inherit', cwd: hwDir });
1981
+ if (setupResult.status !== 0) {
1982
+ log.warn(`Hardware & PCB setup note: exit code ${setupResult.status}`);
1983
+ }
1984
+ }
1985
+ }
1986
+
1931
1987
  async function installOSRemoteGateway() {
1932
1988
  const remoteDir = path.join(moduleBasePath(), 'bdb-os-remote');
1933
1989
  if (!downloadOrUpdateModule('@hybridlabor-api/bdb-os-remote', remoteDir, 'BDB OS Remote Gateway')) {
@@ -2074,9 +2130,10 @@ function verifyEcosystemInstallation() {
2074
2130
  { name: '3. heimdall-token-saver', pkg: '@hybridlabor-api/heimdall-token-saver', paths: [path.join(moduleBasePath(), 'heimdall-token-saver'), path.join(srcDir, 'vendor', 'token-saver')] },
2075
2131
  { name: '4. AO Agent Orchestrator', pkg: '@hybridlabor-api/bdb-agent-orchestrator', paths: [path.join(moduleBasePath(), 'bdb-agent-orchestrator')] },
2076
2132
  { name: '5. bdb-dev-creator-extension', pkg: '@hybridlabor-api/bdb-dev-creator-extension', paths: [path.join(moduleBasePath(), 'bdb-dev-creator-extension')] },
2077
- { name: '6. bdb-os-remote', pkg: '@hybridlabor-api/bdb-os-remote', paths: [path.join(moduleBasePath(), 'bdb-os-remote')] },
2078
- { name: '7. bdb-dev-tool-installer', pkg: '@hybridlabor-api/bdb-dev-tool-installer', paths: [path.join(moduleBasePath(), 'bdb-dev-tool-installer')] },
2079
- { name: '8. aos (bdb agent os)', pkg: '@hybridlabor-api/aos', paths: [srcDir] }
2133
+ { name: '6. bdb-hardware-pcb', pkg: '@hybridlabor-api/bdb-hardware-pcb', paths: [path.join(moduleBasePath(), 'bdb-hardware-pcb')] },
2134
+ { name: '7. bdb-os-remote', pkg: '@hybridlabor-api/bdb-os-remote', paths: [path.join(moduleBasePath(), 'bdb-os-remote')] },
2135
+ { name: '8. bdb-dev-tool-installer', pkg: '@hybridlabor-api/bdb-dev-tool-installer', paths: [path.join(moduleBasePath(), 'bdb-dev-tool-installer')] },
2136
+ { name: '9. aos (bdb agent os)', pkg: '@hybridlabor-api/aos', paths: [srcDir] }
2080
2137
  ];
2081
2138
 
2082
2139
  for (const mod of modules) {
@@ -3179,6 +3236,7 @@ async function promptOptionalModules(installedModules) {
3179
3236
  ? [{ id: 'ao', name: 'AO Agent Orchestrator (Session telemetry & WebUI)', fn: installOSAgentWorkspace }]
3180
3237
  : []),
3181
3238
  { id: 'creator', name: 'BDB Creator Extension (Generative 3D, Video & ComfyUI)', fn: installCreatorExtension },
3239
+ { id: 'hardware', name: 'BDB Hardware & PCB (KiCad + OpenSCAD Electrical/PCB Design)', fn: installHardwarePcb },
3182
3240
  { id: 'installer', name: 'BDB Dev Tool Installer (Interactive Hub & CLI Launcher)', fn: installDevToolInstaller }
3183
3241
  ];
3184
3242
 
@@ -3405,15 +3463,26 @@ function generateAndOpenLaunchpad() {
3405
3463
 
3406
3464
  const filePath = path.join(os.homedir(), '.agents', 'bdb-launchpad.html');
3407
3465
  fs.writeFileSync(filePath, html, 'utf-8');
3466
+ const warnOpenFailed = (e) => log.warn(`Could not open the dashboard automatically (${e.message}). Open it yourself: ${filePath}`);
3408
3467
  try {
3468
+ let child;
3409
3469
  if (process.platform === 'darwin') {
3410
- spawn('open', [filePath], { detached: true, stdio: 'ignore' }).unref();
3470
+ child = spawn('open', [filePath], { detached: true, stdio: 'ignore' });
3411
3471
  } else if (process.platform === 'win32') {
3412
- spawn('cmd.exe', ['/c', 'start', '""', filePath], { detached: true, stdio: 'ignore' }).unref();
3472
+ // cmd.exe /c start has a title-vs-path quoting quirk when the
3473
+ // arguments come from spawn's argv array rather than a real shell --
3474
+ // on a real Windows test run it silently opened nothing, and the
3475
+ // failure was invisible: spawn() reports a bad launch asynchronously
3476
+ // via 'error', not by throwing, so the try/catch around this call
3477
+ // could never have caught it regardless of --verbose. explorer.exe
3478
+ // on a plain file path has no title argument to misparse.
3479
+ child = spawn('explorer', [filePath], { detached: true, stdio: 'ignore' });
3413
3480
  } else {
3414
- spawn('xdg-open', [filePath], { detached: true, stdio: 'ignore' }).unref();
3481
+ child = spawn('xdg-open', [filePath], { detached: true, stdio: 'ignore' });
3415
3482
  }
3416
- } catch (e) { logDebug(e, 'launchpad open'); }
3483
+ child.on('error', warnOpenFailed);
3484
+ child.unref();
3485
+ } catch (e) { warnOpenFailed(e); }
3417
3486
  }
3418
3487
 
3419
3488
  async function universalHarnessSync(primaryMcpConfigPath) {
@@ -3509,7 +3578,7 @@ async function runQuickUpdate(installState) {
3509
3578
 
3510
3579
  fs.mkdirSync(backupDir, { recursive: true });
3511
3580
  fs.mkdirSync(paths.targetSkillDir, { recursive: true });
3512
- fs.mkdirSync(paths.targetLegacyDir, { recursive: true });
3581
+ retireObsoleteLegacyDir(paths.targetLegacyDir);
3513
3582
  fs.mkdirSync(paths.targetWorkspaceDir, { recursive: true });
3514
3583
 
3515
3584
  // Initialize manifest for this update session.
@@ -3582,6 +3651,7 @@ async function runQuickUpdate(installState) {
3582
3651
  // 'ao' intentionally skipped here even for
3583
3652
  // existing installs -- see promptOptionalModules() for why.
3584
3653
  else if (subId === 'creator') await installCreatorExtension();
3654
+ else if (subId === 'hardware') await installHardwarePcb();
3585
3655
  else if (subId === 'installer') await installDevToolInstaller();
3586
3656
  }
3587
3657
 
@@ -3934,7 +4004,7 @@ async function main() {
3934
4004
 
3935
4005
  installStep(`create the skill target directories (${t.value})`, () => {
3936
4006
  fs.mkdirSync(t.targetSkillDir, { recursive: true });
3937
- fs.mkdirSync(t.targetLegacyDir, { recursive: true });
4007
+ retireObsoleteLegacyDir(t.targetLegacyDir);
3938
4008
  fs.mkdirSync(t.targetWorkspaceDir, { recursive: true });
3939
4009
  }, 'The skill copies below will most likely be skipped as well.');
3940
4010
 
package/lib/startup-ui.js CHANGED
@@ -83,7 +83,13 @@ function buildTelemetryCard({ installState, detections, daemonStatus = [] }) {
83
83
  'Google Antigravity': 'Antigravity',
84
84
  'ChatGPT Codex CLI': 'Codex CLI',
85
85
  'Claude Code CLI': 'Claude Code',
86
- 'Roo Code / Cline / VS Code': 'Roo / Cline',
86
+ // The underlying detection (installer.js's 'vscode' evidence) fires on
87
+ // any of: the VS Code app/CLI being present, OR a Roo/Cline extension
88
+ // glob match. Those are not the same claim -- plain VS Code with no
89
+ // Roo/Cline extension installed used to display as "Roo / Cline" here,
90
+ // asserting something that was not there. "VS Code" is the one label
91
+ // that is true in all three trigger cases.
92
+ 'Roo Code / Cline / VS Code': 'VS Code',
87
93
  'Aider CLI': 'Aider',
88
94
  'OpenCode CLI': 'OpenCode',
89
95
  'Cursor / Generic IDE': 'Cursor',
@@ -116,7 +122,9 @@ function buildTelemetryCard({ installState, detections, daemonStatus = [] }) {
116
122
  // 3. Installation State
117
123
  if (installState) {
118
124
  if (installState.isInstalled) {
119
- const versionStatus = installState.updateAvailable
125
+ const versionStatus = installState.isDowngrade
126
+ ? `${BRAND.amber}⚠${BRAND.reset} ${BRAND.white}v${installState.localVersion}${BRAND.reset} installed ${BRAND.amber}(this payload is v${installState.currentVersion} — older, not an update)${BRAND.reset}`
127
+ : installState.updateAvailable
120
128
  ? `${BRAND.amber}v${installState.localVersion}${BRAND.reset} ➔ ${BRAND.emerald}${BRAND.bold}v${installState.currentVersion}${BRAND.reset} ${BRAND.amber}(Update Available)${BRAND.reset}`
121
129
  : `${BRAND.emerald}✔${BRAND.reset} ${BRAND.white}v${installState.currentVersion}${BRAND.reset} ${BRAND.dim}(Current & Up-to-date)${BRAND.reset}`;
122
130
  lines.push(`${G} ${BRAND.lila}└─${BRAND.reset} ${BRAND.dim}Kernel State:${BRAND.reset} ${versionStatus}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hybridlabor-api/aos",
3
- "version": "4.4.2-beta.2",
3
+ "version": "4.4.2-beta.4",
4
4
  "description": "AOS — A Curated AI AGENT OS. Optimized agent skills and add-ons like memB, OpenWiki, Heimdall Token Saver, and Godmode architectures.",
5
5
  "main": "installer.js",
6
6
  "bin": {
@@ -13,7 +13,7 @@ const SKILLS = join(REPO, 'skills');
13
13
 
14
14
  const CATEGORIES = new Set([
15
15
  'design-ui-ux', 'engineering-method', 'media-eventtech',
16
- 'bdb-core', 'saas-ops', 'library',
16
+ 'bdb-core', 'saas-ops', 'library', 'engineering-hardware',
17
17
  ]);
18
18
  const REQUIRED = ['name', 'description', 'category'];
19
19
 
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: godmode-hardware-pcb
3
+ description: "Use when designing electrical schematics, PCB layouts, KiCad projects, or OpenSCAD enclosures — trace geometry, impedance, stackup, DFM/DRC/ERC sign-off, and hardware-software co-design boundaries."
4
+ category: engineering-hardware
5
+ ---
6
+
7
+ # ⚡ BDB Hardware & PCB Godmode
8
+
9
+ This skill is the architectural authority for **Electrical Schematics, PCB Layout, Physical Constraints, and Manufacturing Sign-Off** in the AOS hardware engineering pipeline. It defines how agents must derive physical parameters from first principles, validate designs against IPC standards, and gate release to fabrication — without compromising on measurable, machine-checkable evidence.
10
+
11
+ ---
12
+
13
+ ## 1. Role & Architectural Boundaries
14
+
15
+ * **Physical Design Authority:** Governs schematic capture, board layout, layer stackup, netclass definition, controlled-impedance routing, and DFM/DFA sign-off across KiCad-driven projects. Also governs parametric enclosure and mechanical co-design via OpenSCAD.
16
+ * **Peer Integration:** Operates alongside `godmode-engineering` (firmware and software running on the board) and `godmode-eventtech` (live show-control hardware in the field) — this skill owns the board and enclosure itself, not the code that runs on it or the show that uses it. A request that touches firmware register maps or a live show's signal budget hands off to those peers instead of being re-derived here.
17
+ * **No Informal Constraints:** A schematic or layout may never proceed on default trace widths, unconstrained nets, or "it looked fine in the 3D viewer." Every physical parameter traces back to a formula or a standard, not a guess.
18
+
19
+ ---
20
+
21
+ ## 2. Mathematical and Physical Foundations
22
+
23
+ All trace geometry and thermal boundaries are derived from first principles and IPC standards, never estimated by eye:
24
+
25
+ * **Trace Current Capacity (IPC-2152):** $I = k \cdot \Delta T^{0.44} \cdot A^{0.725}$, with $k = 0.048$ for external (convective) traces and $k = 0.024$ for internal (conductive-only) traces. A $2.5\text{A}$ DC rail at $\Delta T = 10^\circ\text{C}$ on $1\text{ oz}$ external copper needs $\approx 0.79\text{mm}$ trace width — not a rounded-up guess.
26
+ * **Controlled Impedance (IPC-2141):** Single-ended microstrip $Z_0 = \frac{87}{\sqrt{\varepsilon_r + 1.41}} \ln\left(\frac{5.98h}{0.8w+t}\right)$; edge-coupled differential $Z_{diff} \approx 2Z_0\left(1 - 0.48e^{-0.96 s/h}\right)$. USB is a $90\Omega$ differential target, Ethernet/PCIe is $100\Omega$ — these are not interchangeable, and getting the pair spacing wrong by a fraction of $h$ misses the target by more than manufacturing tolerance forgives.
27
+ * **DC IR Drop:** $R_{trace} = \rho \cdot L / (w \cdot t)$, $V_{drop} = I_{peak} \cdot R_{trace}$. On a $+3.3\text{V}$ rail, $V_{drop}$ must stay $\le 0.10\text{V}$ ($3\%$); if it doesn't, widen the trace or move the net to a copper flood — don't just note the number and move on.
28
+ * **Crosstalk (the 3W rule):** center-to-center separation $D \ge 3w$ for parallel traces longer than $15\text{mm}$ keeps mutual coupling below a $70\%$ reduction threshold. This is the default spacing assumption for any signal or clock line, not an optional refinement.
29
+
30
+ ---
31
+
32
+ ## 3. Headless Validation — the Unforgiving Gate
33
+
34
+ **No board may be released to fabrication on visual inspection or LLM self-attestation.** This is not a style preference; it's the same lesson this whole ecosystem has paid for repeatedly elsewhere: a clean-looking run is not evidence, a real exit code is.
35
+
36
+ * **ERC:** `kicad-cli sch erc --exit-code-violations -o reports/erc_report.txt project.kicad_sch` — exit 0 means zero errors and zero unhandled warnings, not "looks connected."
37
+ * **DRC:** `kicad-cli pcb drc --exit-code-violations --format json -o reports/drc_report.json board.kicad_pcb` — must show zero unrouted nets, zero clearance violations, zero broken annular rings, zero thermal spoke disconnections.
38
+ * **DFM/DFA:** benchmark the layout against the target fab house's real capabilities (e.g. JLCPCB standard, PCBWay 4-layer minimum trace/space/via), not a generic "should be fine" assumption.
39
+ * A non-zero exit code from any of the above is an immediate gate blockage — the pipeline halts, it does not continue with a caveat noted for later.
40
+
41
+ ---
42
+
43
+ ## 4. Dedicated MCP Tool Validation Requirement
44
+
45
+ Before issuing schematic edits, layout changes, or fabrication exports, the agent MUST validate the required MCP servers are actually reachable — not assume they are because a config file lists them:
46
+
47
+ 1. **KiCad MCP:** validate the kicad-mcp-server responds over stdio (`tools/list` returns its full tool set — schematic editing, PCB layout, ERC/DRC execution, Gerber/BOM/CPL export) before issuing any node or netlist command.
48
+ 2. **OpenSCAD MCP:** validate the openscad-mcp-server responds before requesting parametric model generation, modification, or STL/3MF export for enclosure co-design.
49
+ 3. **Version awareness:** a stale or absent `kicad-cli` (KiCad 8+) or `openscad` binary on the host changes what's actually possible — check for it and say so plainly, rather than emitting commands that will fail downstream with no clear cause.
50
+
51
+ ---
52
+
53
+ ## 5. Hardware/Software Co-Design Boundary
54
+
55
+ * PCB and enclosure design decisions here must stay coordinated with, but not encroach on, the firmware/software skills that consume the resulting pinout and register map — a GPIO reassignment on the board is a breaking change to firmware that already assumed the old pin, and must be flagged as such, not silently absorbed.
56
+ * Mechanical (OpenSCAD) and electrical (KiCad) constraints are two halves of the same physical object: a connector placement that satisfies routing but collides with the enclosure wall is not a valid design, even if ERC/DRC both pass.
57
+
58
+ ---
59
+
60
+ ## Universal Agent Harness Integration
61
+
62
+ This Godmode rulebook is universally available across the BDB ecosystem, installed alongside the `@hybridlabor-api/bdb-hardware-pcb` module (KiCad + OpenSCAD MCP servers, 5 companion skills under the `engineering-hardware` category):
63
+ * **Claude Code / CLI Agents:** loaded during electrical, PCB, and enclosure design sessions.
64
+ * **Peer skills:** `code-first-hardware-design`, `pcb-constraint-definition`, `pcb-layout-routing-automation`, `pcb-validation-dfm-signoff`, `schematic-datasheet-analysis`.
65
+
66
+ ## Overview
67
+ This skill acts as the architectural authority for electrical and PCB design, enforcing IPC-standard physical constraints, headless validation gates, and hardware/software co-design boundaries.
68
+
69
+ ## When to Use
70
+ - **Trigger:** The user is designing or modifying a schematic, PCB layout, layer stackup, netclass, or an OpenSCAD enclosure meant to house the board.
71
+ - **Exclude:** Do not use for firmware/register-level software running on the board (hand off to `godmode-engineering`), or for live show-control signal routing in the field (hand off to `godmode-eventtech`).
72
+
73
+ ## Core Process
74
+ 1. Derive every trace width, impedance target, and clearance from the formulas above or a cited IPC standard — never from a rounded-up guess.
75
+ 2. Validate KiCad and OpenSCAD MCP servers are actually responsive before issuing edits.
76
+ 3. Run headless ERC and DRC with `--exit-code-violations`; treat any non-zero exit as a hard stop.
77
+ 4. Benchmark the layout against the real target fab house's DFM limits before calling a design release-ready.
78
+ 5. Cross-check the enclosure (OpenSCAD) against the board outline and connector placements (KiCad) before sign-off.
79
+
80
+ ## Common Rationalizations
81
+
82
+ | Rationalization | Reality |
83
+ |---|---|
84
+ | "0.25mm default trace width is fine for a power rail." | IPC-2152 current-capacity math, not the CAD tool's default, sets minimum trace width — a 2.5A rail needs ~0.79mm at 1oz copper, not the default. |
85
+ | "The 3D viewer looks correct, so the board is done." | A visual check is not ERC/DRC. Only a headless run with `--exit-code-violations` and a real exit code is evidence. |
86
+ | "USB and Ethernet differential pairs can use the same spacing." | USB targets 90Ω differential, Ethernet/PCIe targets 100Ω — same formula, different required spacing for the same dielectric height. |
87
+ | "The GPIO can be reassigned in layout; firmware can just adapt." | A pin reassignment is a breaking change to any firmware that already assumed the old pinout — it must be flagged to the software side, not silently absorbed. |
88
+
89
+ ## Red Flags
90
+
91
+ - Proceeding to layout with unconstrained nets or no defined netclasses.
92
+ - Treating a clean-looking 3D render as equivalent to a passing ERC/DRC exit code.
93
+ - Skipping DFM benchmarking against the actual target fab house's capabilities.
94
+ - Changing a pinout or connector placement without flagging the firmware or enclosure impact.
95
+
96
+ ## Verification
97
+
98
+ - [ ] Every load-bearing trace width/impedance/clearance traces back to a formula or IPC standard, not a default or a guess.
99
+ - [ ] ERC and DRC were run headless with `--exit-code-violations`, and the actual exit code — not a description of the run — was checked.
100
+ - [ ] KiCad and OpenSCAD MCP tool availability was validated before issuing edits.
101
+ - [ ] DFM limits were checked against the real target fab house, not a generic assumption.
102
+ - [ ] Any pinout/connector change was cross-checked against firmware assumptions and enclosure geometry.
@@ -84,6 +84,7 @@ If the user's intent matches one of these, jump to the corresponding section:
84
84
  * **"I'm managing the BDB SaaS multi-cloud fleet"** ➔ [BDB Ecosystem & SaaS Ops](#bdb-ecosystem--saas-ops)
85
85
  * **"I need Three.js, 3D, or motion work"** ➔ [Media & EventTech](#media--eventtech)
86
86
  * **"I need live event tech, TouchDesigner, or Resolume help"** ➔ [Media & EventTech](#media--eventtech)
87
+ * **"I need PCB layout, schematic capture, or electrical/hardware design"** ➔ [Electrical & Hardware Design](#electrical--hardware-design)
87
88
 
88
89
  ---
89
90
 
@@ -304,6 +305,23 @@ Resolve and Premiere are interchangeable at this level: pick whichever is actual
304
305
 
305
306
  ---
306
307
 
308
+ ## 🔩 Electrical & Hardware Design
309
+
310
+ Use these for schematic capture, PCB layout, and physical hardware design (KiCad, OpenSCAD).
311
+
312
+ * **Top Picks:** `godmode-hardware-pcb`, `code-first-hardware-design`, `pcb-constraint-definition`
313
+
314
+ * **Architectural authority**: `godmode-hardware-pcb` — enforces IPC-standard trace/impedance/stackup math and the headless ERC/DRC/DFM sign-off gate across the skills below; load it before starting schematic or layout work.
315
+ * **Circuit synthesis & enclosures**: `code-first-hardware-design` — programmatic schematic capture (SKiDL, netlists, S-expressions) and parametric 3D CAD enclosure co-design (OpenSCAD/BOSL2).
316
+ * **Constraints & stackup**: `pcb-constraint-definition` — translates hardware requirements into layer stackup, netclasses, impedance matching, and custom DRC rules before layout starts.
317
+ * **Layout & routing**: `pcb-layout-routing-automation` — floorplanning, component placement, high-speed differential pair routing, thermal via arrays, and keepout enforcement.
318
+ * **Validation & sign-off**: `pcb-validation-dfm-signoff` — automated DRC/ERC, SI/PI screening, fab house DFM/DFA compliance, and production release sign-off (Gerbers/BOM/CPL).
319
+ * **Schematic & datasheet auditing**: `schematic-datasheet-analysis` — electrical rule auditing, datasheet grounding, pinout validation, and power tree tracing.
320
+
321
+ These ship in the separate `@hybridlabor-api/bdb-hardware-pcb` power-up module (KiCad + OpenSCAD MCP servers), not in this base skills package — install it as an optional AOS module when hardware work comes up.
322
+
323
+ ---
324
+
307
325
  ## 🛡️ Overlap: The Supreme Godmodes
308
326
 
309
327
  When do you use a `godmode-*` skill versus a narrower skill?