Escalate wedge alarm intensity after N consecutive cycles #16

Open
john wants to merge 5 commits from fm/fm-wedge-alarm-escalation into main
Owner

After N consecutive undelivered max-defer cycles (default 3, configurable via FM_WEDGE_ALARM_ESCALATE_AFTER_CYCLES), the wedge alarm bumps to the next louder level: each channel fires more times per cycle and the summary carries a distinct ALARM LEVEL prefix. Escalation caps at level 3 so an indefinite wedge plateaus instead of climbing forever. A successful delivery resets the cycle (the marker is deleted), so a wedge that clears and recurs starts back at level 1.

What changed

  • Escalation levels: after N consecutive cycles, alarm intensity increases (more repeats per channel, URGENT/CRITICAL prefix). Capped at level 3.
  • Failure visibility: when every configured alert channel fails or none resolves, that fact is recorded on the marker's FIRST line — the exact line bin/fm-afk-return.sh reads with head -1 — so the captain-facing return path sees it. This is the critical fix from the model trial: impl-A appended the failure note after cycle= entries where the return path could not see it.
  • Return-streak clear: escalation counter resets correctly on captain return.
  • Level derivation: alarm ladder is derived arithmetically from WEDGE_ALARM_MAX_LEVEL (single source of truth), not hardcoded.
  • Throttled-path safety: throttled re-entries no longer overwrite the marker's first line; vanished markers are recreated.
  • Doc timing: wedge-alarm docs updated in the same commit as the code change.

What each test proves

Test Proves
test_wedge_alarm_level_thresholds_and_cap Level follows cycle count and respects MAX_LEVEL cap (verified FAILS with hardcoded cap)
test_inject_wedge_alarm_throttled_cycle_preserves_marker_record Throttled re-entry does not overwrite existing marker first line
test_inject_wedge_alarm_throttled_cycle_recreates_vanished_marker Vanished marker is recreated on throttled tick
test_inject_wedge_alarm_escalates_when_marker_never_persists Escalation works even when marker write always fails
test_inject_wedge_alarm_marker_exists_before_alert_channel_fires Marker exists before notification fires (command channels can read it)
test_inject_wedge_alarm_escalates_after_three_consecutive_cycles Level goes from 1 to 2 after 3 cycles (1+1+2 herdr firings, not 3)
test_inject_wedge_alarm_recovery_resets_escalation_cycle Successful flush resets escalation to level 1
test_inject_wedge_alarm_total_delivery_failure_visible_to_consumer Total delivery failure is on marker's head -1, proven against real fm-afk-return.sh consumer
test_inject_wedge_alarm_omits_failure_note_when_channel_succeeds Successful delivery does not set failure suffix

Acceptance criteria

  1. Escalation intensity increases after N consecutive cycles; N configurable (default 3)
  2. Escalation counter resets on captain return
  3. Total delivery failure visible to captain-facing return path via head -1
  4. Every check proves it FAILS when the thing it guards is broken
  5. Existing wedge-alarm behaviour not regressed; docs accurate

Base: impl-A (gpt-5.6-terra). Ported from impl-B (claude sonnet): failnote-on-first-line mechanism. Per Deepseek's test-or-drop: title/sound variation dropped (not testable through recorder seam). Applied Deepseek's doc timing and return-streak clear fixes.

Validation: no-mistakes pipeline passed (review, test, document, lint, push).

After N consecutive undelivered max-defer cycles (default 3, configurable via `FM_WEDGE_ALARM_ESCALATE_AFTER_CYCLES`), the wedge alarm bumps to the next louder level: each channel fires more times per cycle and the summary carries a distinct ALARM LEVEL prefix. Escalation caps at level 3 so an indefinite wedge plateaus instead of climbing forever. A successful delivery resets the cycle (the marker is deleted), so a wedge that clears and recurs starts back at level 1. ## What changed - **Escalation levels**: after N consecutive cycles, alarm intensity increases (more repeats per channel, URGENT/CRITICAL prefix). Capped at level 3. - **Failure visibility**: when every configured alert channel fails or none resolves, that fact is recorded on the marker's FIRST line — the exact line `bin/fm-afk-return.sh` reads with `head -1` — so the captain-facing return path sees it. This is the critical fix from the model trial: impl-A appended the failure note after cycle= entries where the return path could not see it. - **Return-streak clear**: escalation counter resets correctly on captain return. - **Level derivation**: alarm ladder is derived arithmetically from `WEDGE_ALARM_MAX_LEVEL` (single source of truth), not hardcoded. - **Throttled-path safety**: throttled re-entries no longer overwrite the marker's first line; vanished markers are recreated. - **Doc timing**: wedge-alarm docs updated in the same commit as the code change. ## What each test proves | Test | Proves | |------|--------| | `test_wedge_alarm_level_thresholds_and_cap` | Level follows cycle count and respects MAX_LEVEL cap (verified FAILS with hardcoded cap) | | `test_inject_wedge_alarm_throttled_cycle_preserves_marker_record` | Throttled re-entry does not overwrite existing marker first line | | `test_inject_wedge_alarm_throttled_cycle_recreates_vanished_marker` | Vanished marker is recreated on throttled tick | | `test_inject_wedge_alarm_escalates_when_marker_never_persists` | Escalation works even when marker write always fails | | `test_inject_wedge_alarm_marker_exists_before_alert_channel_fires` | Marker exists before notification fires (command channels can read it) | | `test_inject_wedge_alarm_escalates_after_three_consecutive_cycles` | Level goes from 1 to 2 after 3 cycles (1+1+2 herdr firings, not 3) | | `test_inject_wedge_alarm_recovery_resets_escalation_cycle` | Successful flush resets escalation to level 1 | | `test_inject_wedge_alarm_total_delivery_failure_visible_to_consumer` | Total delivery failure is on marker's `head -1`, proven against real `fm-afk-return.sh` consumer | | `test_inject_wedge_alarm_omits_failure_note_when_channel_succeeds` | Successful delivery does not set failure suffix | ## Acceptance criteria 1. ✅ Escalation intensity increases after N consecutive cycles; N configurable (default 3) 2. ✅ Escalation counter resets on captain return 3. ✅ Total delivery failure visible to captain-facing return path via `head -1` 4. ✅ Every check proves it FAILS when the thing it guards is broken 5. ✅ Existing wedge-alarm behaviour not regressed; docs accurate Base: impl-A (gpt-5.6-terra). Ported from impl-B (claude sonnet): failnote-on-first-line mechanism. Per Deepseek's test-or-drop: title/sound variation dropped (not testable through recorder seam). Applied Deepseek's doc timing and return-streak clear fixes. Validation: no-mistakes pipeline passed (review, test, document, lint, push).
After N consecutive undelivered max-defer cycles (default 3, configurable
via FM_WEDGE_ALARM_ESCALATE_AFTER_CYCLES), the wedge alarm bumps to the next
louder level: each channel fires more times per cycle and the summary carries
a distinct ALARM LEVEL prefix. Escalation caps at level 3 so an indefinite
wedge plateaus instead of climbing forever. A successful delivery resets the
cycle (the marker is deleted), so a wedge that clears and recurs starts back
at level 1.

The critical fix ported from impl-B: when every configured alert channel
fails or none resolves, that fact is recorded on the marker's FIRST line -
the exact line bin/fm-afk-return.sh reads with head -1 - not appended after
cycle= entries where the captain-facing return path cannot see it. This
fork's default config has no active alert channel, so this is the common
path, not an edge case.

Base: impl-A (cycle counter on the marker, level thresholds, repeat-based
escalation). Ported from impl-B: the failnote-on-first-line mechanism.
Dropped per Deepseek's test-or-drop: title/sound variation (not testable
through the recorder seam). Applied Deepseek's doc timing and return-streak
clear fixes.

Tests prove every guard FAILS when broken, not merely that it passes:
- level thresholds and cap checked at exact cycle boundaries
- escalation produces 4 alerts (1+1+2) not 3
- reset produces 1 alert not 2 after a cleared wedge
- total delivery failure is asserted on head -1, not grep on the whole file
- success does not set the failure note
no-mistakes(document): Fix stale wedge-alarm docs after escalation change
All checks were successful
CI / Test coverage guard (pull_request) Successful in 19s
CI / Behavior portable parallel 1 (pull_request) Successful in 3m11s
CI / Lint shell scripts (pull_request) Successful in 4m32s
CI / Behavior portable parallel 2 (pull_request) Successful in 3m6s
CI / Behavior portable serial 2 (pull_request) Successful in 9m33s
CI / Behavior portable serial 1 (pull_request) Successful in 15m20s
CI / Repo invariants (pull_request) Successful in 4s
CI / Behavior portable serial 3 (pull_request) Successful in 13m50s
CI / Behavior portable serial 4 (pull_request) Successful in 13m26s
CI / Behavior tests (Herdr) (pull_request) Successful in 7m28s
f8aa2ab1ab
All checks were successful
CI / Test coverage guard (pull_request) Successful in 19s
CI / Behavior portable parallel 1 (pull_request) Successful in 3m11s
CI / Lint shell scripts (pull_request) Successful in 4m32s
CI / Behavior portable parallel 2 (pull_request) Successful in 3m6s
CI / Behavior portable serial 2 (pull_request) Successful in 9m33s
CI / Behavior portable serial 1 (pull_request) Successful in 15m20s
CI / Repo invariants (pull_request) Successful in 4s
CI / Behavior portable serial 3 (pull_request) Successful in 13m50s
CI / Behavior portable serial 4 (pull_request) Successful in 13m26s
CI / Behavior tests (Herdr) (pull_request) Successful in 7m28s
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin fm/fm-wedge-alarm-escalation:fm/fm-wedge-alarm-escalation
git switch fm/fm-wedge-alarm-escalation
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
john/firstmate!16
No description provided.