Unified SignalGraph workflow¶
This guide is the practical path for a developer or coding agent building a graph-backed instrument, effect, host, or modular application. It connects the existing graph library, kit fixtures, validation, prepared edits, offline rendering, diagnostics, and bake tooling. It does not introduce another graph runtime or a second control authority.
Start here¶
| If you are trying to… | Start with… | Finish by proving… |
|---|---|---|
| Build fixed DSP or voice logic | Processing models and Processor |
Processor lifecycle and audio output tests |
| Learn the graph vocabulary | Signal Graph Reference | A small graph that loads, prepares, and renders |
| Study a complete example | Sample granular graphs and the installed consumer README | The example's structural and executable receipts |
| Build editable routing or hosted composition | Choose the graph boundary | A prepared graph and one executable render |
| Turn an authored or generated graph into audio | Load and validate before preparation | Limits, preparation, partition contract, and output receipt |
| Change a running graph | Prepare and publish a safe edit | Valid edit acceptance and invalid edit rollback |
| Diagnose or reproduce a graph | Inspect and diagnose | Semantic receipt, generation receipt, diagnostics, and audio artifact |
| Automate graph work as an agent | Agent and Forge acceptance workflow | One deterministic fixture with positive and negative controls |
Humans can read the sections top to bottom. Agents should use the table as a dispatch map, then follow the selected section and record the artifacts named there. The capability matrix is the compact status view; this guide is the operational sequence.
What a graph is, and when to use one¶
An audio graph is a set of processing nodes connected by typed paths. Audio, MIDI, automation, modulation, and feedback move along those paths in a defined order. The graph is useful when the application needs to make the connections themselves part of the product: a patch editor, instrument routing network, effect chain, mixer, or hosted processing rack.
| Use this boundary | When it fits | Typical work |
|---|---|---|
Processor |
The signal algorithm and its state are fixed inside one component | Oscillators, voices, filters, samplers, meters, and reusable DSP modules |
SignalGraph |
The application edits nodes, routing, hosted processing, or graph-level automation | Modular instruments, effect chains, mixers, plugin hosts, and patch editors |
| Timeline/playback | The behavior depends on transport, tempo, arrangement, pre-roll, or tails | Songs, clips, automation lanes, and offline renders with transport |
Pulp graphs currently cover DAG composition, stable node identity, prepared topology edits, MIDI and bounded modulation lanes, explicit feedback delay, latency/PDC, serial and parallel execution, graph and timeline offline paths, serialization, and signed bake artifacts. Hosted plugin breadth, richer bus projection, process isolation, and some event-format and continuity cases remain explicitly gated; read the capability matrix before treating a row as a universal guarantee.
The practical rule is to keep fixed DSP and voice state in Processor, and use
SignalGraph where composition is the feature being edited. ProcessorNode wraps a configured processor-node instance; the graph retains
its node lifetime while owning routing and execution. A timeline binding likewise
requires its graph and program store to outlive the binding. Choosing the graph boundary does not require a second runtime or a
duplicate control model.
Ask these three questions before writing code:
- Is the signal algorithm fixed? Put it in a
Processor, including complex voices, sidechains, oversampling, and internal serial or parallel stages. - Can a user or host change the routing while the product runs? Use a
SignalGraph, with prepared edits and an immutable published snapshot. - Does behavior depend on musical time? Use the timeline model and its transport-aware renderer; bind graph work underneath it when needed.
A graph fixture is a routing and lifecycle example, not proof of every host or format. The sample-region example is a small graph-backed fixture; the timeline phase example shows the transport-owned path. Use both when a feature crosses the graph and timeline boundaries.
Choose the graph boundary¶
Start with Processing models. Keep fixed
DSP, voice state, and sample processing in Processor. Use
pulp::host::SignalGraph when the routing or hosted composition is the thing
the application edits: a rack, mixer, modular patch, plugin host, or node
editor. ProcessorNode uses the configured processor-node path while SignalGraph
retains the node lifetime and owns routing and execution. Timeline playback uses its existing
binding to lower transport-driven work into graph nodes.
Discover a fixture or kit¶
Use the existing kit workflow to discover reviewed graph fixtures and their declared files:
pulp kit search graph --root ./fixtures/packages --lane kit --json
pulp kit inspect ./fixtures/packages/my-graph-kit --json
pulp kit validate ./fixtures/packages/my-graph-kit --json
pulp kit verify ./fixtures/packages/my-graph-kit --project . --json
signal-graph-fixture entries are structurally checked by pulp kit verify.
That check proves manifest shape, paths, and declared fixture metadata; it does
not execute a graph. Treat an unexecuted fixture as an input to the next step,
not as audio evidence. pulp kit apply remains a reviewed project mutation and
must be used only after inspecting its plan.
Executable fixture dispatch¶
The use-case names in the planning material map to checked-in source and test targets as follows. Run a target through the normal governed build, then run the matching CTest name from the configured build directory. A source link without a target is an authoring reference, not a passing runtime receipt.
| Workflow | Start with | What it proves | Current boundary |
|---|---|---|---|
| Voice and polyphony (X1) | test_instrument_voice_allocator.cpp, test_voice_runtime_facade.cpp, test_mpe_synth_voice.cpp |
voice lifecycle and per-note building blocks | No single complete instrument fixture yet |
| Feedback and modulation (X2) | pulp-test-host-signal-graph, pulp-test-signal-graph-executor-parity |
graph execution, feedback and parity controls | A combined serialized replay receipt remains open |
| Reusable components (X3) | pulp-test-signal-graph-x9-modular-instrument (X3 cases) |
two independent component instances in the public-API fixture | General template authoring is still higher-level guidance |
| Routing and mutation (X4) | pulp-test-signal-graph-prepared-swap-live and pulp-test-signal-graph-pdc-swap-continuity (declared in test/cmake/sampler_runtime_tests.cmake) |
prepared edits, latency/PDC and continuity policy | Full aux/sidechain application routing remains open |
| Hosted processing (X5) | pulp-test-external-plugin-graph-fixture (declared in test/cmake/timeline_tests.cmake), plus the conditional pulp-test-signal-graph-x9-hosted-format-receipt CTest in CMakeLists.txt when the X9 target exists |
conditional format availability and graph-host seams | Bus negotiation, graph-visible failure, offline parity and isolation remain gated |
| Generated instrument (X6) | pulp-test-dsl-processor |
deterministic generated Processor path |
Graph/template discoverability and full live/baked/offline join remain open |
| Mixed-rate/sample-region work (X7) | sample granular graphs, pulp-test-host-signal-graph |
documented rate and causality building blocks | A complete mixed-rate authoring fixture is not yet claimed |
| Inspection and replay (X8) | pulp-test-live-dsp-telemetry-graph, pulp-test-signal-graph-offline-parity |
diagnostics and output parity surfaces | Joined topology/event/fault replay receipt remains open |
| Complete modular instrument (X9) | pulp-test-signal-graph-x9-modular-instrument |
public-API integration of voices, modulation, state, mutation and offline render | Hosted-format matrix and replay/state/output closure remain conditional |
For a clean-checkout productivity measurement, record the first successful fixture command, elapsed setup time, concepts required, manual integration steps, and the first diagnostic artifact. Repeat the same exercise after any documentation change. This keeps a discoverability improvement distinct from new runtime capability.
Load and validate before preparation¶
For a generated or authored .pulpgraph document, keep the JSON bytes and
serializer version as part of the fixture receipt. The library workflow is:
- Load with
GraphSerializer::from_jsoninto a control-thread graph. - Apply the graph's
GraphLimitsfor the target application. - Call
validate_generated_graph(max_block_size)beforeprepare()and record its first stable rejection reason, actual value, and limit when it fails. - Use
estimate_generated_graph_work_units(max_block_size)when presenting a budget before enforcing the limit. - Record the canonical
GraphSerializer::to_jsonform and the source fixture identity. A canonical semantic form is not a resolved execution-plan fingerprint; keep those diagnostics separate.
Validation is a pure preflight. prepare() is the lifecycle operation that can
drop an old snapshot and publish a newly compiled one. Both are control-thread
operations. Never use graph import, serialization, limits, or preparation from
the audio callback.
Prepare and publish a safe edit¶
For a running graph, prefer the existing begin_prepared_topology_edit() path:
- Start a private candidate from the current prepared generation.
- Apply additions, removals, connections, automation, feedback, or modulation changes to the candidate.
- Prepare the candidate off the audio thread with the target sample rate and block size.
- Commit only when preparation, limits, lifecycle, and latency policy accept the candidate; otherwise retain the current graph and record the refusal.
The transaction is atomic at the graph publication boundary. It does not promise that every latency-changing or feedback edit is seamless. The documented contract may intentionally require eager preparation and a bounded interruption. Do not describe a refused edit as a partial success.
Render the right offline surface¶
Use OfflineSignalGraphHost to prove that a prepared graph is invariant to
block partitioning. It drives the no-transport SignalGraph::process() path;
it does not provide timeline transport or tail injection.
Use the timeline offline renderer when notes, automation, tempo, pre-roll, PDC,
or tails depend on transport. Driving a timeline graph through
OfflineSignalGraphHost produces silence by contract because the nodes receive
no transport state.
For every fixture, record the sample rate, maximum block size, partition list, input stimulus, serialized semantic form, output hash or audio-quality artifact, and whether the path is graph-host or timeline-host. Compare live/offline output only when both paths have the same declared transport contract.
Inspect and diagnose¶
The existing library diagnostics are the first authority:
prepared_stats()for compiled node, connection, port, work, and buffer facts;- routing executor counters for serial/parallel path selection;
- graph authoring receipts and generation for publication identity;
- trace, logs, telemetry, and audio-quality artifacts through the canonical control registry where an application has an installed control adapter.
The CLI and MCP currently expose adjacent control capabilities, but not a single SignalGraph topology inspect/validate/diff/render workflow. Do not infer that capability from a built control component or a kit manifest. The current limitation is tracked in SignalGraph capability matrix.
Bake only after validation¶
When a graph is lowerable and trusted, use the existing bake path:
pulp bake myrack.pulpgraph -o myrack.pulpbake --sign-key signing.key
pulp bake verify myrack.pulpbake --trust signing.key
Bake is an artifact step after graph validation and preparation. It is not a replacement for live graph editing, hosted-plugin lifecycle testing, or process isolation. Hosted Plugin nodes and unsupported runtime-owned nodes remain subject to the documented bake restrictions.
Agent and Forge acceptance workflow¶
An agent-authored graph is ready to use only when one deterministic fixture can:
- be discovered and structurally verified through the kit workflow;
- load and validate limits before preparation;
- prepare and render with a declared partition/transport contract;
- apply one valid prepared mutation and reject one invalid mutation without disturbing the published graph;
- emit a semantic JSON receipt, publication/generation receipt, diagnostics, and an audio-quality artifact; and
- optionally bake and verify the resulting artifact when its node set is lowerable.
This is the smallest useful agent-facing contract. It uses existing graph, control, kit, trace, and audio-quality infrastructure. A typed graph CLI/MCP operation should be added only if this workflow demonstrates repeated manual integration cost; any such operation must reuse the canonical control authority, authorization, receipts, and artifact lineage.
Capability gaps and status¶
See the capability matrix for the current split between library support, authoring/tooling exposure, evidence confidence, and next acceptance gate. Update that matrix and the durable planning ledger whenever a fixture, implementation, or no-change decision changes status.