Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# CodeRabbit Free-tier tuning for mesh-client.
# Docs: https://docs.coderabbit.ai/getting-started/yaml-configuration
# Autofix requires Pro — use “Prompt for AI Agents” on Free.

language: en-US
reviews:
profile: quiet
request_changes_workflow: false
high_level_summary: true
poem: false
review_status: true
enable_prompt_for_ai_agents: true
path_filters:
- '!src/renderer/locales/**'
- '!**/pnpm-lock.yaml'
- '!flatpak/generated-sources.json'
- '!patches/**'
- '!reticulum-sidecar/patches/**'
- '!**/coverage/**'
- '!**/dist/**'
- '!**/dist-electron/**'
- '!**/target/**'
path_instructions:
- path: '**/*'
instructions: |
Focus on correctness, security, races, resource leaks, and IPC/contract bugs.
Skip style/formatting (Prettier/ESLint/Clippy own that).
Do not request cognitive-complexity or Sonar-style refactors.
Prefer minimal diffs; do not suggest drive-by cleanups outside the PR scope.
This repo follows AGENTS.md multi-protocol and i18n rules.
- path: 'src/renderer/locales/**'
instructions: Skip — generated/translated locale JSON.
- path: '**/*.test.ts'
instructions: Prefer behavioral assertions; skip style-only test nits.
- path: '**/*.test.tsx'
instructions: Prefer behavioral/axe assertions; skip style-only test nits.
auto_review:
enabled: true
drafts: false
auto_pause_after_reviewed_commits: 2
ignore_title_keywords:
- 'chore: bump'
- 'chore(deps)'
- 'dependabot'
7 changes: 7 additions & 0 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ else
fi

pnpm run typecheck || fail 'pnpm run typecheck'

if staged_match '^(src/shared/|tsconfig\.strict\.json)'; then
pnpm run typecheck:strict-shared || fail 'pnpm run typecheck:strict-shared'
else
printf 'pre-commit: skip typecheck:strict-shared (no src/shared or tsconfig.strict.json staged)\n' >&2
fi

pnpm run check:electron-security || fail 'pnpm run check:electron-security'

if staged_match '^(flatpak/|org\.coloradomesh\.MeshClient\.yml|package\.json|scripts/check-flatpak\.mjs|scripts/sync-flatpak-electron)'; then
Expand Down
24 changes: 24 additions & 0 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# Branch-scoped Vitest before push: tests related to files changed since merge-base with origin/main.
# Full suite remains `pnpm run test:run` / `pnpm run check:pr`. Skip with git push --no-verify.

REPO_ROOT=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)

fail() {
printf 'pre-push: FAILED — %s\n' "$1" >&2
exit 1
}

if ! git rev-parse --verify origin/main > /dev/null 2>&1; then
printf 'pre-push: skip Vitest --changed (origin/main not available; fetch or run pnpm run check:pr)\n' >&2
exit 0
fi

MERGE_BASE=$(git merge-base HEAD origin/main) || fail 'git merge-base HEAD origin/main'
printf 'pre-push: vitest run --changed %s (merge-base with origin/main)\n' "$MERGE_BASE" >&2

cd "$REPO_ROOT" || fail 'cd repo root'
pnpm exec vitest run --changed "$MERGE_BASE" || fail 'vitest run --changed (pre-push)'

printf 'pre-push: OK\n' >&2
exit 0
2 changes: 1 addition & 1 deletion .github/workflows/flatpak.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
name: Reticulum sidecar (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
env:
RS_RETICULUM_REF: 6d2b28475321bc15c8f60796513d8878b47ed3ab
RS_RETICULUM_REF: 9928abed269a83ec5a7ef165ff1142d938cad706
permissions:
contents: read
strategy:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/reticulum-sidecar.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ jobs:

build-rns-stack:
env:
RS_RETICULUM_REF: 6d2b28475321bc15c8f60796513d8878b47ed3ab
RS_RETICULUM_REF: 9928abed269a83ec5a7ef165ff1142d938cad706
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -167,7 +167,7 @@ jobs:

build-windows-arm64-rns-stack:
env:
RS_RETICULUM_REF: 6d2b28475321bc15c8f60796513d8878b47ed3ab
RS_RETICULUM_REF: 9928abed269a83ec5a7ef165ff1142d938cad706
permissions:
contents: read
runs-on: windows-latest
Expand Down
12 changes: 0 additions & 12 deletions .sonarcloud.properties

This file was deleted.

16 changes: 10 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This file is self-contained. ARCHITECTURE.md and CONTRIBUTING.md are human refer
- **Credits ↔ package.json:** When adding or renaming a person under **Authors** or **Contributors** in [`docs/credits.md`](docs/credits.md), also add/update the matching entry in root `package.json` `contributors` (same order as credits). Format: `"DisplayName https://github.com/handle"` when a GitHub URL exists, otherwise the credits display name/callsign only (e.g. `"megabear - KD5IHC"`). Do **not** put Colorado Mesh org thanks, Acknowledgements projects, or dependency/binary attribution tables into `contributors`.
- **Testing:** Ship a passing test for behavioral changes; do not call the task done without it.
- **Stateful/I/O code:** Preserve integrity on failure; document failure point, fallback, and logging where it matters.
- **Pre-commit patience:** Pre-commit runs staged-related Vitest (`pnpm run test:staged`), staged ESLint, full typecheck, and path-gated `check:*` scripts. Typical small commits are much faster than a full suite; vitest infra / lockfile changes still force a full Vitest run. Be patient — do not interrupt or force-skip. **PR CI** ([`tests.yaml`](.github/workflows/tests.yaml)) always runs the **full** Vitest suite (`pnpm run test:run`) — never `test:staged` / `test:changed` / `vitest related`. i18n is gated via `locale-quality.test.ts` (subprocess of `check:i18n`). **`pnpm run release`** (`scripts/release.sh`) runs full Vitest **plus ungated `check:*` scanners** (including a direct `check:i18n`). Green pre-commit ≠ green CI or release.
- **Pre-commit patience:** Pre-commit runs staged-related Vitest (`pnpm run test:staged`), staged ESLint, full typecheck, path-gated `typecheck:strict-shared` when `src/shared/` is staged, and path-gated `check:*` scripts. Typical small commits are much faster than a full suite; vitest infra / lockfile changes still force a full Vitest run. Be patient — do not interrupt or force-skip. **Pre-push** runs `vitest run --changed` against the merge-base with `origin/main` (branch-scoped; skip with `--no-verify`). **PR CI** ([`tests.yaml`](.github/workflows/tests.yaml)) always runs the **full** Vitest suite (`pnpm run test:run`) — never `test:staged` / `test:changed` / `vitest related`. i18n is gated via `locale-quality.test.ts` (subprocess of `check:i18n`). **`pnpm run check:pr`** (hand, before opening/updating a PR) runs full lint + typecheck + `typecheck:strict-shared` + `test:run` (+ full-feature sidecar check when the branch touches sidecar). **`pnpm run release`** (`scripts/release.sh`) runs full Vitest **plus ungated `check:*` scanners** (including a direct `check:i18n`). Green pre-commit ≠ green CI or release.
- **Fresh clone:** Before other setup, run `node scripts/check-environment.mjs` (works before pnpm is installed). After `pnpm install`, re-run `pnpm run check:environment`. Fix required failures using printed hints and `setup:*` scripts; optional warnings can wait. Wrong/outdated pnpm is blocked by `scripts/check-package-manager.mjs` on `preinstall` and `pnpm run dev` (prints Corepack/`npm install -g pnpm@…` steps; Node 25+ needs Corepack installed separately).

### Platform parity
Expand Down Expand Up @@ -89,7 +89,7 @@ Adding a cross-boundary feature:
## 5. Testing

- Renderer: jsdom (`src/renderer/**/*.test.{ts,tsx}`). Main: node (`src/main/**/*.test.ts`).
- **Reticulum sidecar (Rust):** Clippy + rustfmt via `pnpm run check:reticulum-sidecar` (stub build in pre-commit when `cargo` is on `PATH` **and** sidecar-related paths are staged) and full-feature lint in `reticulum-sidecar.yaml`. Coverage threshold (`cargo llvm-cov --fail-under-lines`) is enforced only in `tests.yaml` when sidecar paths change — not in pre-commit.
- **Reticulum sidecar (Rust):** Clippy + rustfmt via `pnpm run check:reticulum-sidecar` (full-feature fmt + Clippy + test when `cargo` is on `PATH` **and** sidecar-related paths are staged) and the same feature set in `reticulum-sidecar.yaml`. Coverage threshold (`cargo llvm-cov --fail-under-lines`) is enforced only in `tests.yaml` when sidecar paths change — not in pre-commit.
- **Temp dirs in tests:** Use `mkdtempSync(path.join(os.tmpdir(), 'prefix-'))` — never write to a fixed name under `os.tmpdir()` (CodeQL + `check:insecure-temp-files`).
- Vitest worker pool sizes and shared Vite dep inline lists live in `vitest.harness.ts` — update when adding deps that need inlining.
- Prefer `mockConsoleWarn` / `withMockedConsoleWarn` from `src/renderer/lib/vitestConsoleMock.ts` over ad-hoc `vi.spyOn(console, 'warn')` in renderer tests.
Expand All @@ -110,7 +110,9 @@ Adding a cross-boundary feature:

## 6. Commands & CI Checks

**Key commands:** `pnpm run dev`, `pnpm run lint`, `pnpm run typecheck`, `pnpm run test:run`, `pnpm run update`. Reticulum sidecar: `pnpm run check:reticulum-sidecar` (pre-commit stub), `pnpm run reticulum:sidecar:clippy:full`, `pnpm run reticulum:sidecar:test`.
**Key commands:** `pnpm run dev`, `pnpm run lint`, `pnpm run typecheck`, `pnpm run test:run`, `pnpm run check:pr`, `pnpm run update`. Reticulum sidecar: `pnpm run check:reticulum-sidecar` (full features), `pnpm run reticulum:sidecar:clippy:full`, `pnpm run reticulum:sidecar:test`.

**ESLint type-aware scopes:** production `src/**` enables `no-unsafe-*`; `*.test.ts` / `*.test.tsx` keep those off. `@typescript-eslint/no-unnecessary-condition` is error only for `src/shared/**` and `src/renderer/lib/**` (not UI components/runtimes).

**Local Linux CI (optional):** Container mode — `act:ci`, `act:tests`, `act:pr`, … (needs a Docker-compatible engine + act; Podman preferred). Host mode — `act:ci:native`, `act:tests:native`, … (no container engine). See [docs/ci-cd.md](docs/ci-cd.md). macOS/Windows packaging uses native `dist:mac` / `dist:win`. **`dist:mac`** / **`dist:mac:publish`** always run **`scripts/verify-mac-packaging.mjs`** (ZIP + DMG symlink asserts, no raw `.app` CI uploads). macOS signing env (`CSC_LINK`, `CSC_KEY_PASSWORD`, `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID`, `CSC_IDENTITY_AUTO_DISCOVERY`) is scoped to **`macos-latest`** jobs in `release.yaml` / `build.yaml`; partial-secret validation fails the release job when `CSC_LINK` is set but notarization secrets are missing.

Expand All @@ -123,12 +125,14 @@ Adding a cross-boundary feature:
3. markdownlint on **staged** `.md` files only
4. When dependency manifests staged: `pnpm dedupe`, re-stage lockfile and originally staged paths
5. When `en/translation.json` is staged: `pnpm run i18n:auto-translate` and re-stage `src/renderer/locales/`
6. ESLint on **staged** JS/TS with `--cache` (CI still runs full `pnpm run lint`); full `typecheck`
6. ESLint on **staged** JS/TS with `--cache` (CI still runs full `pnpm run lint`); full `typecheck`; path-gated `typecheck:strict-shared` when `src/shared/` or `tsconfig.strict.json` staged
7. Always-on cheap `check:*` scanners; path-gated checks for flatpak / DB migrations / IPC / reticulum interface modes / decommissioned hubs / `check:reticulum-sidecar` (when `cargo` on `PATH` and sidecar paths staged); `check:i18n` when English locale staged else `check:i18n:branch`; `check:licenses`
8. `pnpm audit` only when dependency manifests staged; `actionlint` / `yamllint` when workflows / YAML staged
9. `pnpm run test:staged` → `scripts/precommit-tests.mjs` (staged-only `vitest related`; full suite for vitest config/setup/deps; skip when no source/test staged)

Before PR: `pnpm run lint`, `typecheck`, `test:run` (full suite), plus any relevant `check:*`. Release pre-flight (`pnpm run release`) always uses `test:run` + full `check:*` (no path-gating / soft-skips).
**Pre-push:** `.githooks/pre-push` runs `vitest run --changed <merge-base-with-origin/main>` when `origin/main` exists.

Before PR: `pnpm run check:pr` (lint + typecheck + `typecheck:strict-shared` + full `test:run` + path-aware sidecar). Release pre-flight (`pnpm run release`) always uses `test:run` + full `check:*` (no path-gating / soft-skips).

## 7. Git & PR Workflow

Expand Down Expand Up @@ -170,7 +174,7 @@ Conventional Commits (`feat:`, `fix:`, `docs:`, `chore:`, `refactor:`, `test:`).

- **Engines:** `src/renderer/lib/diagnostics/`; `RoutingDiagnosticEngine.ts`, `RFDiagnosticEngine.ts` (includes MeshCore **High Companion TX Queue** when `queueLen > 200`), `RemediationEngine.ts`, `ReticulumDiagnosticEngine.ts`.
- **Store:** `src/renderer/stores/diagnosticsStore.ts`; routing/RF rows, foreign LoRa, MQTT ignore, redundancy.
- **Tab scoping:** `filterDiagnosticRowsForProtocol()` — Meshtastic/MeshCore tabs show LoRa rows only; Reticulum tab shows `reticulum/*` only. Foreign-LoRa tables UI is Meshtastic-tab-only.
- **Tab scoping:** `filterDiagnosticRowsForProtocol()` — Meshtastic/MeshCore tabs show LoRa rows only; Reticulum tab shows `reticulum/*` only. Foreign-LoRa tables UI is on Meshtastic and MeshCore tabs (keyed by that protocol’s self node id).
- **Extend:** adjust `DiagnosticRow` in `src/renderer/lib/types.ts`, add detector, wire `replaceRoutingRowsFromMap` / `replaceRfRowsForNode`; TTL defaults in `diagnosticRows.ts` (routing 24h, RF 1h).
- **Full reference:** [docs/diagnostics.md](docs/diagnostics.md).

Expand Down
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Sanitize user-controlled strings before logs and IPC per [AGENTS.md](AGENTS.md).

- **Engines:** `src/renderer/lib/diagnostics/`; `RoutingDiagnosticEngine.ts`, `RFDiagnosticEngine.ts`, `RemediationEngine.ts`.
- **Store:** `src/renderer/stores/diagnosticsStore.ts`; routing/RF rows, foreign LoRa, MQTT ignore, redundancy.
- **Tab scoping:** `filterDiagnosticRowsForProtocol()` — Meshtastic/MeshCore tabs show LoRa rows only; Reticulum tab shows `reticulum/*` only. Foreign-LoRa tables UI is Meshtastic-tab-only.
- **Tab scoping:** `filterDiagnosticRowsForProtocol()` — Meshtastic/MeshCore tabs show LoRa rows only; Reticulum tab shows `reticulum/*` only. Foreign-LoRa tables UI is on Meshtastic and MeshCore tabs (keyed by that protocol’s self node id).
- **Extend:** adjust `DiagnosticRow` in `src/renderer/lib/types.ts`, add detector, wire `replaceRoutingRowsFromMap` / `replaceRfRowsForNode`; TTL defaults in `diagnosticRows.ts` (routing 24h, RF 1h).
- **Node health score:** `src/renderer/lib/nodeHealthScore.ts`; `nodeHealthScore(node)` → `NodeHealthBreakdown`; `nodeHealthTier(total)` → color tier.
- **Watch/notify:** `src/renderer/stores/watchedNodesStore.ts` (persisted Set<nodeId>); `src/renderer/hooks/useNodeStatusNotifier.ts` (fires OS Notification on online/offline transitions).
Expand Down
19 changes: 16 additions & 3 deletions docs/ci-cd.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Runs on every push and pull request to `main`:
5. Upload Cobertura coverage to GitHub Code Coverage (non-fork PRs / pushes) — Vitest merge job only
6. Upload merged test results artifact (retained 7 days)

SonarQube Cloud uses **Automatic Analysis (Autoscan)** — not GitHub Actions — because the Free plan Sonar Way quality gate includes cognitive-complexity thresholds we cannot customize, and CI scanning would fail PRs on that gate. Keep **Automatic Analysis enabled**. Scope and issue suppressions are configured in `sonar-project.properties` / `.sonarcloud.properties` and (for multicriteria under Autoscan) the SonarCloud project Analysis Scope UI.
Static analysis on PRs is **CodeQL** (security) plus ESLint, Clippy, and pre-commit `check:*` scanners. AI PR review is **CodeRabbit** (see [CodeRabbit](#coderabbit) below). SonarQube Cloud is not used.

Test results are available as a downloadable artifact from the workflow run.

Expand All @@ -63,7 +63,18 @@ Path-filtered on `reticulum-sidecar/**` and related scripts:
1. **`lint` job (ubuntu-latest)** — `cargo fmt --check` + `cargo clippy` with `rns-stack,rns-ble,rns-rnode-tcp` (`-D warnings`)
2. **Build matrix** — stub + full-stack `cargo test` and release builds on Linux, macOS, and Windows (including WoA arm64 jobs)

Local parity: `pnpm run reticulum:sidecar:clippy:full`, `pnpm run check:reticulum-sidecar` (pre-commit stub). See [development-environment.md](development-environment.md#reticulum-sidecar-optional).
Local parity: `pnpm run reticulum:sidecar:clippy:full`, `pnpm run check:reticulum-sidecar` (pre-commit full-feature). See [development-environment.md](development-environment.md#reticulum-sidecar-optional).

---

## CodeRabbit

PR review comments come from [CodeRabbit](https://docs.coderabbit.ai/) via [`.coderabbit.yaml`](../.coderabbit.yaml) (quiet profile, path filters, auto-pause after two reviewed commits).

- Prefer opening as a **draft** until the feature diff is ready, then mark ready for review.
- Free plan: about **1 PR review per developer per hour**; each auto-incremental push counts. After auto-pause, request another pass with `@coderabbitai review`.
- Batch actionable findings via the **Prompt for AI Agents** block into one local commit (Autofix requires Pro).
- Check remaining allowance with `@coderabbitai rate limit`.

---

Expand Down Expand Up @@ -243,7 +254,9 @@ The pre-commit hook (`.githooks/pre-commit`) runs checks beyond what GitHub Acti
- **Staged-file** Prettier + markdownlint (not a full-tree `pnpm run format` / `lint:md`)
- `pnpm dedupe` when dependency manifests are staged
- `pnpm run i18n:auto-translate` when `en/translation.json` is staged (fills new English keys vs `HEAD`) + re-stages locales
- Staged ESLint (`--cache`) + full `typecheck`; always-on cheap `check:*` scanners; path-gated flatpak / DB / IPC / reticulum catalog / sidecar stub checks (sidecar stub also requires `cargo` on `PATH` when sidecar paths are staged; `check:i18n` when English locale staged, else `check:i18n:branch`)
- Staged ESLint (`--cache`) + full `typecheck`; path-gated `typecheck:strict-shared` when shared paths staged; always-on cheap `check:*` scanners; path-gated flatpak / DB / IPC / reticulum catalog / full-feature sidecar checks (sidecar also requires `cargo` on `PATH` when sidecar paths are staged; `check:i18n` when English locale staged, else `check:i18n:branch`)
- Pre-push: `vitest run --changed` vs merge-base with `origin/main` when available
- Before PR: `pnpm run check:pr` (full lint + typecheck + strict-shared + `test:run` + path-aware sidecar)
- `pnpm audit` only when dependency manifests staged; `actionlint` / `yamllint` only when relevant files are staged
- `pnpm run test:staged` (`scripts/precommit-tests.mjs`: staged-only `vitest related`; full suite when vitest config/setup mocks or dependency manifests change; skip when no source/test staged)

Expand Down
Loading