Files
serumalt/AGENTS.md
T
biggy facd5c5b5e chore: move build output under build/ and scripts into scripts/
Layout:
- scripts/ holds the four build scripts plus the new clean.sh
- cmake/ holds mingw64-toolchain.cmake
- the scripts default to build/linux, build/macos, build/windows, build/harness
- docs/ holds the audit documents, with the round 1 task list archived

Cleanup:
- scripts/clean.sh removes generated output, with --dry-run, --staging,
  --legacy and --all, and refuses tracked, protected or outside paths
- the Windows VST3 manifest now comes from cmake/moduleinfo.json.in, so its
  version, vendor and class IDs follow cmake/Plugin.cmake instead of a
  hand-written heredoc that could drift
- deleted the unused Source/GUI/Slider, which no code constructed
- fixed the hidden double processBlock overload: three warning lines to zero
- unslopped the build scripts, the two .cjs helpers and the CMake comments
- .gitignore covers the upstream JUCE Projucer and DemoRunner binaries

Verified: five scripts pass bash -n; the harness passes CTest with zero
warning lines; the Linux VST3 and Standalone link; the Windows cross-build
passes its x86-64, runtime-import and entry-point checks; each configure
exposes only its own targets; and a full build leaves Source/ and
third_party/JUCE unchanged. The generated manifest is identical to the
previous hand-written one.
2026-10-06 09:06:53 +02:00

7.3 KiB
Raw Blame History

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).

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 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.

Most C++ files still live directly under Source/. Existing implementation subdirectories are EffectUnits/, GUI/, Presets/, Resources/, and Tests/. FXProcessor, PluginEditor, Resources, and RAVEButton remain at the source root. RAVEButton.{h,cpp} defines RaveController.

The lowercase Source/engine, modulation, params, plugin, and synth directories currently hold guidance, not relocated implementations. Consult their AGENTS.md files when editing the related root-level sources. Consult the matching AGENTS.md when editing an existing implementation subdirectory. docs/AUDIT_REPORT.md is a historical audit and proposed refactor, not a pending instruction to move files. Update the explicit source list and includes only when an actual source move is part of the requested task.

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.