budgetState's "замер устарел" reason compared measurement time and
budget-renewal time at day-only precision (dayOfSafe). For five_hour
windows this made the two dates print identically even though the
underlying instants differ, producing a nonsensical owner-facing
message ("measured on 2026-08-03, renewed on 2026-08-03").
Fix: moskovskoeVremya now also returns minute (same Intl call, single
source of Moscow time, no second implementation). New slovoMomenta()
prints hour:minute only when the two compared moments fall on the same
calendar day (data-driven rule, not a per-window-kind special case) —
seven_day* windows stay day-only since their days normally differ.
Added a class-wide sensor: budget.test.mjs now scans every
budgetState reason an owner actually sees (gates.mjs / cli.mjs consume
it directly) for two named quantities colliding verbatim, across all
five rate-limit kinds and day-boundary edge cases. Verified red before
the fix (both dates rendered "2026-08-03"), green after.
tools/night/budget.mjs tools/night/budget.test.mjs
Найдена беда 03.08.2026: spawn_budget_per_shift в файле стоял 40, хотя решение 9а
протокола (docs/grilling/2026-08-02-nastroyki-nochi-i-ramka-deneg.md, строка 246)
называет 20 — предел рождений работников был вдвое слабее обещанного владельцу,
и ничто в коде это не сверяло.
tools/night/sverka-nastroek-s-resheniyami.mjs — сторож с тремя исходами на настройку
(сходится молчит / расхождение в сторону БОЛЬШЕЙ траты или СЛАБЕЕ защиты падает вслух /
любое другое расхождение говорит словами, но не падает), плюс отдельные падения на
отсутствующую настройку и на мусор в записи. Список решений явный, с номером решения
и строкой протокола на каждую настройку. bez_doli_ne_zapuskat и probe_budget_window_hours
намеренно не в списке — это решения самой затеи, а не владельца.
night.config.json (не в git — закрыт .gitignore строкой 241 по решению владельца):
spawn_budget_per_shift приведён к 20 по решению 9а той же правкой в этом угле.
The check "prichina padeniya dohodit do vladeltsa KAK BYLA" called
createWorkspace() twice (once per regex) instead of once. This caused two
bugs at once:
1. Flaky under full-suite load: double work didn't fit the 5s timeout
(Test timed out in 5000ms), while passing in isolation. Measured
635ms for a single call vs 1170ms for the old double-call version —
confirms the double invocation was the timing cause.
2. Blind spot: the two regexes ("podstavnoe padenie" / "za soboy ubrano")
were asserted on TWO DIFFERENT runs, so code that emits the reason on
run 1 but only "za soboy ubrano" (no reason) on run 2 would still pass
green — the check wasn't actually guarding what it claimed to guard.
Fixed by catching the thrown error once and asserting both regexes
against the same captured message, matching how workspace.mjs actually
throws (reason + cleanup note concatenated into one Error, see
workspace.mjs:725).
tools/test:tools full run before: 1577 passed | 1 failed (1578 total).
After: 1578 passed | 0 failed (50 files), stable across two full runs.
Mutation check: temporarily stripped "za soboy ubrano" wording from
workspace.mjs, confirmed the test goes red, then reverted (git diff
clean afterward).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>