Before writing code, read ARCHITECTURE.md to understand the codebase map and data flow.
- The 2-Strike Rule: If a test, build step, command, or script fails more than TWICE with the exact same error, STOP EDITING IMMEDIATELY. Explain the error and explicitly ask for guidance. Do not attempt a third fix.
- Zero Hallucination Policy: Do not hallucinate functions, variables, missing files, or imports. If context is missing, STOP and ask. Never assume the structure of unread files.
- Verify Before Writing: Before modifying any file, you MUST use tools (e.g.,
readorgrep) to confirm the target lines and logic actually exist. Do not blindly patch based on outdated context. - Output Restrictions: No sycophantic openers or closing fluff. No em dashes, smart quotes, or Unicode. ASCII only. Be concise. If unsure, say so. Never guess.
- Git Restrictions: Never push to a remote, force-push, or use
--no-verifyunless explicitly commanded. Always confirm before destructive operations (e.g.,reset --hard,branch -D).
- Pre-Flight: Read all relevant files and
CONTRIBUTING.mdbefore writing anything. Understand the full requirement. - Scope Control: Only make changes directly requested. Do not refactor, add features, reformat, or add comments/types to code outside the scope of the prompt.
- Testing Mandate: Test after writing. Never leave code untested. Fix errors before moving on. Never declare a task "done" without a passing test.
- Resilient Architect Principle: Ensure state integrity if a failure occurs mid-execution. Document the failure point, fallback, and logging strategy for stateful/I/O-bound logic.
- Process Boundaries: Electron desktop app with three boundaries:
src/main/(Node.js, SQLite, BLE, MQTT),src/preload/(Context bridge), andsrc/renderer/(React 19, Vite, Zustand). - Dual-Protocol: Support both
meshtasticandmeshcoreprotocols. Gate UI features viaProtocolCapabilities. UseuseRadioProvider(protocol)rather than string comparisons. - Diagnostics Integration: When modifying networking/routing logic, ensure compatibility with the Diagnostics panel and routing anomaly detection mechanisms (e.g., Hop Goblins, Hidden Terminals).
- Package Management: Strictly use
pnpmfor all operations to maintain our established launch speed benchmarks. - Technology Bans: Do not introduce, suggest, or integrate any cryptocurrency-based technologies, dependencies, or services under any circumstances.
- Formatting: Prettier strictly enforced. Semi: always, Quotes: single, Trailing commas: all, Print width: 100, Tab width: 2 spaces, End of line: LF.
- TypeScript: Strict mode enabled. Avoid
any; useunknownwith type guards. Export types explicitly and prefer interfaces over type aliases. - React: Functional components only.
react-hooks/exhaustive-depsis an error-level rule. Use optional chaining (?.) for nullable values in JSX. Every interactive element requires anaria-label. - State (Zustand): Define default values outside components at the module level for stable reference equality. Avoid subscribing to entire Maps if only a single ID is needed.
- Magic Numbers: Extract time constants to
src/renderer/lib/timeConstants.ts(e.g.,MS_PER_SECOND). Define domain-specific thresholds at the module level. - Performance: Avoid O(n) operations in hot paths. Extract lazy cleanup logic into private methods triggered only when a collection exceeds a threshold.
- Error Handling: Never swallow errors. Every catch must log, rethrow, or have
// catch-no-log-ok <reason>. Prefer Result Types over deep try/catch nesting. - Logging: Use
console.debugfor diagnostics,warnfor recoverable,errorbefore rethrow. No bareconsole.log. - Log Injection Validation: Sanitize user-controlled data with
sanitizeLogMessage()at the call site before passing toappendLine()or any logger. - IPC Safety: Never expose
ipcRendererdirectly. Use namespaced channels (e.g.,db:*). Expose minimal API surface viacontextBridge. - System Boundaries: Banned
exec/execSync(use spawn APIs). NodangerouslySetInnerHTMLoreval(). Validate inputs at system boundaries but do not over-validate internal code.
- Environments: Renderer tests use jsdom (
src/renderer/**/*.test.{ts,tsx}). Main tests use node (src/main/**/*.test.ts). - Console Mocking: When testing code that logs errors, mock the console method before spying to prevent stderr noise (e.g.,
vi.spyOn(console, 'warn').mockImplementation(() => {});). UsebeforeEachfor shared setup. - Contract Tests: Update
src/main/index.contract.test.tswhen changing CSP, build config, IPC limits, or log filters.
- Development:
pnpm run dev(dev mode),pnpm run build(production build),pnpm start(build & start). - Quality:
pnpm run lint(ESLint),pnpm run format(Prettier),pnpm run typecheck,pnpm run lint:md. - Validation:
pnpm run check:log-injection,pnpm run check:db-migrations,pnpm run check:ipc-contract,pnpm run check:licenses. - Testing:
pnpm test(watch),pnpm run test:run(CI mode). Example single file:pnpm dlx vitest run src/main/database.test.ts.
- Commits: Use Conventional Commits (
feat:,fix:,docs:,chore:,refactor:,test:). - Remote Tracking: Ensure operations are tracked against the correct remote (
Colorado-Mesh/meshtastic-client). - Pre-PR Sweep: Update
README.md, bump versions if warranted, and group metadata changes into a single commit before opening a PR. - PR Descriptions: When executing
gh pr create, the description MUST include details for all commits in the branch (git log origin/main..HEAD --oneline), not just the most recent one.
- Engines:
lib/diagnostics/—RoutingDiagnosticEngine.ts(hop anomalies),RFDiagnosticEngine.ts(RF),RemediationEngine.ts(fixes). - Store:
diagnosticsStore.tsholds routing rows, RF rows, foreign LoRa, MQTT ignore, packet redundancy. - Adding a finding: Extend
DiagnosticRowintypes.ts, add detector in engine, add to store viareplaceRoutingRowsFromMaporreplaceRfRowsForNode. - TTL: Routing 24h, RF 1h — configure in
diagnosticRows.ts. - Debug: Add
console.debugin detector. Check store in DevTools. Routing requires GPS on both nodes.
- Reproduce: Developer runs
pnpm startand exercises the failing path. Report findings to AI. - Locate: Search error text in
main/*.tsorrenderer/**/*.tsx. - Log: Add
console.debugonly if needed to trace execution flow. - Fix: Apply minimum change needed.
- Test: Add test in same directory (
*.test.tsor*.test.tsx). - Verify: Run
pnpm dlx vitest run <test-file>andpnpm run lint.
- Connection bugs:
useDevice.ts/useMeshCore.ts. - UI state bugs: matching store in
stores/. - IPC failures:
index.ts.
- Meshtastic:
useDevice.ts+createConnection()inconnection.ts. - MeshCore:
useMeshCore.ts+MeshCoreConnectionfrom@liamcottle/meshcore.js. - Capability gating: Use
useRadioProvider(protocol)fromproviderFactory.ts. ReturnsProtocolCapabilities. - Never: Compare
protocol === 'meshcore'strings.
- Schema: SQLite with WAL mode. Version in
db.pragma('user_version'). - Migration: Add
migration_N()indatabase.ts, increment version. - API: Use
db-compat.ts(wrapsnode:sqlite). Neverbetter-sqlite3. - Test: Run
pnpm run check:db-migrationsafter changes.
- Meshtastic BLE:
connection.ts→createBleConnection()→TransportManager. - MeshCore BLE: macOS/Windows:
noble-ble-manager.ts. Linux: Web Bluetooth viaTransportWebBluetoothIpc. - Serial:
createSerialConnection()inconnection.ts. Port identity:serialPortSignature.ts. - Errors:
humanizeBleError,humanizeSerialError,humanizeHttpErrorinconnection.ts. - Reconnect: Watchdog in
useDevice.ts(BLE_STALE_THRESHOLD_MS, BLE_DEAD_THRESHOLD_MS).
- Meshtastic:
mqtt-manager.ts. Handles AES decryption, protobuf decode, dedup (10-min window inseenPacketIds). - MeshCore:
meshcore-mqtt-adapter.ts. JSON v1 envelope format. - Debug: Tagged logs
[MQTT]in mqtt-manager.ts. CheckMQTTStatusin UI.
- Location:
components/*.tsx. Tests co-located as*.test.tsx. - State: Use Zustand stores in
stores/. Define defaults at module level. - Rules: Every interactive element needs
aria-label. Functional components only. NodangerouslySetInnerHTML. - Adding a panel: Add to
lazyTabPanels.tsorlazyAppPanels.ts. Gate viaProtocolCapabilities.
- Types: Define in
electron-api.types.ts. - Handler: Add in
index.tswith namespaced channel (db:*,mqtt:*,meshcore:*, etc.). - Expose: Add to
preload/index.tsviacontextBridge.exposeInMainWorld. - Contract test: Update
index.contract.test.tsif changing CSP, IPC limits, or log filters. - Never: Expose
ipcRendererdirectly.
- Location:
stores/*.ts. - Pattern:
create<Name>((set, get) => ({ ... })). Define defaults at module level. - Persistence: Use
persistmiddleware for localStorage. For SQLite, call IPC from an effect. - Subscriptions: Prefer selecting specific fields (
useStore(s => s.field)) over entire store.
| Issue | Where to look |
|---|---|
| Connection fails | useDevice.ts / useMeshCore.ts |
| Messages not sending | useDevice.sendText / useMeshCore... |
| UI not updating | Zustand store, React useEffect dependency |
| BLE timeout | noble-ble-manager.ts, bleConnectErrors |
| Serial port not found | serialPortSignature.ts |
| MQTT reconnect loop | mqtt-manager.ts |
| Database error | database.ts migrations |
| Log panel missing data | log-service.ts, check [TAG] prefixes |