Repository navigation
howl_guard: re-arm hold, soundcheck calibration, mandatory cap (phase 2 item 6b, PR C) - #80
Merged
Merged
Conversation
tap
force-pushed
the
feat/howl-guard-policy
branch
from
October 1, 2026 16:12
5555843 to
5eaa678
Compare
… 2 item 6b, PR C) - After a timer re-arm, LOST arms only once the verdict has been ok for release_hold_s (not on the first ok block). F -> 2F, LOST-ducks after the re-arm: gated 16 in 6 of 8 runs -> 0; sweep 47 in 20 of 30 -> 1. Walk and cold-start rows unchanged to the last printed digit. - Soundcheck sampler: calibrate_begin() / calibrate_end(apply) over a fixed 0.1 dB histogram per mic (8 KB, allocated by the constructor); per-mic thresholds = 30 s median + cal_d_margin_db (4) / cal_a_margin_db (3), measured with shadow guards over 180 stable and 30 walk runs (D + 3 the smallest with 0 stable ducks). Applied: 0 of 180 stable runs ducked, 30 of 30 walks at the limit - 6 seen and released after the misalignment oracle (median 1.74 s, min 1.63). - cap_db is mandatory for opening without a declaration; removing the cap now also re-arms a mic latched before any declaration. Without a cap 90 of 90 track-off sweep runs stay in ARMING for all 20 s. - New gated rows ColdStartWithoutACapStaysArmed, SoundcheckCalibrationOnStableMaterial, SoundcheckCalibrationSeesAWalk; sweeps CalibrationMargins, CalibrationApplied; LouderCoupling split out of Walks. docs/howl-guard.md, HANDOFF item 12, README. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDhgJqxUsdmWPKh6pKrV5A
…gates ducks off a burst - HowlGuardHost.CostPerBlock: the wall-clock ratio moves by 4-8x between hosts (Intel 0.57 / 1.09 % float, Linux GCC CI 4.61 / 6.86 % float and 4.59 / 7.15 % double, failing the 4.5 / 7 % bounds by 0.001). Print and RecordProperty the ratios; gate < 25 % only. - HowlGuardHost.ColdStartWithoutBackingTrack: the held note's ducks after OPEN_CAPPED are classified against the burst oracle (Intel: 2 of 6 runs, both ducks on a loop-born burst, 0 off; macOS arm64 CI: 5 of 6 runs, which failed the <= 4 bound). The duck count is reported; gated: howl blocks, the OPEN_CAPPED time, runs with a duck off any burst <= 6 of 18. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDhgJqxUsdmWPKh6pKrV5A
tap
force-pushed
the
feat/howl-guard-policy
branch
from
October 1, 2026 22:16
8c2e368 to
1798fce
Compare
tap
added a commit
that referenced
this pull request
Oct 2, 2026
…nstead of apt gcc-arm-none-eabi Ubuntu's gcc-arm-none-eabi pulls the 463 MB libstdc++-arm-none-eabi-newlib package from the runner's Azure mirror. That fetch stalled past the 15-minute step limit three times on 2026-10-01 (#81 twice, #80 once) after eating two 45-minute legs on #67. Both Cortex legs now restore Arm's arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi from the Actions cache, or download it (179 MB from Arm's CDN, curl with retries, verified against Arm's published sha256) on a miss, and put its bin on PATH; only qemu-system-arm still comes from apt. It is the same upstream release Ubuntu noble packages (15:13.2.rel1-2), so the fingerprint and icount gates on this PR's own run are the check that the numerics did not move. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDhgJqxUsdmWPKh6pKrV5A
tap
added a commit
that referenced
this pull request
Oct 9, 2026
The guard costs ~4x on the Linux GCC runner what it costs on the Intel Mac (HowlGuardHost.CostPerBlock's comment, from PR #80's CI) while the canceller runs ~2x faster there. To localize that in one CI run the detector bench now times one block's stages separately, float and double, through the library's own code: howl_bank resonators + envelopes (a detector whose tick never comes, so process_block runs run_bank alone) howl_tick howl_detail::decide on envelope snapshots howl_tick_logs decide()'s 66 std::log10 calls alone howl_fit one band's least-squares fit howl_readouts harmonic / subharmonic readouts + 32 band_level_db guard policy howl_guard update() + apply(), the detector not run guard m1 / m2 the whole guard per block, as CostPerBlock times it The existing b64_f64 / b64_f32 cases are unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDhgJqxUsdmWPKh6pKrV5A
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.
PR C of the safety layer (phase 2 item 6b), stacked on #78 (
feat/howl-guard, 55a62fd). Spec: the safety design note §4c, the decisions of 2026-10-01: (1) the re-arm hold, (2) a soundcheck calibration step, (3)cap_dbmandatory for opening without a declaration.What lands
howl_guard.h). After a timer re-arm, the verdict trigger (LOST) arms only once the verdict has been continuously ok forrelease_hold_s(1.5 s, the same hold as a release). Before, the first ok block re-armed it. Walk releases and every other path are unchanged.calibrate_begin()samples each mic's D and A′ once per block forcalibrate_s(30 s).calibrate_end(apply)returns astd::span<const guard_calibration>, one entry per mic: blocks, median / p95 / max of D and A′, and the suggestedd_db/a_db= median +cal_d_margin_db(4 dB) /cal_a_margin_db(3 dB). Withapplyeach mic runs on its own thresholds.set_thresholds(mic, d, a)restores a stored soundcheck;clear_calibration()returns to the policy's;threshold_d_db()/threshold_a_db()read them. Applied thresholds re-arm LOST (an ok block under them first), survivereset()and are not touched byset_policy().uint32counts (8 KB) per mic, allocated by the constructor. O(1) per block, exact to the bin width for any quantile, identical on every platform, no random source. A reservoir needs a random source and an O(n) selection; P² has no error bound on the bimodal window a cold start produces.unprotectedraised and no OPEN. This PR documents the rule as mandatory and closes one hole: a mic that latched before any declaration (its floor was the cap) now re-arms when the cap is removed. Before, it kept the old cap as its floor.ColdStartWithoutACapStaysArmed,SoundcheckCalibrationOnStableMaterialandSoundcheckCalibrationSeesAWalk;LouderCouplingRearmsandColdStartWithoutBackingTracknow gate the new numbers;MUTAP_SLOWsweeps,CalibrationMargins(shadow guards over a 9 × 8 margin grid) andCalibrationApplied;LouderCouplingis split out ofWalksAndLouderCoupling, which is nowWalks.docs/howl-guard.md(new sections, every number from this PR's runs), HANDOFF item 12, the header comments, a README line.Measured (macOS 15.7 x86_64, i9-8950HK, AppleClang 17, Release, double; every run from reset)
1. Re-arm hold: F → 2F before → after
"Before" is this PR's test harness on PR B's guard (55a62fd). Same rows, same host.
Time ducked or releasing after the change, median per room, before → after: gated mt5 0.96 → 0.51, studio 0.94 → 0.26, hall 0.93 → 0.26, cabin 0.56 → 0.56; sweep mt9 0.95 → 0.77.
2. Soundcheck calibration: the margin sweep
HowlGuardSweep.CalibrationMargins: 210 runs at the canceller's limit − 6 with the backing track, a 30 s soundcheck from reset.Stable runs with a duck after the soundcheck, of 180:
Walks seen, of 30 (a duck within 5 s of the change); in brackets, runs that ducked before the walk:
2079.35 s on 4 threads.
The margins.
The soundcheck medians over the 210 runs:
What the soundcheck prints (gated row; two of its eight lines):
Applied (the live guard on its soundcheck thresholds at the default margins):
CalibrationApplied, 1227 s)Applied
d_dbacross the sweep: −8.15 to −2.25 dB;a_db: −27.55 to −22.85 dB. The duck not by LOST was a detector TRIP (mt5 → mt105).Gates, with margin:
3. Cap rule: track-off rows
20 s from reset at the limit − 6 (gated: cabin and mt5, seeds 1, 21, 41).
unprotectedfrom (s)"10.00" is block 7499, 9.9987 s.
Sweep (six rooms × five seed sets, 30 runs a row):
Defaults
None of PR B's defaults moved. New fields:
calibrate_s= 30: the protocol's window.cal_d_margin_db= 4 andcal_a_margin_db= 3: measured above.What did not separate, or could not be measured
HowlGuardSweep.OperatingPointswas not re-run: the guard is not in it.Verification
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DMUTAP_WERROR=ON && cmake --build build: clean.ctest --test-dir build --output-on-failure: 374 of 374 passed (21 skipped: the 20MUTAP_SLOWsweeps andPortableRandom.MatchesLibstdcxxBitForBit), 3143.71 s. The 16 gated guard rows took 53.09 to 88.65 s each (a first full run failedColdStartWithoutBackingTrackon my own bound, ≥ 10.0 s against block 7499 = 9.9987 s; fixed to 10 − 1.5 blocks, then the full run above).--gtest_filter='HowlGuardHost.*'): 777.37 s.MUTAP_SLOW=1 MUTAP_SLOW_THREADS=4): CalibrationMargins 2079.35 s, CalibrationApplied 1227 s, ColdStart 1890 s, Walks 245 s, LouderCoupling 88 s, TwoMicsAndAudibleCost 567 s (its two-mic and audible-cost tables match PR B's).afc_chain.his unchanged (bit-identical without a guard by construction).mutap_fingerprintpassed. No icount or fingerprint workload includesafc_chain.horhowl_guard.h.pre-commit run --files <changed>: passed.scripts/tidy.shontest_howl_guard.cpp,test_howl_guard_host.cppandtest_afc_chain.cpp: clean. A directclang-tidy-18 -p build-tidycount on each: 0 warnings.clamped_rangeiterators, as 55a62fd does. The calibrate_end / quantile loops walk iterators over each mic's histogram slice.Follow-up commit 8c2e368 (CI)
HowlGuardHost.CostPerBlock: Linux GCC CI (job 110465810942) read the guard at 4.61 / 6.86 % (float, 1 / 2 mics) and 4.59 / 7.15 % (double) of a canceller, against 0.57 / 1.09 / 0.84 / 1.73 % on the Intel Mac, failing the 4.5 / 7 % bounds by 0.001. The ratios are now printed andRecordProperty'd; the gate is < 25 % (a gross regression only).HowlGuardHost.ColdStartWithoutBackingTrack: macOS arm64 CI ducked after OPEN_CAPPED in 5 of 6 held-note runs (Intel 2 of 6) and failedduck_runs <= 4. The ducks are now classified against the burst oracle: on Intel both are within 0.5 s of a +20 dB loop-born burst (0 off a burst). Gated: 0 howl blocks, OPEN_CAPPED at the timeout (>= 10 s − 1.5 blocks, median < 11 s), runs with a duck off any burst <= 6 of 18 (Intel 0). The duck count is reported, not gated.scripts/tidy.shclean, a directclang-tidy-18count 0.🤖 Generated with Claude Code
https://claude.ai/code/session_01DDhgJqxUsdmWPKh6pKrV5A