Test the five proactive rules and the restraint gate #2

Closed
claude wants to merge 0 commits from overnight/loop-rule-tests into master
Contributor

Tests only. No behaviour changed, no threshold touched.

Why. internal/loop/rules.go decides when Maven speaks to you unprompted, and it had no test file. The five rules were only covered sideways through cmd/mavend/tick_test.go. That is the least tested and most user-facing code in the repo.

Commit 1 — the five predicates. water, meal, break, service_down, netdata_critical. For each: fires when it should, stays quiet when it should not, and stays quiet on missing data. DESIGN.md calls the no-data case load-bearing ("silence on no-data is shuts up when uncertain") — all five honour it, and the gate backstops them, so there was no bug to fix.

The ops rules also get six forgery attempts: right value, wrong source. DESIGN.md says a compromised poller must not be able to forge a trigger. It cannot — including a lookalike source name like poll:uptimekuma-staging.

Commit 2 — the restraint gate. Quiet hours, presence, cooldown, snooze, calendar-busy, one-nudge-per-tick, and that a user reminder bypasses the gate ("wake me 7" has to fire at night).

Two gaps found. Both are skipped tests, not fixes — I wanted the tests to say what is actually true.

  1. Snooze does nothing at runtime. The gate reads State.SnoozeUntil, but the Gatherer hard-codes it to nil (gather.go:153). There is no store table behind it. So every snooze test passes while the real daemon can never fill the map — snooze a nudge and Maven nudges you again anyway. Filed as Vikunja #364, and it is the higher-priority one.
  2. Snooze is not applied to reminders. DESIGN.md says a reminder bypasses the gate but snooze still applies. RemindDecisions (loop.go:120) has no snooze check. Same task.

Both skipped tests are written to the intended contract, so un-skipping them is the finish line for #364.

internal/loop coverage 83.7%. Full tree green.

Tests only. No behaviour changed, no threshold touched. **Why.** `internal/loop/rules.go` decides when Maven speaks to you unprompted, and it had no test file. The five rules were only covered sideways through `cmd/mavend/tick_test.go`. That is the least tested and most user-facing code in the repo. **Commit 1 — the five predicates.** water, meal, break, service_down, netdata_critical. For each: fires when it should, stays quiet when it should not, and stays quiet on missing data. DESIGN.md calls the no-data case load-bearing ("silence on no-data is *shuts up when uncertain*") — all five honour it, and the gate backstops them, so there was no bug to fix. The ops rules also get six forgery attempts: right value, wrong `source`. DESIGN.md says a compromised poller must not be able to forge a trigger. It cannot — including a lookalike source name like `poll:uptimekuma-staging`. **Commit 2 — the restraint gate.** Quiet hours, presence, cooldown, snooze, calendar-busy, one-nudge-per-tick, and that a user reminder bypasses the gate ("wake me 7" has to fire at night). **Two gaps found. Both are skipped tests, not fixes — I wanted the tests to say what is actually true.** 1. **Snooze does nothing at runtime.** The gate reads `State.SnoozeUntil`, but the Gatherer hard-codes it to `nil` (`gather.go:153`). There is no store table behind it. So every snooze test passes while the real daemon can never fill the map — snooze a nudge and Maven nudges you again anyway. Filed as Vikunja #364, and it is the higher-priority one. 2. **Snooze is not applied to reminders.** DESIGN.md says a reminder bypasses the gate but snooze still applies. `RemindDecisions` (`loop.go:120`) has no snooze check. Same task. Both skipped tests are written to the intended contract, so un-skipping them is the finish line for #364. `internal/loop` coverage 83.7%. Full tree green.
claude added 2 commits 2026-07-31 00:15:51 +02:00
Table-driven tests for water, meal, break, service_down and netdata_critical,
straight against the predicate with a fake State. Reviewers: the no-data rows
(every rule must stay quiet when its key is missing) and the ops forgery rows,
where a fact with the right value but the wrong source must be refused.
No rule fired on missing data, so no fix was needed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
Pins the conservative side of Gate(): quiet hours, away, calendar-busy,
cooldown and snooze, plus one nudge per tick at max severity. Reviewers: the
two skipped tests at the bottom are real gaps, not flakes. Reminders ignore
snooze (loop.go:120) and the Gatherer never fills SnoozeUntil (gather.go:153),
so snooze does nothing at runtime. No behaviour was changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
Owner

Superseded by #47, which landed this whole stack on master as one reviewed integration merge. This PR head is an ancestor of master — its commits are in, nothing here is lost. Closing as merged-by-proxy rather than merged, since the merge came in through #47.

Review threads on this PR were answered or acted on before the merge; the Russian wording fixes went in as #48.

Superseded by #47, which landed this whole stack on master as one reviewed integration merge. This PR head is an ancestor of master — its commits are in, nothing here is lost. Closing as merged-by-proxy rather than merged, since the merge came in through #47. Review threads on this PR were answered or acted on before the merge; the Russian wording fixes went in as #48.
kami closed this pull request 2026-07-31 20:21:30 +02:00

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kami/Maven#2