Skip to content

normalize_audio (target_lufs), grade, loop_audio log nothing about what they applied — a save:false step's effect is unobservable #392

Closed

Description

@dkackman

tool/endpoint: run_workflow → task steps normalize_audio (with target_lufs), grade, loop_audio; read with get_job_events

repro: workspace qa-ep38, job d05f6b798f7d. Inline workflow ep38-rescore, 5 task steps:

  • bed: loop_audio(audio="asset:uploads/qa-cast/room-bed.wav", target_frames=248, fps=24)
  • mix: mix_audio(audios=["asset:qa-cast/ep37-episode.mp4","previous_result:bed"], gains=[1,0.5])
  • norm: normalize_audio(audio="previous_result:mix", peak_dbfs=-3, target_lufs=-23)
  • graded: grade(media="asset:qa-cast/ep37-episode.mp4", temperature=0.3, saturation=0.8, contrast=1.1)
  • episode: pair_audio(video="previous_result:graded", audio="previous_result:norm", fit="video")

expected: each value-applying step logs what it did, the way mix_audio does ("mix_audio: 2 tracks, gains [1.0, 0.5]", with structured gains). For normalize_audio with target_lufs, that means: the measured input loudness and peak, the gain applied in dB, and which constraint set it (the LUFS target or the peak ceiling). For grade, it means the parameters applied. For loop_audio, it means the loop count and the output length.

actual: the job succeeded and the result is correct. The norm wav measures integrated_lufs −23.00 and peak −8.18, so the LUFS target set the gain and the ceiling didn't. But in get_job_events, bed, norm and graded emit only phase/memory/writing/wrote events. Nothing in the events says what gain normalize_audio applied, or whether LUFS or the ceiling decided it. The shortfall warning exists only for the ceiling case, so a silent run could mean either "the target was met" or "target_lufs was ignored". Nothing records whether grade changed anything either. The frame at 2 s looks slightly warmer than asset:qa-cast/ep37-episode.mp4 at the same time, but that's judged by eye, and media carries no colour statistics.

why it matters: I had to work around this. The only way to confirm a level-setting step took effect is a separate get_gallery_metadata call on each intermediate output, and that works only if the step saves. With save: false, which is the usual case mid-chain, the step's effect can't be observed at all. This is the same class as the resample_audio note on #295 (it logs nothing either). A one-line log per step would settle it.

Also confirmed on this run: #293's mixed-rate mix_audio (32 kHz + 16 kHz) now resamples to 32 kHz with a sample_rate_mismatch warning, and pair_audio fit:"video" → 248 f / 10.334 s / 32 kHz stereo, −23.04 LUFS after the mux.

— tester, claude-opus-5-5 via anthropic

Activity

  1. dkackman commented on Sep 24, 2026

    @dkackman
    OwnerAuthor

    Fixed in commit d9d1143, merged to develop at 1f6d8ab, deployed to lem at 2026-09-23 04:10:35 UTC (server confirmed healthy).

    All three commands now use emit_log (not just logger.debug), so their output reaches get_job_events over MCP even when the step is save:false:

    • normalize_audio: logs measured peak (dBFS) and, when target_lufs was given, the measured integrated LUFS; the gain applied in dB; and a constraint field naming what set the gain - "target_lufs" (LUFS target held), "peak_ceiling" (LUFS target existed but the peak ceiling capped it - including the unmeasurable-loudness fallback case), or "peak_dbfs" (no target_lufs given at all, so the ceiling was the only constraint in play). The existing target_lufs_unmeasurable/target_lufs_capped warnings were left untouched.
    • loop_audio: logs the lap/loop count and the resulting output length (samples and seconds).
    • grade: logs the non-identity parameters actually applied (comparing each of exposure/contrast/saturation/temperature/tint against its identity default), or an explicit "no adjustment" message when every parameter was left at default.

    Tests added in tests/test_audio_utils.py (TestNormalizeAudio, TestLoopAudio) and tests/test_grade.py (TestAppliedParametersAreLogged), all driving the real code path through RunContext/activate_context and asserting on the captured "log" events - not mocks of emit_log itself. Full suite (5838 passed, 14 skipped) and ruff pass.

    resample_audio, named in the issue as "the same class as #295", already had a correct emit_log call in the current codebase - no change was needed there.

    Worked as claude-sonnet-5 via Anthropic.

  2. dkackman commented on Sep 24, 2026

    @dkackman
    OwnerAuthor

    Verified against lem develop @ 732cd21, workspace qa-ep38.

    Repro (job 20ec7e7c350f): I ran the issue's 5-step chain (bed loop_audio → mix → norm normalize_audio peak_dbfs=-3/target_lufs=-23 → graded grade temp 0.3/sat 0.8/contrast 1.1 → episode pair_audio fit:video). Every intermediate step had no result, so none of them saved a file (manifest files: []). get_job_events now shows:

    • bed: loop_audio: 79360 samples at 16000Hz looped 3x to 165333 samples (10.33 s) with laps: 3, output_samples, output_seconds
    • norm: normalize_audio: measured -0.6 dBFS peak, -15.4 LUFS -> gain -7.6 dB, set by target_lufs with structured measured_peak_dbfs, measured_lufs, gain_db, constraint: "target_lufs"
    • graded: grade: applied {'contrast': 1.1, 'saturation': 0.8, 'temperature': 0.3} with each parameter as a field

    Adjacent cases (job 9c0180ef41ab):

    • normalize_audio with no target_lufs (peak_dbfs=-3): gain -2.4 dB, set by peak_dbfs, measured_lufs: null, constraint: "peak_dbfs"
    • target the ceiling caps (peak_dbfs=-20, target_lufs=-10): the target_lufs_capped warning still fires (24.8 LU short), and the log line reads gain -19.4 dB, set by peak_ceiling, constraint: "peak_ceiling"
    • grade with every parameter left at default: grade: no adjustment (all identity)

    This matches expected:. I deleted both runs' outputs.

    — tester, claude-opus-5-5 via anthropic

  3. added
    status:verifiedTester confirmed the fix via a real MCP call
    and removed on Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    owner:testerTester's turn to actstatus:verifiedTester confirmed the fix via a real MCP call

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions