Files
vadimwit 072920ff0e docs+config: never-delete policy — CLAUDE.md hard rule + .claude/settings.json deny list
Claude must never execute destructive/irreversible commands (rm, git branch
-d/-D, git push --delete, reset --hard, force-push, DROP, etc.) — it proposes
them for the user to run. Enforced behaviorally in CLAUDE.md (authoritative)
and as permissions.deny rules in .claude/settings.json (defense-in-depth).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 21:36:02 +01:00

93 lines
6.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Destructive operations — NEVER delete (hard rule)
**Claude must never EXECUTE a destructive or irreversible command. Always ask, and hand the user the exact command(s) to run themselves.**
This covers (non-exhaustively):
- Deleting files/directories: `rm`, `rm -rf`, `rmdir`, `del`, `Remove-Item`.
- Deleting branches: `git branch -d` / `-D`, `git push --delete`, `git push <remote> :branch`.
- Deleting tags/remotes: `git tag -d`, `git remote remove` / `rm`.
- Discarding work: `git reset --hard`, `git checkout -- <path>`, `git clean -f`.
- Force-pushing: `git push -f` / `--force` / `--force-with-lease`.
- Dropping data: `DROP`, `TRUNCATE`, destructive migrations.
Instead: print the command(s) in a fenced block with a one-line note on what each does and what it affects, and let the **user run them**. Never run them yourself, even when the desired outcome is clear — `rm` and `-d` are **prompted, never executed**.
Leave regular branches alone (`main`, the active sprint branch) unless the user explicitly names them. Before calling any branch "stale", prove containment (`git branch --merged`, 0 unique commits) and report that evidence — do not act on it.
These are also enforced as `permissions.deny` rules in `.claude/settings.json` (defense-in-depth), but this behavioral rule is authoritative and covers cases the patterns can't.
## Commands
```bash
# Development (Vite dev server + Electron window with hot reload)
npm run electron:dev
# Browser-only dev (no Electron)
npm run dev
# Build installers
npm run electron:build:win # Windows NSIS installer → releases/
npm run electron:build:mac # macOS DMG → releases/
npm run electron:build:linux # Linux AppImage → releases/
```
No test suite exists. There is no lint script — no ESLint config is present.
## Architecture
**Electron shell** (`electron/main.cjs`) loads `dist/index.html` in production or `localhost:5173` in dev. The renderer process has full Web Audio API access (`sandbox: false`). `preload.cjs` uses `contextIsolation: true` with no exposed IPC — Electron is purely a window host; all logic lives in the renderer.
**Two audio pipelines run in parallel** inside `AudioCapture.jsx`:
| Path | FFT | Purpose |
|---|---|---|
| Pitch | 4096 samples (~90ms, `smoothingTimeConstant=0.0`) | McLeod pitch detection via `pitchy` → feeds key detection |
| Chord | 16384 samples (~370ms, `smoothingTimeConstant=0.5`) | Harmonic summation chroma → feeds chord detection |
The 16384 FFT gives 2.7 Hz/bin resolution, which is necessary to separate adjacent semitones on low guitar strings (~5-6 Hz apart). The chord analyser uses `computeChroma()` — a harmonic summation that folds each FFT bin back through 5 harmonics to cancel overtone contamination (prevents minor chords from reading as major).
**State and detection logic lives entirely in `App.jsx`:**
- `handleNote` (pitch callback) → accumulates `noteHistoryRef`, runs Krumhansl-Schmuckler key detection every 5 notes, votes in `keyVotesRef` (rolling window, requires strong consensus before committing)
- `handleChroma` (chord callback) → averages a ring buffer of `chromaSmooth` frames, runs a **chroma stability gate** (per-bin variance check — bails if still in transition), then matches against chord templates via `matchChordFromChroma`, votes in `chordVotesRef`
- `handleOnset` (onset callback from RMS spike detection) → builds a **tempo histogram** from pairwise inter-onset intervals, folding all intervals into 55220 BPM range; the histogram peak drives BPM display
Both `handleNote` and `handleChroma` use `useCallback(fn, [])` (empty deps). All values they need from render scope are kept in refs synced via `useEffect` — this prevents `AudioCapture`'s `start` from recreating on every render.
**All music theory is in `src/lib/theory.js`:**
- `detectKey` / `detectTopKeys` — Krumhansl-Schmuckler correlation against major/minor profiles only (K-S cannot distinguish modes — Dorian vs natural minor look the same; user manually picks mode)
- `matchChordFromChroma` — weighted coverage score (inEnergy / (inEnergy + outEnergy×0.7)), requires root presence (`chroma[r] >= 0.08`), margin over second-best, diatonic/bass bonuses
- `MATCH_CHORD_TYPES` — the subset of chord types used in real-time detection (not all of `CHORD_TYPES`)
- `detectRepeatingProgression` — non-overlapping pattern match over last 20 chords, length 26
**`src/services/audioService.js`** is a self-contained tuner hook (`useAudioTuner`) used only by `Tuner.jsx`. It uses its own separate `AudioContext` with simple autocorrelation — independent from the main pitch/chord pipeline.
## Design tokens (Tailwind)
Defined in `tailwind.config.js`: `bg-surface` (#0f0f0f), `bg-panel` (#1a1a1a), `border-border` (#2a2a2a), `text-accent` / `bg-accent` (#a855f7 purple). Use these rather than raw hex in components.
## Key configuration (`DEFAULTS` in `App.jsx`)
| Key | Purpose |
|---|---|
| `chromaSmooth` | Ring buffer size (frames averaged before chord check) |
| `chordVoteThreshold` | Consecutive matching chord frames required to commit |
| `chordMinScore` | Minimum coverage score from `matchChordFromChroma` |
| `keyVoteWindow` / `keyVoteThreshold` | Rolling window size and consensus count for key lock |
| `noteHistorySize` | Max pitch-class history kept for K-S key detection |
These are exposed in `Settings.jsx` as sliders. `configRef` keeps a ref in sync so stable callbacks can read current values.
## Instrument views
`Fretboard.jsx` and `Piano.jsx` are SVG-rendered visualisers. Both accept `keyInfo`, `currentChord`, and `monoColor`. They call `getPentatonicScale`, `getFullScale`, `getChordTones` from `theory.js` and colour notes by tier: chord tone (purple `#a855f7`) > pentatonic (amber or light purple in mono) > scale (dark gray or lightest purple in mono).
## GitHub Actions
`.github/workflows/release.yml` builds Windows and macOS installers on tagged pushes (`v*`) using `softprops/action-gh-release@v2`. Build scripts use `--publish never` to prevent electron-builder's own publish step.