FA-77186 / Calendar recurrence rules / Open access
Pending alarms for a recurring timed series: ack key · case 01
Acknowledging an occurrence does not silence its alarm.
ROOT CAUSE
Acknowledgements are looked up by alarm fire time instead of occurrence start.
VERIFIED REPAIR
At the ack key decision use `s.isoformat(timespec='minutes')`, leaving the other decision sites of the model unchanged.
Unsuccessful approach: The partial repair `s.isoformat()` looks up the start with seconds, which never matches the minute-precision acknowledgement keys.
Case contract
Occurrences start at dtstart + k*interval_hours for k < count. Each has one alarm at start + trigger_min minutes (negative means before the start). Occurrences whose start string (YYYY-MM-DDTHH:MM) is in acknowledged have no alarm. An alarm due at or before now is delivered at now if the occurrence has not started yet (start > now), otherwise dropped. Alarms due after now + horizon_hours are not scheduled. Return [occurrence_start, fire_time] pairs in series order.
Why this case matters
Recurring calendar series are expanded into concrete instances for display, reminders and conflict checks.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import datetime as dt
import calendar
N = 1
observations = []
def solve(dtstart, interval_hours, count, trigger_min, acknowledged, now, horizon_hours):
first = dt.datetime.fromisoformat(dtstart)
now_t = dt.datetime.fromisoformat(now)
acked = set(acknowledged)
end = now_t + dt.timedelta(hours=horizon_hours)
out = []
for k in range(count):
s = first + dt.timedelta(hours=k * interval_hours)
fire = s + dt.timedelta(minutes=trigger_min)
if fire > end:
break
if fire.isoformat(timespec='minutes') in acked:
continue
if fire <= now_t:
if s > now_t:
fire = now_t
else:
continue
out.append([s.isoformat(timespec='minutes'), fire.isoformat(timespec='minutes')])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: negative trigger',
['2024-01-01T09:00', 24, 3, -15, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T08:45'],
['2024-01-02T09:00', '2024-01-02T08:45'],
['2024-01-03T09:00', '2024-01-03T08:45']]),
('positive trigger',
['2024-01-01T09:00', 24, 3, 10, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T09:10'],
['2024-01-02T09:00', '2024-01-02T09:10'],
['2024-01-03T09:00', '2024-01-03T09:10']]),
('horizon edge with lead time',
['2024-01-01T10:00', 1, 6, -60, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T09:00'],
['2024-01-01T11:00', '2024-01-01T10:00'],
['2024-01-01T12:00', '2024-01-01T11:00'],
['2024-01-01T13:00', '2024-01-01T12:00']]),
('horizon exact',
['2024-01-01T10:00', 2, 5, 0, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']]),
('acknowledged occurrence',
['2024-01-01T09:00', 4, 4, -30, ['2024-01-01T13:00'], '2024-01-01T00:00', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'],
['2024-01-01T17:00', '2024-01-01T16:30'],
['2024-01-01T21:00', '2024-01-01T20:30']]),
('missed alarm catch-up',
['2024-01-01T09:00', 24, 2, -120, [], '2024-01-01T08:30', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']]),
('missed long before start',
['2024-01-01T12:00', 24, 2, -1440, [], '2024-01-01T09:00', 48],
[['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']])],
[('horizon edge with lead time',
['2024-01-01T10:00', 1, 6, -60, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T09:00'],
['2024-01-01T11:00', '2024-01-01T10:00'],
['2024-01-01T12:00', '2024-01-01T11:00'],
['2024-01-01T13:00', '2024-01-01T12:00']]),
('horizon exact',
['2024-01-01T10:00', 2, 5, 0, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']]),
('acknowledged occurrence',
['2024-01-01T09:00', 4, 4, -30, ['2024-01-01T13:00'], '2024-01-01T00:00', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'],
['2024-01-01T17:00', '2024-01-01T16:30'],
['2024-01-01T21:00', '2024-01-01T20:30']]),
('missed alarm catch-up',
['2024-01-01T09:00', 24, 2, -120, [], '2024-01-01T08:30', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']]),
('missed long before start',
['2024-01-01T12:00', 24, 2, -1440, [], '2024-01-01T09:00', 48],
[['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']]),
('occurrence starting now',
['2024-01-01T09:00', 24, 2, -10, [], '2024-01-01T09:00', 48],
[['2024-01-02T09:00', '2024-01-02T08:50']]),
('started occurrence dropped',
['2024-01-01T09:00', 1, 4, -5, [], '2024-01-01T10:30', 4],
[['2024-01-01T11:00', '2024-01-01T10:55'], ['2024-01-01T12:00', '2024-01-01T11:55']])],
[('acknowledged occurrence',
['2024-01-01T09:00', 4, 4, -30, ['2024-01-01T13:00'], '2024-01-01T00:00', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'],
['2024-01-01T17:00', '2024-01-01T16:30'],
['2024-01-01T21:00', '2024-01-01T20:30']]),
('missed alarm catch-up',
['2024-01-01T09:00', 24, 2, -120, [], '2024-01-01T08:30', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']]),
('missed long before start',
['2024-01-01T12:00', 24, 2, -1440, [], '2024-01-01T09:00', 48],
[['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']]),
('occurrence starting now',
['2024-01-01T09:00', 24, 2, -10, [], '2024-01-01T09:00', 48],
[['2024-01-02T09:00', '2024-01-02T08:50']]),
('started occurrence dropped',
['2024-01-01T09:00', 1, 4, -5, [], '2024-01-01T10:30', 4],
[['2024-01-01T11:00', '2024-01-01T10:55'], ['2024-01-01T12:00', '2024-01-01T11:55']]),
('ack and catch-up mix',
['2024-02-01T09:00', 3, 4, -240, ['2024-02-01T12:00'], '2024-02-01T08:00', 12],
[['2024-02-01T09:00', '2024-02-01T08:00'],
['2024-02-01T15:00', '2024-02-01T11:00'],
['2024-02-01T18:00', '2024-02-01T14:00']]),
('zero trigger all future',
['2024-03-01T06:00', 6, 3, 0, ['2024-03-01T06:00'], '2024-03-01T00:00', 24],
[['2024-03-01T12:00', '2024-03-01T12:00'], ['2024-03-01T18:00', '2024-03-01T18:00']])],
[('missed long before start',
['2024-01-01T12:00', 24, 2, -1440, [], '2024-01-01T09:00', 48],
[['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']]),
('occurrence starting now',
['2024-01-01T09:00', 24, 2, -10, [], '2024-01-01T09:00', 48],
[['2024-01-02T09:00', '2024-01-02T08:50']]),
('started occurrence dropped',
['2024-01-01T09:00', 1, 4, -5, [], '2024-01-01T10:30', 4],
[['2024-01-01T11:00', '2024-01-01T10:55'], ['2024-01-01T12:00', '2024-01-01T11:55']]),
('ack and catch-up mix',
['2024-02-01T09:00', 3, 4, -240, ['2024-02-01T12:00'], '2024-02-01T08:00', 12],
[['2024-02-01T09:00', '2024-02-01T08:00'],
['2024-02-01T15:00', '2024-02-01T11:00'],
['2024-02-01T18:00', '2024-02-01T14:00']]),
('zero trigger all future',
['2024-03-01T06:00', 6, 3, 0, ['2024-03-01T06:00'], '2024-03-01T00:00', 24],
[['2024-03-01T12:00', '2024-03-01T12:00'], ['2024-03-01T18:00', '2024-03-01T18:00']]),
('regression: negative trigger',
['2024-01-01T09:00', 24, 3, -15, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T08:45'],
['2024-01-02T09:00', '2024-01-02T08:45'],
['2024-01-03T09:00', '2024-01-03T08:45']]),
('positive trigger',
['2024-01-01T09:00', 24, 3, 10, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T09:10'],
['2024-01-02T09:00', '2024-01-02T09:10'],
['2024-01-03T09:00', '2024-01-03T09:10']])],
[('started occurrence dropped',
['2024-01-01T09:00', 1, 4, -5, [], '2024-01-01T10:30', 4],
[['2024-01-01T11:00', '2024-01-01T10:55'], ['2024-01-01T12:00', '2024-01-01T11:55']]),
('ack and catch-up mix',
['2024-02-01T09:00', 3, 4, -240, ['2024-02-01T12:00'], '2024-02-01T08:00', 12],
[['2024-02-01T09:00', '2024-02-01T08:00'],
['2024-02-01T15:00', '2024-02-01T11:00'],
['2024-02-01T18:00', '2024-02-01T14:00']]),
('zero trigger all future',
['2024-03-01T06:00', 6, 3, 0, ['2024-03-01T06:00'], '2024-03-01T00:00', 24],
[['2024-03-01T12:00', '2024-03-01T12:00'], ['2024-03-01T18:00', '2024-03-01T18:00']]),
('regression: negative trigger',
['2024-01-01T09:00', 24, 3, -15, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T08:45'],
['2024-01-02T09:00', '2024-01-02T08:45'],
['2024-01-03T09:00', '2024-01-03T08:45']]),
('positive trigger',
['2024-01-01T09:00', 24, 3, 10, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T09:10'],
['2024-01-02T09:00', '2024-01-02T09:10'],
['2024-01-03T09:00', '2024-01-03T09:10']]),
('horizon edge with lead time',
['2024-01-01T10:00', 1, 6, -60, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T09:00'],
['2024-01-01T11:00', '2024-01-01T10:00'],
['2024-01-01T12:00', '2024-01-01T11:00'],
['2024-01-01T13:00', '2024-01-01T12:00']]),
('horizon exact',
['2024-01-01T10:00', 2, 5, 0, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']])]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: negative trigger | [['2024-01-01T09:00', '2024-01-01T08:45'], ['2024-01-02T09:00', '2024-01-02T08:45'], ['2024-01-03T09:00', '2024-01-03T08:45']] | [['2024-01-01T09:00', '2024-01-01T08:45'], ['2024-01-02T09:00', '2024-01-02T08:45'], ['2024-01-03T09:00', '2024-01-03T08:45']] | Passed |
| positive trigger | [['2024-01-01T09:00', '2024-01-01T09:10'], ['2024-01-02T09:00', '2024-01-02T09:10'], ['2024-01-03T09:00', '2024-01-03T09:10']] | [['2024-01-01T09:00', '2024-01-01T09:10'], ['2024-01-02T09:00', '2024-01-02T09:10'], ['2024-01-03T09:00', '2024-01-03T09:10']] | Passed |
| horizon edge with lead time | [['2024-01-01T10:00', '2024-01-01T09:00'], ['2024-01-01T11:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T11:00'], ['2024-01-01T13:00', '2024-01-01T12:00']] | [['2024-01-01T10:00', '2024-01-01T09:00'], ['2024-01-01T11:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T11:00'], ['2024-01-01T13:00', '2024-01-01T12:00']] | Passed |
| horizon exact | [['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']] | [['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']] | Passed |
| acknowledged occurrence | [['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-01T13:00', '2024-01-01T12:30'], ['2024-01-01T17:00', '2024-01-01T16:30'], ['2024-01-01T21:00', '2024-01-01T20:30']] | [['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-01T17:00', '2024-01-01T16:30'], ['2024-01-01T21:00', '2024-01-01T20:30']] | Failed |
| missed alarm catch-up | [['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']] | [['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']] | Passed |
| missed long before start | [['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']] | [['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']] | Passed |
SHA-256 / 441d5ab3d98e845203b35be6b0eef1993e4eea525109fecc495f05893d39fe98
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import datetime as dt
import calendar
N = 1
observations = []
def solve(dtstart, interval_hours, count, trigger_min, acknowledged, now, horizon_hours):
first = dt.datetime.fromisoformat(dtstart)
now_t = dt.datetime.fromisoformat(now)
acked = set(acknowledged)
end = now_t + dt.timedelta(hours=horizon_hours)
out = []
for k in range(count):
s = first + dt.timedelta(hours=k * interval_hours)
fire = s + dt.timedelta(minutes=trigger_min)
if fire > end:
break
if s.isoformat() in acked:
continue
if fire <= now_t:
if s > now_t:
fire = now_t
else:
continue
out.append([s.isoformat(timespec='minutes'), fire.isoformat(timespec='minutes')])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: negative trigger',
['2024-01-01T09:00', 24, 3, -15, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T08:45'],
['2024-01-02T09:00', '2024-01-02T08:45'],
['2024-01-03T09:00', '2024-01-03T08:45']]),
('positive trigger',
['2024-01-01T09:00', 24, 3, 10, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T09:10'],
['2024-01-02T09:00', '2024-01-02T09:10'],
['2024-01-03T09:00', '2024-01-03T09:10']]),
('horizon edge with lead time',
['2024-01-01T10:00', 1, 6, -60, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T09:00'],
['2024-01-01T11:00', '2024-01-01T10:00'],
['2024-01-01T12:00', '2024-01-01T11:00'],
['2024-01-01T13:00', '2024-01-01T12:00']]),
('horizon exact',
['2024-01-01T10:00', 2, 5, 0, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']]),
('acknowledged occurrence',
['2024-01-01T09:00', 4, 4, -30, ['2024-01-01T13:00'], '2024-01-01T00:00', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'],
['2024-01-01T17:00', '2024-01-01T16:30'],
['2024-01-01T21:00', '2024-01-01T20:30']]),
('missed alarm catch-up',
['2024-01-01T09:00', 24, 2, -120, [], '2024-01-01T08:30', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']]),
('missed long before start',
['2024-01-01T12:00', 24, 2, -1440, [], '2024-01-01T09:00', 48],
[['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']])],
[('horizon edge with lead time',
['2024-01-01T10:00', 1, 6, -60, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T09:00'],
['2024-01-01T11:00', '2024-01-01T10:00'],
['2024-01-01T12:00', '2024-01-01T11:00'],
['2024-01-01T13:00', '2024-01-01T12:00']]),
('horizon exact',
['2024-01-01T10:00', 2, 5, 0, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']]),
('acknowledged occurrence',
['2024-01-01T09:00', 4, 4, -30, ['2024-01-01T13:00'], '2024-01-01T00:00', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'],
['2024-01-01T17:00', '2024-01-01T16:30'],
['2024-01-01T21:00', '2024-01-01T20:30']]),
('missed alarm catch-up',
['2024-01-01T09:00', 24, 2, -120, [], '2024-01-01T08:30', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']]),
('missed long before start',
['2024-01-01T12:00', 24, 2, -1440, [], '2024-01-01T09:00', 48],
[['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']]),
('occurrence starting now',
['2024-01-01T09:00', 24, 2, -10, [], '2024-01-01T09:00', 48],
[['2024-01-02T09:00', '2024-01-02T08:50']]),
('started occurrence dropped',
['2024-01-01T09:00', 1, 4, -5, [], '2024-01-01T10:30', 4],
[['2024-01-01T11:00', '2024-01-01T10:55'], ['2024-01-01T12:00', '2024-01-01T11:55']])],
[('acknowledged occurrence',
['2024-01-01T09:00', 4, 4, -30, ['2024-01-01T13:00'], '2024-01-01T00:00', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'],
['2024-01-01T17:00', '2024-01-01T16:30'],
['2024-01-01T21:00', '2024-01-01T20:30']]),
('missed alarm catch-up',
['2024-01-01T09:00', 24, 2, -120, [], '2024-01-01T08:30', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']]),
('missed long before start',
['2024-01-01T12:00', 24, 2, -1440, [], '2024-01-01T09:00', 48],
[['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']]),
('occurrence starting now',
['2024-01-01T09:00', 24, 2, -10, [], '2024-01-01T09:00', 48],
[['2024-01-02T09:00', '2024-01-02T08:50']]),
('started occurrence dropped',
['2024-01-01T09:00', 1, 4, -5, [], '2024-01-01T10:30', 4],
[['2024-01-01T11:00', '2024-01-01T10:55'], ['2024-01-01T12:00', '2024-01-01T11:55']]),
('ack and catch-up mix',
['2024-02-01T09:00', 3, 4, -240, ['2024-02-01T12:00'], '2024-02-01T08:00', 12],
[['2024-02-01T09:00', '2024-02-01T08:00'],
['2024-02-01T15:00', '2024-02-01T11:00'],
['2024-02-01T18:00', '2024-02-01T14:00']]),
('zero trigger all future',
['2024-03-01T06:00', 6, 3, 0, ['2024-03-01T06:00'], '2024-03-01T00:00', 24],
[['2024-03-01T12:00', '2024-03-01T12:00'], ['2024-03-01T18:00', '2024-03-01T18:00']])],
[('missed long before start',
['2024-01-01T12:00', 24, 2, -1440, [], '2024-01-01T09:00', 48],
[['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']]),
('occurrence starting now',
['2024-01-01T09:00', 24, 2, -10, [], '2024-01-01T09:00', 48],
[['2024-01-02T09:00', '2024-01-02T08:50']]),
('started occurrence dropped',
['2024-01-01T09:00', 1, 4, -5, [], '2024-01-01T10:30', 4],
[['2024-01-01T11:00', '2024-01-01T10:55'], ['2024-01-01T12:00', '2024-01-01T11:55']]),
('ack and catch-up mix',
['2024-02-01T09:00', 3, 4, -240, ['2024-02-01T12:00'], '2024-02-01T08:00', 12],
[['2024-02-01T09:00', '2024-02-01T08:00'],
['2024-02-01T15:00', '2024-02-01T11:00'],
['2024-02-01T18:00', '2024-02-01T14:00']]),
('zero trigger all future',
['2024-03-01T06:00', 6, 3, 0, ['2024-03-01T06:00'], '2024-03-01T00:00', 24],
[['2024-03-01T12:00', '2024-03-01T12:00'], ['2024-03-01T18:00', '2024-03-01T18:00']]),
('regression: negative trigger',
['2024-01-01T09:00', 24, 3, -15, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T08:45'],
['2024-01-02T09:00', '2024-01-02T08:45'],
['2024-01-03T09:00', '2024-01-03T08:45']]),
('positive trigger',
['2024-01-01T09:00', 24, 3, 10, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T09:10'],
['2024-01-02T09:00', '2024-01-02T09:10'],
['2024-01-03T09:00', '2024-01-03T09:10']])],
[('started occurrence dropped',
['2024-01-01T09:00', 1, 4, -5, [], '2024-01-01T10:30', 4],
[['2024-01-01T11:00', '2024-01-01T10:55'], ['2024-01-01T12:00', '2024-01-01T11:55']]),
('ack and catch-up mix',
['2024-02-01T09:00', 3, 4, -240, ['2024-02-01T12:00'], '2024-02-01T08:00', 12],
[['2024-02-01T09:00', '2024-02-01T08:00'],
['2024-02-01T15:00', '2024-02-01T11:00'],
['2024-02-01T18:00', '2024-02-01T14:00']]),
('zero trigger all future',
['2024-03-01T06:00', 6, 3, 0, ['2024-03-01T06:00'], '2024-03-01T00:00', 24],
[['2024-03-01T12:00', '2024-03-01T12:00'], ['2024-03-01T18:00', '2024-03-01T18:00']]),
('regression: negative trigger',
['2024-01-01T09:00', 24, 3, -15, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T08:45'],
['2024-01-02T09:00', '2024-01-02T08:45'],
['2024-01-03T09:00', '2024-01-03T08:45']]),
('positive trigger',
['2024-01-01T09:00', 24, 3, 10, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T09:10'],
['2024-01-02T09:00', '2024-01-02T09:10'],
['2024-01-03T09:00', '2024-01-03T09:10']]),
('horizon edge with lead time',
['2024-01-01T10:00', 1, 6, -60, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T09:00'],
['2024-01-01T11:00', '2024-01-01T10:00'],
['2024-01-01T12:00', '2024-01-01T11:00'],
['2024-01-01T13:00', '2024-01-01T12:00']]),
('horizon exact',
['2024-01-01T10:00', 2, 5, 0, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']])]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: negative trigger | [['2024-01-01T09:00', '2024-01-01T08:45'], ['2024-01-02T09:00', '2024-01-02T08:45'], ['2024-01-03T09:00', '2024-01-03T08:45']] | [['2024-01-01T09:00', '2024-01-01T08:45'], ['2024-01-02T09:00', '2024-01-02T08:45'], ['2024-01-03T09:00', '2024-01-03T08:45']] | Passed |
| positive trigger | [['2024-01-01T09:00', '2024-01-01T09:10'], ['2024-01-02T09:00', '2024-01-02T09:10'], ['2024-01-03T09:00', '2024-01-03T09:10']] | [['2024-01-01T09:00', '2024-01-01T09:10'], ['2024-01-02T09:00', '2024-01-02T09:10'], ['2024-01-03T09:00', '2024-01-03T09:10']] | Passed |
| horizon edge with lead time | [['2024-01-01T10:00', '2024-01-01T09:00'], ['2024-01-01T11:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T11:00'], ['2024-01-01T13:00', '2024-01-01T12:00']] | [['2024-01-01T10:00', '2024-01-01T09:00'], ['2024-01-01T11:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T11:00'], ['2024-01-01T13:00', '2024-01-01T12:00']] | Passed |
| horizon exact | [['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']] | [['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']] | Passed |
| acknowledged occurrence | [['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-01T13:00', '2024-01-01T12:30'], ['2024-01-01T17:00', '2024-01-01T16:30'], ['2024-01-01T21:00', '2024-01-01T20:30']] | [['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-01T17:00', '2024-01-01T16:30'], ['2024-01-01T21:00', '2024-01-01T20:30']] | Failed |
| missed alarm catch-up | [['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']] | [['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']] | Passed |
| missed long before start | [['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']] | [['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']] | Passed |
SHA-256 / 1dd15f22e11bd7fc61de05912bf801822234c177c77d6667a5fe3c001d34f649
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import datetime as dt
import calendar
N = 1
observations = []
def solve(dtstart, interval_hours, count, trigger_min, acknowledged, now, horizon_hours):
first = dt.datetime.fromisoformat(dtstart)
now_t = dt.datetime.fromisoformat(now)
acked = set(acknowledged)
end = now_t + dt.timedelta(hours=horizon_hours)
out = []
for k in range(count):
s = first + dt.timedelta(hours=k * interval_hours)
fire = s + dt.timedelta(minutes=trigger_min)
if fire > end:
break
if s.isoformat(timespec='minutes') in acked:
continue
if fire <= now_t:
if s > now_t:
fire = now_t
else:
continue
out.append([s.isoformat(timespec='minutes'), fire.isoformat(timespec='minutes')])
return out
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[('regression: negative trigger',
['2024-01-01T09:00', 24, 3, -15, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T08:45'],
['2024-01-02T09:00', '2024-01-02T08:45'],
['2024-01-03T09:00', '2024-01-03T08:45']]),
('positive trigger',
['2024-01-01T09:00', 24, 3, 10, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T09:10'],
['2024-01-02T09:00', '2024-01-02T09:10'],
['2024-01-03T09:00', '2024-01-03T09:10']]),
('horizon edge with lead time',
['2024-01-01T10:00', 1, 6, -60, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T09:00'],
['2024-01-01T11:00', '2024-01-01T10:00'],
['2024-01-01T12:00', '2024-01-01T11:00'],
['2024-01-01T13:00', '2024-01-01T12:00']]),
('horizon exact',
['2024-01-01T10:00', 2, 5, 0, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']]),
('acknowledged occurrence',
['2024-01-01T09:00', 4, 4, -30, ['2024-01-01T13:00'], '2024-01-01T00:00', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'],
['2024-01-01T17:00', '2024-01-01T16:30'],
['2024-01-01T21:00', '2024-01-01T20:30']]),
('missed alarm catch-up',
['2024-01-01T09:00', 24, 2, -120, [], '2024-01-01T08:30', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']]),
('missed long before start',
['2024-01-01T12:00', 24, 2, -1440, [], '2024-01-01T09:00', 48],
[['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']])],
[('horizon edge with lead time',
['2024-01-01T10:00', 1, 6, -60, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T09:00'],
['2024-01-01T11:00', '2024-01-01T10:00'],
['2024-01-01T12:00', '2024-01-01T11:00'],
['2024-01-01T13:00', '2024-01-01T12:00']]),
('horizon exact',
['2024-01-01T10:00', 2, 5, 0, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']]),
('acknowledged occurrence',
['2024-01-01T09:00', 4, 4, -30, ['2024-01-01T13:00'], '2024-01-01T00:00', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'],
['2024-01-01T17:00', '2024-01-01T16:30'],
['2024-01-01T21:00', '2024-01-01T20:30']]),
('missed alarm catch-up',
['2024-01-01T09:00', 24, 2, -120, [], '2024-01-01T08:30', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']]),
('missed long before start',
['2024-01-01T12:00', 24, 2, -1440, [], '2024-01-01T09:00', 48],
[['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']]),
('occurrence starting now',
['2024-01-01T09:00', 24, 2, -10, [], '2024-01-01T09:00', 48],
[['2024-01-02T09:00', '2024-01-02T08:50']]),
('started occurrence dropped',
['2024-01-01T09:00', 1, 4, -5, [], '2024-01-01T10:30', 4],
[['2024-01-01T11:00', '2024-01-01T10:55'], ['2024-01-01T12:00', '2024-01-01T11:55']])],
[('acknowledged occurrence',
['2024-01-01T09:00', 4, 4, -30, ['2024-01-01T13:00'], '2024-01-01T00:00', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'],
['2024-01-01T17:00', '2024-01-01T16:30'],
['2024-01-01T21:00', '2024-01-01T20:30']]),
('missed alarm catch-up',
['2024-01-01T09:00', 24, 2, -120, [], '2024-01-01T08:30', 48],
[['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']]),
('missed long before start',
['2024-01-01T12:00', 24, 2, -1440, [], '2024-01-01T09:00', 48],
[['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']]),
('occurrence starting now',
['2024-01-01T09:00', 24, 2, -10, [], '2024-01-01T09:00', 48],
[['2024-01-02T09:00', '2024-01-02T08:50']]),
('started occurrence dropped',
['2024-01-01T09:00', 1, 4, -5, [], '2024-01-01T10:30', 4],
[['2024-01-01T11:00', '2024-01-01T10:55'], ['2024-01-01T12:00', '2024-01-01T11:55']]),
('ack and catch-up mix',
['2024-02-01T09:00', 3, 4, -240, ['2024-02-01T12:00'], '2024-02-01T08:00', 12],
[['2024-02-01T09:00', '2024-02-01T08:00'],
['2024-02-01T15:00', '2024-02-01T11:00'],
['2024-02-01T18:00', '2024-02-01T14:00']]),
('zero trigger all future',
['2024-03-01T06:00', 6, 3, 0, ['2024-03-01T06:00'], '2024-03-01T00:00', 24],
[['2024-03-01T12:00', '2024-03-01T12:00'], ['2024-03-01T18:00', '2024-03-01T18:00']])],
[('missed long before start',
['2024-01-01T12:00', 24, 2, -1440, [], '2024-01-01T09:00', 48],
[['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']]),
('occurrence starting now',
['2024-01-01T09:00', 24, 2, -10, [], '2024-01-01T09:00', 48],
[['2024-01-02T09:00', '2024-01-02T08:50']]),
('started occurrence dropped',
['2024-01-01T09:00', 1, 4, -5, [], '2024-01-01T10:30', 4],
[['2024-01-01T11:00', '2024-01-01T10:55'], ['2024-01-01T12:00', '2024-01-01T11:55']]),
('ack and catch-up mix',
['2024-02-01T09:00', 3, 4, -240, ['2024-02-01T12:00'], '2024-02-01T08:00', 12],
[['2024-02-01T09:00', '2024-02-01T08:00'],
['2024-02-01T15:00', '2024-02-01T11:00'],
['2024-02-01T18:00', '2024-02-01T14:00']]),
('zero trigger all future',
['2024-03-01T06:00', 6, 3, 0, ['2024-03-01T06:00'], '2024-03-01T00:00', 24],
[['2024-03-01T12:00', '2024-03-01T12:00'], ['2024-03-01T18:00', '2024-03-01T18:00']]),
('regression: negative trigger',
['2024-01-01T09:00', 24, 3, -15, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T08:45'],
['2024-01-02T09:00', '2024-01-02T08:45'],
['2024-01-03T09:00', '2024-01-03T08:45']]),
('positive trigger',
['2024-01-01T09:00', 24, 3, 10, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T09:10'],
['2024-01-02T09:00', '2024-01-02T09:10'],
['2024-01-03T09:00', '2024-01-03T09:10']])],
[('started occurrence dropped',
['2024-01-01T09:00', 1, 4, -5, [], '2024-01-01T10:30', 4],
[['2024-01-01T11:00', '2024-01-01T10:55'], ['2024-01-01T12:00', '2024-01-01T11:55']]),
('ack and catch-up mix',
['2024-02-01T09:00', 3, 4, -240, ['2024-02-01T12:00'], '2024-02-01T08:00', 12],
[['2024-02-01T09:00', '2024-02-01T08:00'],
['2024-02-01T15:00', '2024-02-01T11:00'],
['2024-02-01T18:00', '2024-02-01T14:00']]),
('zero trigger all future',
['2024-03-01T06:00', 6, 3, 0, ['2024-03-01T06:00'], '2024-03-01T00:00', 24],
[['2024-03-01T12:00', '2024-03-01T12:00'], ['2024-03-01T18:00', '2024-03-01T18:00']]),
('regression: negative trigger',
['2024-01-01T09:00', 24, 3, -15, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T08:45'],
['2024-01-02T09:00', '2024-01-02T08:45'],
['2024-01-03T09:00', '2024-01-03T08:45']]),
('positive trigger',
['2024-01-01T09:00', 24, 3, 10, [], '2024-01-01T00:00', 72],
[['2024-01-01T09:00', '2024-01-01T09:10'],
['2024-01-02T09:00', '2024-01-02T09:10'],
['2024-01-03T09:00', '2024-01-03T09:10']]),
('horizon edge with lead time',
['2024-01-01T10:00', 1, 6, -60, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T09:00'],
['2024-01-01T11:00', '2024-01-01T10:00'],
['2024-01-01T12:00', '2024-01-01T11:00'],
['2024-01-01T13:00', '2024-01-01T12:00']]),
('horizon exact',
['2024-01-01T10:00', 2, 5, 0, [], '2024-01-01T08:00', 4],
[['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']])]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression: negative trigger | [['2024-01-01T09:00', '2024-01-01T08:45'], ['2024-01-02T09:00', '2024-01-02T08:45'], ['2024-01-03T09:00', '2024-01-03T08:45']] | [['2024-01-01T09:00', '2024-01-01T08:45'], ['2024-01-02T09:00', '2024-01-02T08:45'], ['2024-01-03T09:00', '2024-01-03T08:45']] | Passed |
| positive trigger | [['2024-01-01T09:00', '2024-01-01T09:10'], ['2024-01-02T09:00', '2024-01-02T09:10'], ['2024-01-03T09:00', '2024-01-03T09:10']] | [['2024-01-01T09:00', '2024-01-01T09:10'], ['2024-01-02T09:00', '2024-01-02T09:10'], ['2024-01-03T09:00', '2024-01-03T09:10']] | Passed |
| horizon edge with lead time | [['2024-01-01T10:00', '2024-01-01T09:00'], ['2024-01-01T11:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T11:00'], ['2024-01-01T13:00', '2024-01-01T12:00']] | [['2024-01-01T10:00', '2024-01-01T09:00'], ['2024-01-01T11:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T11:00'], ['2024-01-01T13:00', '2024-01-01T12:00']] | Passed |
| horizon exact | [['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']] | [['2024-01-01T10:00', '2024-01-01T10:00'], ['2024-01-01T12:00', '2024-01-01T12:00']] | Passed |
| acknowledged occurrence | [['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-01T17:00', '2024-01-01T16:30'], ['2024-01-01T21:00', '2024-01-01T20:30']] | [['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-01T17:00', '2024-01-01T16:30'], ['2024-01-01T21:00', '2024-01-01T20:30']] | Passed |
| missed alarm catch-up | [['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']] | [['2024-01-01T09:00', '2024-01-01T08:30'], ['2024-01-02T09:00', '2024-01-02T07:00']] | Passed |
| missed long before start | [['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']] | [['2024-01-01T12:00', '2024-01-01T09:00'], ['2024-01-02T12:00', '2024-01-01T12:00']] | Passed |
SHA-256 / 689d7e1fe41e28ff04682c3f8442f4e2ab23c13b63c0e4646ab2b5fd38f4fa65
Verification & scope
Stipulated, bounded recurrence-expansion model evaluated offline on explicit fixtures; it is not a complete iCalendar implementation and makes no claim of standards conformance. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:49:23.212663+00:00.
Case digest / 3a38b1ad41c8a99c3383e90e2bc7af4954c2043ccacf2232cb3990c83e1ddf51