Skip to content

howl_guard: a LOST in probation is a strike, default on (cabin's duck cycle); TwoMicSmoke honours MUTAP_SLOW_THREADS - #88

Merged
tap merged 4 commits into
mainfrom
feat/guard-probation-strike
Oct 9, 2026
Merged

tap merged 4 commits into
mainfrom
feat/guard-probation-strike

Conversation

@tap

@tap tap commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Two leftovers from the safety layer, plus two HANDOFF items from #87.

1. guard_policy::lost_in_probation_strikes, default on

Under F → 2F, cabin's verdict holds ok while the mic is ducked. It therefore releases on the walk path, and PR C's re-arm hold cannot reach it. The design note's alternative, counting a LOST in OPEN within probation as a strike, is now a policy option (include/mutap/howl_guard.h: one branch in OPEN, copied through set_policy(), the field appended after cap_db). It is on by default. Tim's decision (2026-10-09): the cabin is the karaoke scenario itself; 4 ducks and then a stable capped level beat 19 ducks in two minutes; and the 3 dB / 60 s back-off elsewhere is acceptable. The bar first set for it (≤ 1 LOST-duck a run in cabin) is not met, and no rule that needs three strikes to latch can meet it.

All numbers: macOS 15.7 x86_64 (i9-8950HK), AppleClang 17, Release, double, 4 threads.

Headline (default policy; option off in brackets where it differs)

Claim (the 40 dB rule is the howl oracle) Gated rows (cabin, mt5) MUTAP_SLOW sweep (six rooms, five seed sets)
Guarded cold starts that reached the 40 dB rule 0 of 126 0 of 690
… at the canceller's limit + 6, unguarded twins 575 blocks, 3 of 6 runs —
Declaration with the backing track, median 1.77 to 2.07 s 1.76 to 2.11 s
Declaration without it 0 runs: with the cap all reach OPEN_CAPPED at 10.00 s; without it all 18 stay in ARMING 0 runs: 180 of 180 through the cap; without it 90 of 90 in ARMING for all 20 s
Walk at exact − 6: release − misalignment-oracle reconvergence, median [min]; strikes 1.59 s [1.38], 0 early of 3; 3 (option off: 0) 1.60 s [1.38], 0 early of 6; 6 (off: 0)
Walk at the limit − 6, factory thresholds: ducks / howl blocks 0 of 12 / 0 0 of 30 / 0
Walk at the limit − 6, soundcheck thresholds: ducked; release − reconvergence, median [min]; early; howl blocks 12 of 12; 1.74 s [1.63]; 0; 0 30 of 30; 1.74 s [1.63]; 0; 0
Stable material at the limit − 6, soundcheck thresholds: runs with a duck 0 of 8 0 of 180
Walk at the limit + 6 (rehearsal → hall): howl blocks guarded / unguarded 0 / 1203 —
F → 2F: howl blocks; LOST-ducks after the timer re-arm 0; 0 in 8 runs (PR B: 16 in 6 of 8) 0; 0 in 30 runs (PR B: 47 in 20 of 30; option off: 1 in 1 of 30)
F → 2F, cabin (walk-path releases): LOST-ducks a run, median; runs latched 4 in 30 s; 1 of 2 (option off: 4; 0) 3 in 30 s, 4 of 5; 4 in 120 s, 5 of 5 latched at the cap, 0 ducks after (off: 4 and 19 a run, none latched)
F → 2F, the other rooms: restore level at the end; timer re-arm −3.00 dB; 10.00 s (option off: 0.00; 5.00) −3.00 dB at 30 s, 0.00 at 120 s; 10.00 s (off: 0.00, mt5 and mt9 −3.00; 5.00)
Two mics, one forced: first TRIP on the wrong mic alone; howl blocks 0 of 8; 1 1 of 20; 3
Stable material at the limit − 6: ducks off a loop-born burst 0 in 24 runs 0 in 180 runs
Dattorro in the loop: voice 0.5 s after a trip, with / without the bus stage −66.53 / −47.13 dB —
Cost per block, one mic, float / double 0.57 / 0.84 % of a canceller —

Cabin, option off → on

Rows LOST-ducks after the change (median a run [max]) Strikes at the end Runs latched Restore level at the end, median [min] (dB) Gain at the end, median (dB) Time ducked or releasing Howl blocks
gated, 2 runs, 30 s 7 (4 [4]) → 7 (4 [4]) 0 → 5 0 → 1 of 2 0.00 [0.00] → −6.00 [−9.00] 0.00 → −26.00 0.56 → 0.65 0 → 0
sweep, 5 runs, 30 s 18 (4 [4]) → 17 (3 [4]) 0 → 14 0 → 4 of 5 0.00 [0.00] → −9.00 [−9.00] −5.47 → −26.00 0.55 → 0.59 0 → 0
sweep, 5 runs, 120 s 72 (19 [22]) → 19 (4 [4]) 0 → 15 0 → 5 of 5 (LATCHED a median 23.27 s after the change, 0 ducks after it) 0.00 → −9.00 0.00 → −11.89 (the cap) 0.49 → 0.12 0 → 0

What else the default changes (off → on)

Rows Runs off → on
F → 2F, the other five rooms (sweep, 30 s) 25 one strike from the change's own LOST (inside the cold start's probation): level 0.00 → −3.00 dB, re-arm 5.00 → 10.00 s, time ducked or releasing 0.26 → 0.51; LOST-ducks unchanged, TRIP-ducks fewer (mt5 4 → 2, mt9 3 → 0); decayed by 120 s
Walks at exact − 6, gated / sweep 12 / 30 same ducks and release timing; each LOST-duck a strike (0 → 3 / 0 → 6); studio → rehearsal's sweep minimum 1.44 → 1.46 s
Song gap, gated 6 the gap's LOST is a strike (0 → 3 a room, level −3.00 dB, the re-arm after 10 s instead of 5); mt5's A′ maximum −15.11 → −15.10 dB; still 3 of 3 OPEN when the singer returns, 0 howl
Cold start (126 gated, 690 sweep), stable material (24 / 180), two mics (8 / 20), soundcheck rows, walks at the limit − 6 and above the limits — identical to the last printed digit

docs/howl-guard.md: the main tables are now the default's. The F → 2F section leads with the option-on rows (gated, and the sweep at 30 s and 120 s). PR C's re-arm-hold tables are kept as "measured with the option off". The new section "A LOST in probation as a strike" holds every off → on comparison and the decision. HANDOFF item 12 is updated.

Gates: HowlGuardHost.LouderCouplingRearms runs the default first and still gates both ways. Every run ducks, 0 howl blocks, at least 4 of 8 re-armed, and at most 4 LOST-ducks after a re-arm. With the option on, cabin must take at least one strike (measured 5), and no other room may end below −6 dB (measured −3.00). Nothing is gated on cabin's duck count. The state-machine suite gains LostInProbationIsAStrikeWhenEnabled (exact ticks, float and double; the float half runs on both emulated selections). The song-gap row now prints strikes and the restore level.

2. TwoMicSmoke (and TwoMicLoop.DryTwoMicLimitIsTheSummedPaths) honour MUTAP_SLOW_THREADS

These tests spawned 5 and 4 threads regardless of the setting. They now use a worker pool sized the way test_howl_guard_host.cpp sizes it: at most 4 by default, overridden by MUTAP_SLOW_THREADS. The printed numbers are identical before and after, both by default and with MUTAP_SLOW_THREADS=1 (diff of the outputs is empty; 17.8 s on 1 thread against 7.1 s by default). The smoke comment now quotes today's unison numbers: uncertainty −15.34 .. −10.65 dB, medians −11.18 / −13.60, misalignment +6.05 .. +14.61. They had moved from the 2026-09-30 numbers, and the log does not single out the change that moved them; the speech rows read as they did then.

3. HANDOFF item 8: two open issues found by #87

bench/bench_aec.cpp fails -DMUTAP_WERROR=ON on AppleClang (-Wsign-conversion, lines 95 / 115 / 134 / 149). Separately, howl_detail::run_bank's bit-identity has not been compared under the Cortex-M / Hexagon toolchains.

Verification (rebased on 00590e5)

  • cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DMUTAP_WERROR=ON; ctest --test-dir build: 100% tests passed, 0 tests failed out of 422 (29 skipped: the MUTAP_SLOW sweeps and PortableRandom), 3419.00 s; mutap_fingerprint passed.
  • Gated HowlGuardHost.* on the default-on build: 16 of 16 passed, 755.91 s on 4 threads. Every table reads as the scratch on-build's, to the last printed digit. After the rebase, LouderCouplingRearms and SongGapDoesNotRestart were re-run and are unchanged.
  • State-machine suite (howl_guard_test*, validation, RT contract): 42 of 42 passed.
  • HowlGuardSweep.LouderCoupling (30 s and 120 s, on and off): 974.9 s. The Walks, ColdStart and TwoMicsAndAudibleCost sweeps were run on a scratch build with the option defaulted on (287 s, 2051 s, 619 s) and compared with the option-off build.
  • pre-commit run --files on the branch's files: passed. scripts/tidy.sh on the three changed TUs: clean. A direct clang-tidy-18 -p build-tidy on the same TUs (the .clang-tidy header filter covers include/ and tests/): 0 warnings. The guard is in no fingerprint or icount workload.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DDhgJqxUsdmWPKh6pKrV5A

tap and others added 4 commits October 9, 2026 09:45
…; ships off

A policy option: a LOST in OPEN within probation_s is a strike, as a TRIP
there is (the safety design note's alternative for F -> 2F). Measured on
the live loop, off -> on (macOS x86_64, AppleClang 17, Release):

- Cabin under F -> 2F, 30 s: 7 -> 7 LOST-ducks in the 2 gated runs, 18 ->
  17 in the 5 sweep runs (median 4 -> 3 a run). Over 120 s: 72 -> 19
  (median 19 -> 4 a run); every cabin run latched at the cap (-11.89 dB)
  a median 23.27 s after the change and ducked no more.
- The other five rooms: one strike from the change's own LOST (inside the
  cold start's probation), level -3.00 dB, the re-arm at 10.00 s instead
  of 5.00; no new duck; 0 howl blocks.
- Cold start (126 gated, 690 sweep runs), stable material, two mics,
  soundcheck rows: identical. Walks at exact - 6: same ducks and release
  timing, each LOST-duck a strike (0 -> 3 gated, 0 -> 6 sweep).

The bar for default ON was at most 1 LOST-duck a run in cabin; it is not
met, so the default stays off (behaviour unchanged). LouderCouplingRearms
runs every row both ways; the LouderCoupling sweep adds the 120 s rows.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDhgJqxUsdmWPKh6pKrV5A
They spawned one thread per row or seed set (4 and 5) regardless. Now a
worker pool sized as test_howl_guard_host.cpp sizes it: at most 4 by
default, MUTAP_SLOW_THREADS overrides. The printed numbers are identical
before and after, by default and on 1 thread.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDhgJqxUsdmWPKh6pKrV5A
…e default

Tim's decision (2026-10-09): the cabin is the karaoke scenario itself; 4
ducks then a capped level beats 19 ducks in two minutes, and the 3 dB /
60 s back-off elsewhere is acceptable. The policy default flips to true.

docs/howl-guard.md and the gated comments now describe the default: the
F -> 2F main tables are the option-on rows (gated, and the sweep at 30 s
and 120 s), the PR C re-arm-hold tables stay as the option-off
alternative, and the walks at exact - 6 and the song gap carry their
strike counts (3 / 6 walk strikes, 3 gap strikes a room, mt5's gap A'
maximum -15.10 dB; 0 with the option off). The gap row prints strikes and
the restore level. Re-run on the default-on build: every gated
HowlGuardHost row reads as the scratch on-build's, 16 of 16 passed,
755.91 s on 4 threads.

LouderCouplingRearms runs the default first and still gates both ways.
TwoMicSmoke's comment quotes today's unison numbers (-15.34 .. -10.65 dB,
medians -11.18 / -13.60), which had moved from the 2026-09-30 ones by a
change the log does not single out.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDhgJqxUsdmWPKh6pKrV5A
bench_aec.cpp's -Wsign-conversion under -DMUTAP_WERROR=ON on AppleClang
(CI's bench-smoke builds without -Werror), and run_bank's bit-identity
not compared under the Cortex-M / Hexagon toolchains.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDhgJqxUsdmWPKh6pKrV5A
@tap
tap force-pushed the feat/guard-probation-strike branch from 975b62a to 666339b Compare October 9, 2026 15:45
@tap tap changed the title howl_guard: a LOST in probation is a strike (cabin's duck cycle); TwoMicSmoke honours MUTAP_SLOW_THREADS howl_guard: a LOST in probation is a strike, default on (cabin's duck cycle); TwoMicSmoke honours MUTAP_SLOW_THREADS Oct 9, 2026
@tap
tap merged commit ea55d17 into main Oct 9, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant