Release v0.3.1 — fix OSC send-failure misclassification, arm CuemsLogger on the library, converge submodules - #8
Merged
Merged
Conversation
… on start() >24h MTC continuity baseline. The fade engine consumes the QF tick callback (MtcTickSource -> callbackMs); the unified receiver makes callbackMs continuous past 24h so fades keep their phase across the rollover. Add MtcReceiver::resetWrapOffset() in MtcTickSource::start() before constructing the receiver, so a tick source restarted in-process after a >24h run starts the new session at 0 (the wire-driven reset can't catch a graceful restart's small backward delta). 9/9 unit tests pass incl. test_mtc_tick_source. Plan 3d.
rc1 + Plan 5 Phase A hardening (strict-< resync tolerance, test-state timeout reset). API-identical to rc1 (no signature change); the soak ships this commit.
… cuems-RELATIONS index
…rame-reset) Working tree submodule was already checked out at 59fc76e (per CLAUDE.md's documented pin) but the last commit on this branch had re-pinned the recorded pointer to 8a30d05, leaving the two out of sync.
Debugging session on the cuems-engine side traced silently-dying fades back to FadeMotion::evalAndSend treating lo_send()'s bytes-sent return value as a failure indicator (ret != 0) instead of checking for < 0.
…nd-fullframe-reset)" This reverts commit bc3fb0d.
…nd (v0.3.1) T069 — liblo's lo_send() returns bytes-sent (positive, non-zero) on success and -1 on failure, never 0. FadeMotion::evalAndSend checked `ret != 0`, which misclassified every successful send as a failure and killed any fade with duration_ms > ~25ms once kOscFailureThreshold (5 consecutive ticks) was reached — sending only a handful of ticks near start_value before removal. The existing test suite didn't catch this because every OscSendFn mock used the opposite POSIX-style "0 = success" convention; nothing exercised a real liblo-style positive return. - src/motion/FadeMotion.cpp: `ret != 0` -> `ret < 0`. - src/motion/FadeMotion.h, MotionRegistry.h: document the OscSendFn return-value contract to prevent recurrence. - src/motion/FadeMotion.cpp: WARNING log on individual send failures. - src/motion/MotionRegistry.cpp: DEBUG log when a motion recovers from a transient failure streak. - tests/test_fade_motion.cpp: regression test with a liblo-style mock (returns 20) asserting the fade survives kOscFailureThreshold ticks. - CMakeLists.txt, debian/changelog, CHANGELOG.md: bump 0.3.0 -> 0.3.1. See specs/planning/T069-fix-osc-send-failure-misclassification.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
NOT MERGED, NOT DEPLOYED — authored on a branch for review at your convenience (active work is ongoing in this repo). gradient-motiond's daemon-side logging already works on the fleet (syslog, real priorities); this fixes the LIBRARY half. Root cause: HAVE_CUEMS_LOGGER was defined on mtcreceiver and the gradient-motiond executable but NOT on the gradient_motion static library, so every GME_LOG_* compiled into the library fell back to logging.h's std::cerr emitter — stamped PRIORITY=6 under systemd, invisible to `cuems-logs -e` — and GradientEngine.cpp, listed in BOTH targets, compiled twice with two different logging backends. That unavailable macro layer is also why src/motion/* hand-rolled fprintf(stderr, "WARNING ...") lines. * src/CMakeLists.txt: define + link PUBLIC on gradient_motion, guarded by ENABLE_CUEMS_LOGGER AND TARGET cuemslogger (covers BUILD_DAEMON=OFF). PUBLIC kills the dual-backend hazard: daemon and the test binaries now share one backend. Side effect to be aware of: ctest output goes to syslog when cuemslogger is enabled. * Root CMakeLists: drop the now-redundant PRIVATE arming on the daemon (it inherits via the library's usage requirements). * Hand-rolled fprintf/cerr sites -> GME_LOG_*: MotionFactory (unknown curve, lo_address_new failure -> WARNING; START_CROSSFADE stub -> INFO), MotionRegistry (crossfade drop -> INFO, cancelMotion miss -> WARNING), CurveFactory (unknown curve -> WARNING). * OscServer: liblo errorHandler + callback exceptions -> GME_LOG_ERROR; the two startup-aborting failures (UDP bind, lo_server_thread_start) -> GME_LOG_CRITICAL. These bypassed the GME_LOG_* layer the same file uses everywhere else. * GradientEngine::onTick (MIDI thread, 100 Hz) deliberately untouched — already level-gated and queue-pop-only. Verified: full build (daemon + tests), ctest 9/9 passed, libgradient_motion.a now resolves CuemsLogger symbols.
…eads cuemslogger c39e2d8 -> 6463cd9: master now carries the anton closelog()-in-setNewSlug memory fix and the audioplayer header fix (ancestry-gated before any consumer bumped: git merge-base --is-ancestor 30c9cb4 6463cd9). mtcreceiver 8a30d05 -> 20ab95b: same 24h-continuity line this branch's base already tracks, plus the journald-aware fallback stub (with logDebug and a shared multi-header guard) and the MTC-RX FullFrame diagnostic at debug priority. This repo builds mtcreceiver with HAVE_CUEMS_LOGGER, so it uses the real logger path; run ctest after merging as the base branch intended.
…tion The packaging BUILD stage could not run reliably on GitHub Actions or any virtualized runner: - dh_auto_test ran the full ctest suite, including the latency-budgeted test_motion_registry_bench and bench_osc_latency, which miss their p99 budgets under neighbour load on shared runners. The MTC tests also want an ALSA sequencer a container may not expose, and bench_osc_latency writes its results back into tests/bench_results/, dirtying the source tree mid-package-build. - -DBUILD_TESTS=OFF in debian/rules was a dead flag: no such option existed in CMakeLists.txt, so every packaging build silently compiled the entire test suite. - A checkout without submodules failed late with CMake's FATAL_ERROR rather than a legible message. Changes: - debian/rules: override_dh_auto_test skips the suite in the BUILD stage (TEST/COVERAGE are separate upstream pipeline stages). Escape hatch RUN_DEB_TESTS=1 forces it locally; DEB_BUILD_OPTIONS=nocheck still wins. override_dh_auto_configure guards for the mtcreceiver/cuemslogger submodules and fails up front with the remedy. - CMakeLists.txt: add a real BUILD_TESTS option, default ON so plain `cmake ..` and the CI TEST/COVERAGE stages are unaffected, guarding enable_testing() and add_subdirectory(tests). Verified: default configure still yields 9 tests; -DBUILD_TESTS=OFF yields 0. Full dpkg-buildpackage -b -us -uc exits 0, compiles no test binaries, and the extracted binary reports gradient-motiond 0.3.1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Plans the TEST -> COVERAGE -> DOCUMENT -> BUILD -> DEPLOY pipeline, replacing
the two disconnected workflows (tests.yml, docs.yml) with a gated suite that
also covers the currently-absent BUILD and DEPLOY stages.
Records the repo-specific constraints the implementation must respect, so
they are not re-derived: the two latency-budgeted benchmarks must be collected
rather than gated on; test_motion_registry_bench is mislabelled "integration"
so label-based gating silently does nothing; bench_osc_latency writes into the
source tree; the .deb must be built in a debian:bookworm container because
${shlibs:Depends} otherwise stamps Ubuntu-flavoured dependencies into a package
destined for Debian nodes.
Leaves three decisions open for confirmation: the DEPLOY target (no APT repo
infrastructure exists in the ecosystem, so GitHub Releases is the assumed
artifact of record), the workflow filename (README badges are pinned to
tests.yml), and Pages deploy ownership.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fix(log): arm CuemsLogger on the gradient_motion library; route stdio bypasses
An audit of the libnng dependency found it is already fully gone: no nng_* calls or includes, no find_package(nng) in any of the four CMakeLists, no libnng-dev in debian/control Build-Depends or the CI apt lists, and `ldd` on the built daemon shows only liblo. It was removed in 538d992 (v0.3.0, spec 007-osc-input-transport), which replaced the NNG bus client with a localhost UDP OSC listener. libnng is not even installed on the dev machine and the daemon builds and links fine. What remained were stale mentions that misdescribe the current design: - CLAUDE.md still claimed the daemon "talks to the CUEMS engine over the NNG bus (libnng)" and listed NNG 1.10.1 as an active technology. This is the file seeding every session's context, so it was the highest-value fix. Replaced with the OSC transport description and a 007 entry that records the supersession explicitly -- a future reader hitting the NNG references still in specs/005-* needs to know they are historical. - CLAUDE.md also said "FadeRegistry map + fixed SPSC status queue". The type is MotionRegistry, and the status queue was the NNG-side one that was removed; the surviving SPSC queue carries inbound commands. Comments were generalized rather than merely de-NNG'd, so they survive a future transport swap: - src/signal/LockFreeQueue.h: producer is "the inbound command thread" (parenthetically, the liblo OSC server thread today), not "the NNG receive thread". - daemon/comms/OscServer.h: "matches NngBusClient convention" referenced a deleted file; now "the convention for daemon-specific inbound-transport clients". - src/engine/GradientEngine.cpp: "no NNG" -> "no outbound channel". Deliberately left as historical record: specs/** (including the 006 quickstart's libnng-dev apt line), .cursor/plans/, CHANGELOG.md, CONTRIBUTORS.md, debian/changelog, debian/copyright, and the README release-notes entries. The two test-file comments explaining the local StatusEvent struct also stay -- that rationale is still live. Comment-only in the code; the daemon rebuilds and links clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…hreshold-t069 Brings in the NNG-reference cleanup (ff44b5e), which belongs with the CLAUDE.md documentation work on chore/bump-mtcreceiver-24h rather than with the T069 OSC send-failure fix.
An audit of the libnng dependency found it is already fully gone: no nng_* calls or includes, no find_package(nng) in any of the four CMakeLists, no libnng-dev in debian/control Build-Depends or the CI apt lists, and `ldd` on the built daemon shows only liblo. It was removed in 538d992 (v0.3.0, spec 007-osc-input-transport), which replaced the NNG bus client with a localhost UDP OSC listener. libnng is not even installed on the dev machine and the daemon builds and links fine. What remained were stale mentions that misdescribe the current design: - CLAUDE.md still claimed the daemon "talks to the CUEMS engine over the NNG bus (libnng)" and listed NNG 1.10.1 as an active technology. This is the file seeding every session's context, so it was the highest-value fix. Replaced with the OSC transport description and a 007 entry that records the supersession explicitly -- a future reader hitting the NNG references still in specs/005-* needs to know they are historical. - CLAUDE.md also said "FadeRegistry map + fixed SPSC status queue". The type is MotionRegistry, and the status queue was the NNG-side one that was removed; the surviving SPSC queue carries inbound commands. Comments were generalized rather than merely de-NNG'd, so they survive a future transport swap: - src/signal/LockFreeQueue.h: producer is "the inbound command thread" (parenthetically, the liblo OSC server thread today), not "the NNG receive thread". - daemon/comms/OscServer.h: "matches NngBusClient convention" referenced a deleted file; now "the convention for daemon-specific inbound-transport clients". - src/engine/GradientEngine.cpp: "no NNG" -> "no outbound channel". Deliberately left as historical record: specs/** (including the 006 quickstart's libnng-dev apt line), .cursor/plans/, CHANGELOG.md, CONTRIBUTORS.md, debian/changelog, debian/copyright, and the README release-notes entries. The two test-file comments explaining the local StatusEvent struct also stay -- that rationale is still live. Comment-only in the code; the daemon rebuilds and links clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…0.3.1 Integrates the T069 work onto the v0.3.1 line: - fix: OSC send-failure misclassification in FadeMotion::evalAndSend (lo_send returns bytes-sent, not 0, on success), plus the 0.3.0 -> 0.3.1 version bump across CMakeLists, CHANGELOG and debian/changelog. - build(deb): runner-safe packaging (dh_auto_test skipped in the BUILD stage, real BUILD_TESTS option, submodule preflight guard). - docs(planning): T069 and T070 plans. Submodule pins resolve to this branch's converged heads (cuemslogger 6463cd9, mtcreceiver 20ab95b) -- the topic branch's re-pin to 59fc76e was reverted in d9e470d, so it contributes no submodule change.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Integration branch for v0.3.1, collecting the T069 fade-killing bug fix, the
library half of the logging backend (PR #7), runner-safe packaging, and two
submodule convergences.
integration/v0.3.1→main.The headline item is a production bug: every fade longer than ~25 ms was being
silently killed mid-flight.
Contents
fix: OSC send-failure misclassification (T069) —
bedcc3fliblo'slo_send()returns the number of bytes sent (positive, non-zero) onsuccess and
-1on failure — never0.FadeMotion::evalAndSendcheckedret != 0, so it counted every successful send as a failure. OncekOscFailureThreshold(5 consecutive ticks) was reached the motion was removed,meaning any fade with
duration_msgreater than ~25 ms emitted only a handful ofticks near
start_valueand then stopped. Changed toret < 0.The test suite missed this because every
OscSendFnmock used the oppositePOSIX-style "0 = success" convention; nothing exercised a real liblo-style
positive return. The regression test added here uses a liblo-style mock
(returns 20) and asserts the fade survives past the threshold.
Also documents the
OscSendFnreturn-value contract on both declarations(
FadeMotion.h,MotionRegistry.h) so future callers and mocks can'treintroduce the assumption, and adds a
WARNINGon individual send failuresplus a
DEBUGwhen a motion recovers from a transient failure streak — priorvisibility was limited to the terminal
MotionError:"osc_send_failed".Version bumped 0.3.0 → 0.3.1 across
CMakeLists.txt,CHANGELOG.md,debian/changelog.fix(log): arm CuemsLogger on the
gradient_motionlibrary —ec7e812(PR #7)HAVE_CUEMS_LOGGERwas defined onmtcreceiverand on thegradient-motiondexecutable but not on the
gradient_motionstatic library, so everyGME_LOG_*compiled into the library fell back tologging.h'sstd::cerremitter — stamped
PRIORITY=6under systemd and invisible tocuems-logs -e.GradientEngine.cpp, listed in both targets, compiled twice against twodifferent logging backends. The daemon-side logging already worked on the fleet;
this fixes the library half.
Arming is now
PUBLICongradient_motion(guarded byENABLE_CUEMS_LOGGER AND TARGET cuemslogger, soBUILD_DAEMON=OFFstill works), which also removes thedual-backend hazard. Hand-rolled
fprintf(stderr, ...)/cerrsites inMotionFactory,MotionRegistry,CurveFactoryandOscServerwere routedthrough
GME_LOG_*; the two startup-abortingOscServerfailures (UDP bind,lo_server_thread_start) are nowGME_LOG_CRITICAL.ctestoutput now goes tosyslog. That's an intended consequence of
PUBLICarming (test binaries sharethe daemon's backend), but it changes local test-run ergonomics.
GradientEngine::onTick(MIDI thread, 100 Hz) was deliberately left untouched —already level-gated and queue-pop-only.
build(deb): runner-safe packaging —
9f1e0beThe packaging BUILD stage couldn't run reliably on GitHub Actions or any
virtualised runner:
dh_auto_testran the full ctest suite including the two latency-budgetedbenchmarks, which miss their p99 budgets under neighbour load on shared
runners; the MTC tests also want an ALSA sequencer a container may not expose,
and
bench_osc_latencywrites results back intotests/bench_results/,dirtying the source tree mid-package-build.
-DBUILD_TESTS=OFFindebian/ruleswas a dead flag — no such optionexisted in
CMakeLists.txt, so every packaging build silently compiled theentire test suite.
override_dh_auto_testnow skips the suite in the BUILD stage (TEST/COVERAGEare separate pipeline stages);
RUN_DEB_TESTS=1forces it locally andDEB_BUILD_OPTIONS=nocheckstill wins.override_dh_auto_configureguards forthe submodules and fails up front with the remedy instead of hitting CMake's
FATAL_ERRORlate.BUILD_TESTSis now a real option, defaultON, so plaincmake ..and the CI TEST/COVERAGE stages are unaffected.chore(submodules): converge on reconciled heads —
adb7ad0Both bumps are fast-forwards from what
maincurrently pins:maincuemsloggerc39e2d86463cd9mtcreceiver59fc76e20ab95bcuemsloggerpicks up thecloselog()-in-setNewSlugmemory fix (30c9cb4)and the audioplayer header fix.
mtcreceiverbringsmainonto the 24h-continuity line: >24hmtcHeadcontinuity (
7e9b81d), the resync-tolerance hardening (8a30d05), thehold-QF-timebase-on-periodic-full-frame-resync fix (
aa44894), plus thejournald-aware fallback stub and the MTC-RX FullFrame diagnostic at debug
priority.
This repo builds
mtcreceiverwithHAVE_CUEMS_LOGGER, so it uses the reallogger path.
docs —
e121fd9,2454c2f,0285507,1d4b235,c1ebd03CLAUDE.mdrestructured, with component detail extracted from thecuems-RELATIONSindex and thegradient-motiond↔ engineGradientClientOSC contract documented.
libnnghas been fully gonesince v0.3.0 (
538d992) — nonng_*calls, nofind_package(nng), absentfrom
debian/controland the CI apt lists — butCLAUDE.mdstill describedthe daemon as talking over the NNG bus and listed NNG 1.10.1 as an active
technology. Inbound-transport comments in
LockFreeQueue.h,OscServer.handGradientEngine.cppwere generalised rather than merely de-NNG'd, so theysurvive a future transport swap. Historical records (
specs/**,CHANGELOG,debian/changelog) were deliberately left intact.specs/planning/. T070 plans thelayered TEST → COVERAGE → DOCUMENT → BUILD → DEPLOY CI suite and is planning
only — no workflow changes ship in this PR.