Files
serumalt/AGENTS.md
T
biggy 4b0db50271 refactor(source): move every implementation into its module directory
- 36 files move into the module whose AGENTS.md already claimed them:
  Engine/, Synth/, Modulation/, Params/, Plugin/, GUI/, EffectUnits/, Resources/
- Source/engine, modulation, params, plugin and synth become Engine,
  Modulation, Params, Plugin and Synth, matching the directories that already
  hold code
- ExactCompare.h stays at the Source/ root as the one shared header
- SERUMALT_INCLUDE_DIRS adds one include directory per module, so the 31
  parent-relative includes become plain names with no path prefix
- the module AGENTS.md files list owned files instead of describing a move
  that had not happened, and README.md and AGENTS.md describe the real tree

Verified: the harness passes CTest, the Linux VST3 and Standalone link, the
Windows cross-build passes its module checks, each configure exposes only its
own targets, no first-party warning line appears, and the generated Windows
manifest is unchanged.
2026-10-06 09:17:49 +02:00

144 lines
7.6 KiB
Markdown
Raw 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.
<!-- graft:start -->
## Graft — repo context graph
This repo is indexed in `graft/`: small linked markdown nodes that explain each
system and carry exact file:line spans, kept in sync with the code through git.
For ANY task here — understanding how something works, finding where code lives,
or scoping a change — get context from the graph before grepping or opening
source files. Re-ask freely (it's cheap) and reuse literal identifiers you
already have (symbol, error string, file name) as the query. New to this repo?
Run `graft map` first — a token-budgeted orientation (dir clusters, hubs,
hotspots), no LLM, no key.
- Run `graft ask "<your question>" --source` → ranked nodes with the relevant
code spans inlined (each hit's ≤8-line crux by default; `--full` for whole
definitions when the crux isn't enough). Match the tool to the task shape:
for understanding or editing, the top node IS the answer — cite its
`covers:` file:line spans and edit straight from `--source`. For
exhaustive tasks ("every occurrence / every caller of this pattern"), ranked
results are top-N, not complete — run `graft grep "<literal>"` instead
(exhaustive over indexed files, grouped by enclosing symbol), falling back
to raw `grep -rn` only for unindexed files.
- `graft skeleton <file>` → every definition's signature + span, ~10× cheaper
than reading the file; use it to skim an API surface.
- `graft callers <symbol>` gives precomputed, exact edges — who calls this.
Add `--direction out` for what it calls, or `--depth N` to walk
transitively for the full blast radius. For structural questions, skip
ranking and use this directly.
- Or browse: `graft/INDEX.md` lists every node; follow the links.
- Monorepos and folders of multiple repos rank fairly across sub-projects —
hits carry `[scope/]` labels naming which one they're from. Narrow with
`graft ask "<task>" --in <scope>/` once you know where you're working.
If a returned span is truncated ("+N more lines"), open the file at that exact
range before finalizing. Only open source files when a node genuinely lacks a
needed detail, and then at the exact file:line the node points to — never
re-read whole files.
After big code changes, refresh the graph with `graft build` (deterministic,
no API key, $0).
<!-- graft:end -->
# SerumAlt agent guidance
## Project
SerumAlt is a JUCE 7 wavetable synthesiser using C++17. Project metadata is in
`CMakeLists.txt`. Production formats are VST3 and Standalone on Linux, VST3/AU/Standalone
on macOS, and VST3 on Windows. First-party classes use `namespace serum`; executable
entry points and JUCE's `createPluginFilter` are outside that namespace.
## Development scope
This is a fun proof of concept. Prefer small working changes, clear names, and
successful compilation over release engineering or broad rewrites. Use the existing
smoke harness for DSP changes; add test infrastructure only when requested. The user
will judge sound and interaction by using the synth. Keep ownership and audio-thread
safety fixes practical, without adding a generic framework.
## Build and verification
Read README.md's Building section before changing scripts, dependencies, or CMake.
It is the maintained source for prerequisites, options and artifact paths.
- Linux production: `./scripts/build_linux.sh`.
- macOS production, on a Mac: `./scripts/build_macos.sh`.
- Windows x64 VST3, cross-compiled on Linux: `./scripts/build_windows.sh`.
- Native QA build and execution: `./scripts/build_harness.sh --run`.
- Shell syntax: `for script in scripts/*.sh; do bash -n "$script"; done`.
- Build output removal: `./scripts/clean.sh --dry-run`, then the flags you need.
Production scripts explicitly enable `SERUMALT_BUILD_PLUGIN` and disable
`SERUMALT_BUILD_TESTS`. The harness script does the reverse in a separate directory.
Building `SerumAltTest` alone does not execute it; `--run` invokes CTest.
When changing CMake, verify production has no `SerumAltTest` target and the
harness-only build has no `SerumAlt`, VST3, AU, or Standalone target.
Keep existing build trees and local changes intact. Each script defaults to a
directory under `build/`; use a new `BUILD_DIR` for clean-build verification. Use a
fresh Windows `OUTPUT_DIR` for distribution checks, since staging does not remove
extra files. Never edit generated CMakeCache.txt files. The checked-in
`cmake/mingw64-toolchain.cmake` is the single source of cross-compiler configuration.
## Current source layout
`CMakeLists.txt` owns the explicit `SERUMALT_SOURCES` list, the module include
directories and shared target setup. `cmake/Plugin.cmake` owns production formats
and MinGW link flags; `cmake/Harness.cmake` owns the console harness and CTest
registration. The harness compiles the same processor/editor sources independently
of the plugin.
Every implementation lives in a module directory under `Source/`, and the
`AGENTS.md` in that directory owns its rules:
- `Engine/` the voice pool, LFOs, wavetable library, FX rack, matrix and macros.
- `Synth/` wavetables, oscillators, filters, envelopes, LFOs and the voice.
- `Modulation/` the modulation matrix, macros, and `RAVEButton`, which defines
`RaveController`.
- `Params/` parameter IDs, indexed groups, enums and unit mappings.
- `Plugin/` the JUCE `AudioProcessor` boundary.
- `GUI/` the editor with its controls and previews.
- `EffectUnits/` the effect rack and the effect units.
- `Presets/`, `Resources/` and `Tests/`, as their names suggest.
`Source/ExactCompare.h` is the one shared header, and several modules include it.
`SERUMALT_INCLUDE_DIRS` lists one include directory per module, so a source names a
header without a path prefix. No two headers share a basename; check that before
adding one. `docs/AUDIT_REPORT.md` is a historical audit and proposed refactor, not
a pending instruction. Update `SERUMALT_SOURCES` and `SERUMALT_INCLUDE_DIRS` when
files move.
## Style and ownership
Follow the surrounding file. Existing C++ generally uses four-space indentation,
`#pragma once`, PascalCase types, camelCase members without prefixes, `k`-prefixed
constants, scoped enums, and `noexcept` on cheap getters and DSP paths. Use `Count`
only for enums that need a size or iteration sentinel. Headers include JuceHeader;
implementation files normally include their own header first.
Prefer value members or `std::unique_ptr` for owned objects and attachments.
`addAndMakeVisible` does not transfer ownership or delete children. Raw allocations
returned by `createEditor` and `createPluginFilter` transfer ownership to JUCE callers.
Guard parameter lookups and invalid state data. Use `jassert` sparingly for programmer
errors and return `bool` from bounded inserts to report failure.
## Real-time and concurrency requirements
For new or changed audio paths, allocate buffers during preparation and avoid heap
allocation, blocking locks and I/O in `processBlock` or per-sample DSP. Account for
variable host block sizes without allocating in the callback. Keep GUI updates on
the JUCE message thread. Exchange GUI/audio state through a safe snapshot or bounded
handoff; a timer or a writer-only mutex does not make concurrent access safe.
The engine prebuilds wavetables and renders through bounded buffer views. GUI and
state edits hold `Engine::getControlLock()`; audio uses a try-lock to capture into
reserved storage and keeps its last snapshot when busy. Keep those two copies
separate. RAVE changes render settings, not the underlying knob values. The
historical audit describes the older implementation, not current behavior.
## Scope of guidance
Module guidance refines this file for its related sources. Preserve the root
real-time and ownership requirements if an example conflicts with them.