From 16995891fda4498604b86417186e1b0fcf908f1e Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 1 Jun 2026 02:06:49 +0100 Subject: [PATCH] Harden song swarm rendering environment --- .../aqua-dsp-usage-manual.md | 15 ++++---- src/AquaSynth.Faust/FaustCompiler.cs | 36 +++++++++++++++++-- state/evidence.jsonl | 1 + tools/run-song-snippet-trial-swarm.ps1 | 10 +++++- 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/docs/music-production-knowledge/aqua-dsp-usage-manual.md b/docs/music-production-knowledge/aqua-dsp-usage-manual.md index 9f488d6..b3b9687 100644 --- a/docs/music-production-knowledge/aqua-dsp-usage-manual.md +++ b/docs/music-production-knowledge/aqua-dsp-usage-manual.md @@ -42,15 +42,17 @@ voices with `gain=@/seq/kick`, `freq=@/seq/lead/freq`, or Voice-like leads: ```aqua -source_port name=lead_src kind=syrinx pressure=.75 opening=.42 -acoustic_network name=lead_body source=lead_src radiation=beak -acoustic_voice name=lead network=lead_body freq=@/seq/lead/freq gain=@/mix/lead/gain +voice name=vowel_lead wave=saw freq=@/seq/lead/freq gain=@/mix/lead/gain sustain=.18 decay=.22 fm=3 fmi=.25 lpf=.62 ``` Use syrinx/acoustic ownership when the reference has singing, vocal chops, -creature calls, reed-like lead, or expressive vowel motion. Move pressure, -opening, filter/formant color, and pitch. A static syrinx badge is not a vocal -line. +creature calls, reed-like lead, or expressive vowel motion, but only if the +patch includes a complete, known-valid acoustic graph scaffold. A lone +`source_port` or `acoustic_network` line does not render. Start from +`patches/advanced/syrinx-voice.aqua` or `patches/advanced/bird-syrinx.aqua` +when using syrinx in the swarm; otherwise use a formant-filtered ordinary lead +for the attempt and write the missing syrinx scaffold as an Aqua gap. A static +syrinx badge is not a vocal line. Subtractive drums: @@ -109,4 +111,3 @@ pressure. - Hand-writing isolated frequencies when the reference suggests a tonal center or progression. - Treating a metric bump as a lesson without a reusable owner sentence. - diff --git a/src/AquaSynth.Faust/FaustCompiler.cs b/src/AquaSynth.Faust/FaustCompiler.cs index 5efaf92..769f017 100644 --- a/src/AquaSynth.Faust/FaustCompiler.cs +++ b/src/AquaSynth.Faust/FaustCompiler.cs @@ -193,7 +193,8 @@ public static class FaustCompiler var dotnet = await RunAsync( "dotnet", ["run", "--project", Path.Combine(tempDir, "Render.csproj"), "--", outputPath, options.SampleRate.ToString(CultureInfo.InvariantCulture), frames.ToString(CultureInfo.InvariantCulture)], - cancellationToken); + cancellationToken, + DotnetRenderEnvironment(tempDir)); if (dotnet.ExitCode != 0 || !File.Exists(outputPath)) { return new FaustRender([], options.SampleRate, "dotnet", dotnet.Stdout, dotnet.Stderr); @@ -273,7 +274,8 @@ public static class FaustCompiler private static async Task<(int ExitCode, string Stdout, string Stderr)> RunAsync( string fileName, IReadOnlyList arguments, - CancellationToken cancellationToken) + CancellationToken cancellationToken, + IReadOnlyDictionary? environment = null) { var start = new ProcessStartInfo(fileName) { @@ -282,6 +284,13 @@ public static class FaustCompiler UseShellExecute = false }; foreach (var argument in arguments) start.ArgumentList.Add(argument); + if (environment is not null) + { + foreach (var (key, value) in environment) + { + start.Environment[key] = value; + } + } using var process = Process.Start(start) ?? throw new InvalidOperationException($"failed to start `{fileName}`"); var stdout = process.StandardOutput.ReadToEndAsync(); @@ -299,4 +308,27 @@ public static class FaustCompiler return (process.ExitCode, await stdout, await stderr); } + + private static IReadOnlyDictionary DotnetRenderEnvironment(string tempDir) + { + var home = Path.Combine(tempDir, "dotnet-home"); + var appData = Path.Combine(tempDir, "dotnet-appdata"); + var localAppData = Path.Combine(tempDir, "dotnet-localappdata"); + var packages = Path.Combine(tempDir, "nuget-packages"); + Directory.CreateDirectory(home); + Directory.CreateDirectory(appData); + Directory.CreateDirectory(localAppData); + Directory.CreateDirectory(packages); + return new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["DOTNET_CLI_HOME"] = home, + ["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "1", + ["DOTNET_NOLOGO"] = "1", + ["NUGET_PACKAGES"] = packages, + ["APPDATA"] = appData, + ["LOCALAPPDATA"] = localAppData, + ["USERPROFILE"] = home, + ["HOME"] = home + }; + } } diff --git a/state/evidence.jsonl b/state/evidence.jsonl index bc42d3a..561521b 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -269,3 +269,4 @@ {"timestamp":"2026-06-01T00:31:00+01:00","topic":"cumulative-music-knowledge-and-external-signal-map","claim":"The song swarm now owns one cumulative loop-level music-production `.cc`: it seeds manuals once, indexes them before pass one, then appends pass-learned worker/curator evidence with `music-distill --learned-only true --include-manuals false`. Added a current music-intelligence source map: Every Noise is static genre-vocabulary context, Echo Nest is gone, Spotify audio features are restricted/deprecated and not a training substrate, AcousticBrainz is historical, and local Essentia-style extraction should own primary audio signal.","evidence":"tools/run-song-snippet-trial-swarm.ps1; tools/IpaTrialWorker/Program.cs; docs/music-production-knowledge/music-intelligence-source-map.md; artifacts/parity/song-snippet-swarms/20260531T232719334/music-production-knowledge-seed.md; artifacts/parity/manual-knowledge-smoke/20260601T002939/report.md"} {"timestamp":"2026-06-01T01:05:00+01:00","topic":"per-target-local-music-intelligence","claim":"Each song challenge now emits a provider-labeled `intelligence.md` built from local audio analysis and source-path vocabulary, and musician prompts must read it alongside Qdrant music-production knowledge. The report translates tempo, register, brightness, noisiness, density, spectral motion, and cautious genre hints into AquaSynth production decisions. Keep prompt surfaces free of PowerShell markdown backtick escapes: escaped `r`/`t`/`n` tokens became hidden control characters in agent instructions, corrupting metric and file names.","evidence":"tools/IpaTrialWorker/Program.cs; tools/run-song-snippet-trial-swarm.ps1; artifacts/parity/song-snippet-swarms/20260531T234041397/pass-001/agent-01/target-01/intelligence.md; artifacts/parity/song-snippet-swarms/20260531T234041397/pass-001/agent-01/hypothesis-agent.prompt.md"} {"timestamp":"2026-06-01T01:58:00+01:00","topic":"song-swarm-render-failure-visibility","claim":"Song-swarm render failure must be visible to agents and operators. `song-score` now writes `audio/render-failure.txt` and includes failure summaries in `evaluator-report.md`. The parser accepts documented `env=ad attack=... decay=...` syntax, because the manual taught that form and agents copied it. Codex musician jobs now set repo-local `DOTNET_CLI_HOME`/`NUGET_PACKAGES` with first-time-use disabled so internal renderer `dotnet run` calls do not write to denied sandbox home directories.","evidence":"src/AquaSynth.Core/PatchScript.cs; tests/AquaSynth.Dsl.Tests/PatchScriptTests.cs; tools/IpaTrialWorker/Program.cs; tools/run-song-snippet-trial-swarm.ps1; artifacts/parity/song-render-failure-smoke/20260601T0145-agent01-a01/smoke-agent01-target01-attempt01/song-candidates/agent_01__swarm__a01/audio/render-failure.txt; artifacts/parity/song-render-failure-smoke/20260601T0152-agent01-a01-after-ad/smoke-agent01-target01-attempt01/summary.csv"} +{"timestamp":"2026-06-01T02:08:00+01:00","topic":"song-swarm-render-environment-and-syrinx-scaffold","claim":"Renderer builds must not depend on user-profile NuGet or dotnet config. `FaustCompiler.RenderAsync` now runs its temporary C# render project with temp-local HOME, APPDATA, LOCALAPPDATA, DOTNET_CLI_HOME, and NUGET_PACKAGES. The music manual and swarm prompt now treat acoustic/syrinx as complete-graph syntax: agents should start from known-valid syrinx patches or log a gap, not submit isolated `source_port`/`acoustic_network` fragments that cannot render.","evidence":"src/AquaSynth.Faust/FaustCompiler.cs; docs/music-production-knowledge/aqua-dsp-usage-manual.md; tools/run-song-snippet-trial-swarm.ps1; artifacts/parity/song-snippet-swarms/20260601T005553312/pass-001/agent-01/iterations/attempt-01/target-01/agent-01-target-01-attempt-01/song-candidates/agent_01__swarm__scene_a/audio/render-failure.txt"} diff --git a/tools/run-song-snippet-trial-swarm.ps1 b/tools/run-song-snippet-trial-swarm.ps1 index e935e85..5229a0f 100644 --- a/tools/run-song-snippet-trial-swarm.ps1 +++ b/tools/run-song-snippet-trial-swarm.ps1 @@ -91,9 +91,16 @@ function Start-CodexAgentJob { New-Item -ItemType Directory -Force -Path (Split-Path -Parent $JobLogPath) | Out-Null $dotnetHome = Join-Path $JobRepoRoot ".dotnet_cli_home" $nugetHome = Join-Path $JobRepoRoot ".nuget_packages" - New-Item -ItemType Directory -Force -Path $dotnetHome, $nugetHome | Out-Null + $appDataHome = Join-Path $JobRepoRoot ".dotnet_appdata" + $localAppDataHome = Join-Path $JobRepoRoot ".dotnet_localappdata" + $agentHome = Join-Path $JobRepoRoot ".dotnet_agent_home" + New-Item -ItemType Directory -Force -Path $dotnetHome, $nugetHome, $appDataHome, $localAppDataHome, $agentHome | Out-Null $env:DOTNET_CLI_HOME = $dotnetHome $env:NUGET_PACKAGES = $nugetHome + $env:APPDATA = $appDataHome + $env:LOCALAPPDATA = $localAppDataHome + $env:USERPROFILE = $agentHome + $env:HOME = $agentHome $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = "1" $env:DOTNET_NOLOGO = "1" Add-Content -LiteralPath $JobLogPath -Value "[$([DateTimeOffset]::UtcNow.ToString("O"))] $JobCodexCommand $($JobArgs -join ' ')" @@ -517,6 +524,7 @@ Noise and texture: Instrument ownership: - This harness is feeding the music-generator curriculum, so do not solve the target with three naked oscillators and a prayer. The scorer now records instrument-role metrics and chip-distress risk in CultCache. - If the patch has a singing, creature, or alien lead role, use the syrinx/acoustic owner: source_port kind=syrinx, pressure/opening curves, acoustic_network, and acoustic_voice, then shape it with filters/modulation. +- Acoustic/syrinx graph syntax is complete-graph syntax. Do not write isolated source_port or acoustic_network fragments. If you use source_port, you must also define the referenced acoustic_path/path, terminals/connections/radiation_port, wave_clock when needed, acoustic_network, and acoustic_voice. If you cannot build that complete scaffold from a known-valid local patch such as patches/advanced/syrinx-voice.aqua, use a formant-filtered ordinary lead for this attempt and write the missing syrinx scaffold as an Aqua gap instead of submitting a non-rendering badge. - If the patch has drums or rhythmic transients, use subtractive ownership: pitched sine/triangle body, filtered noise skin, short envelope, and pattern gate. - If the patch has a pad, bed, drone, or harmonic wash, use additive/PAD ownership: `layer engine=add` or `engine=pad`, `harmonics`, `spectrum`, and slow modulation. - Ordinary simple voices are still legal as helpers, but a candidate built mostly from square/sine/saw blips with no syrinx, subtractive drum, additive/PAD, or shaped texture role will be treated as chip-distress-risk evidence.