#59: PresenceEngine — immutable DB-free snapshot; is_present with the always-present default; three-child fixture walked every day of a four-week span (2 rounds).
#60: override overlap rejection in the DAO, check+INSERT atomic under the connection lock, error names the conflict; deterministic gated race test (2 rounds).
#61: overrides take precedence over the pattern — all three precedence cases; snapshot frozen to tuples (4 rounds).
#62: next-N-present-days preview with inclusive start and a two-year scan cap (2 rounds; one stale finding re-verified).
#63: the week-parity regression guard — full walks of 2020 (366d, ISO wk 53) and 2015 (365d, ISO wk 53) with block-derived expectations (3 rounds).
Suite on the feature branch: 1005 passed (~135 new tests in presence.py/dao_presence).
Completes Feature 05, merged via gate-approved task PRs #57–#63:
- #57: pure presence model (PresenceSchedule + PresenceOverride) with total construction validation and lossless pipe-CSV round-trips (2 review rounds).
- #58: anchor-date cycle_week_index — D-004 arithmetic, pre-anchor floor-division walking, three-year-boundary proof incl. 2020's 53 ISO weeks; source guard bans isocalendar/isoweek (2 rounds).
- #59: PresenceEngine — immutable DB-free snapshot; is_present with the always-present default; three-child fixture walked every day of a four-week span (2 rounds).
- #60: override overlap rejection in the DAO, check+INSERT atomic under the connection lock, error names the conflict; deterministic gated race test (2 rounds).
- #61: overrides take precedence over the pattern — all three precedence cases; snapshot frozen to tuples (4 rounds).
- #62: next-N-present-days preview with inclusive start and a two-year scan cap (2 rounds; one stale finding re-verified).
- #63: the week-parity regression guard — full walks of 2020 (366d, ISO wk 53) and 2015 (365d, ISO wk 53) with block-derived expectations (3 rounds).
Suite on the feature branch: 1005 passed (~135 new tests in presence.py/dao_presence).
presence.py — a PURE module (no HA imports, no DB access, no timezone
logic): PresenceSchedule (child_id, cycle_length_weeks, anchor_date,
pattern mapping week index -> frozenset of present weekdays) and
PresenceOverride (child_id, inclusive start/end range, real-bool
is_present, optional note) with construction that rejects every
invalid shape up front:
- cycle length: integer 1..4 (the 4 cap mirrors schema.py's documented
CHECK); bools/floats/strings rejected.
- pattern: dict required; week indices validated as non-bool ints
covering 0..n-1 exactly (gaps and extras rejected); weekday sets
validated as ints 0..6 (bools rejected — True would silently mean
Monday; strings rejected — '02' would silently mean {0,2}); an
explicitly empty week is valid (absent all week — distinct from no
schedule, the engine's default).
- anchor/override dates: datetime.date or strict YYYY-MM-DD (strptime
round-trip; '2026-1-5' rejected).
- override range: end >= start; is_present must be a real bool;
note optional string.
- child ids: non-bool ints (SQLite binds True as 1).
Serialization: encode() emits the database's pipe-separated CSV
(sorted weekdays per week, empty segment for an absent week);
decode() derives the cycle length from the segment count (mirroring
the schema's segment-count CHECK) and validates strictly, so
decode(encode(x)) round-trips losslessly and equal patterns encode
identically. Tests: 59 (construction matrix, round-trips, encode
shapes, override coverage). Suite: 972 passed.
Review round 1 (Codex gpt-5.6-terra, 2 MEDIUM): datetime.datetime
subclasses datetime.date and slipped past the anchor check, storing a
time component that would TypeError every later calendar comparison —
rejected explicitly now (both model classes). The frozen schedule's
pattern dict let callers inject unvalidated weekdays or drop required
weeks after construction; the validated mapping is now exposed as a
read-only MappingProxyType over frozenset weeks. Tests: datetime
rejection matrix and immutability assertions. Suite: 974 passed.
presence.cycle_week_index: ((target_date - anchor_date).days // 7) %
cycle_length_weeks — anchor-date arithmetic ONLY (D-004). Python
floor division makes pre-anchor dates walk the cycle backwards
(day -1 is the last week), never inverting; the modulo of a negative
stays non-negative, so the index is always a valid week index. The
target date passes through the model's strict plain-date parsing.
Tests: formula offsets, three-week pattern wrap, backwards walking,
day-by-day stability across three Dec-31 -> Jan-1 boundaries
including 2020 (verified 53 ISO weeks — the parity-inversion
regression), and a source guard that the module never reads ISO week
numbers. Suite: 980 passed.
Review round 1 (Codex gpt-5.6-terra, LOW): the boundary walk asserted
transitions in {(same, +1)} which even a reset/inverting
implementation could satisfy on a two-week cycle. The test now
asserts a hand-verified table of expected indices for every day
around each of the three Dec-31/Jan-1 boundaries (2019/2020/2021,
anchor 2019-01-06), so a parity bug cannot pass by coincidence.
Suite: 980 passed.
PresenceEngine is an immutable, database-free snapshot of the
children's schedules answering is_present(child_id, date): a child
with NO schedule entry is present every day (the household's
always-present sibling); a scheduled child is present exactly when
the date's weekday is in the pattern for that date's cycle week
(anchor-date arithmetic via cycle_week_index — never ISO parity).
Construction validates the snapshot shape (dict keyed by the
schedule's own child_id, values are PresenceSchedules) and exposes it
read-only (MappingProxyType + __setattr__ refusal). is_present
rejects non-int child ids and non-plain dates.
Tests: the three-child fixture (two complementary rotation children,
one schedule-free) asserted for every day of a four-week span plus
spot-checked swap days; weekend/weekday-set behaviour across weeks;
construction/input rejection matrix; engine immutability. Suite:
985 passed.
Review round 1 (Codex gpt-5.6-terra, 2 LOW): engine snapshot keys now
pass _validate_child_id (True/1.0 compare equal to 1 and would be
accepted then behave differently on lookup); the always-present test
now walks all 28 days asserting all three children's expected verdicts
instead of sampling every third day. Suite: 985 passed.
PresenceOverridesDao.create now refuses an override that overlaps any
existing override of the SAME child (inclusive boundaries): the check
runs inside the same transaction as the INSERT under the connection
lock, so a racing create cannot slip a conflict between check and
write — a deterministic race test proves exactly one of two concurrent
overlapping creates lands. The ValueError names the conflicting
override's id, its range, and its flag. Round-trip, per-child scope,
and remove-then-recreate tests added. Suite: 990 passed.
The matrix test's end-before-start probe (expecting the schema's
end>=start IntegrityError) hit the new overlap query first, since an
inverted range lexically 'overlaps' the seeded row. The overlap
check now runs only for well-ordered ranges; invalid ones fall
through to the INSERT and keep the IntegrityError contract. Suite:
990 passed.
Review round 1 (Codex gpt-5.6-terra, LOW): the racing test completed
synchronously and proved nothing. The executor now gates task A's
INSERT — inside its open transaction, after its overlap probe — so
task B queues on the connection lock, re-runs the probe against A's
committed row, and is rejected; exactly one lands. Suite: 990 passed.
PresenceEngine gains an overrides snapshot (child_id -> list of
PresenceOverride): is_present checks covering overrides BEFORE the
pattern and returns the override's flag as the answer — an absent
override on a normally-present day returns False, a present one on a
normally-absent day returns True, and an override on a child with no
schedule still applies (holidays work for the always-present sibling).
Snapshot validation mirrors the schedules': dict of lists keyed by the
override's own child id, exposed read-only. The DAO layer guarantees
no overlapping overrides per child; a hand-built snapshot that carries
them resolves deterministically to the LATEST start date (documented).
Tests: all three precedence cases, span edges (outside days stay on
the pattern), snapshot rejection matrix, deterministic overlap
tie-break. Suite: 995 passed.
Review round 1 (Codex gpt-5.6-terra, MEDIUM): the overrides mapping
was shallow-copied, leaving caller-owned lists mutable through
_overrides — a later append would change is_present results. Each
validated list is copied into an immutable tuple before wrapping;
tests prove post-construction appends change nothing and the snapshot
refuses mutation. Suite: 996 passed.
Review round 3 (Codex gpt-5.6-terra, LOW): the helper copied the list,
so the mutation could never have affected even a shallow snapshot.
The engine is now constructed directly on the caller's list, making
the freeze regression-proof. Suite green before commit: 997 passed.
PresenceEngine.next_present_dates(child_id, start_date, count):
walks forward from the INCLUSIVE start date through is_present, so
overrides and the pattern are honoured identically — this is the
preview the admin Schedule tab renders before a parent saves a
custody pattern. Count must be a positive int; the scan is capped at
two years (MAX_PREVIEW_SCAN_DAYS) and an always-absent child raises
a clear ValueError instead of looping forever. Tests: known two-week
pattern (straight five-day week, mid-week start skipping the absent
week), inclusive start, override blocking the preview start,
schedule-free child, input validation, always-absent error. Suite:
1002 passed.
The six-day preview expectation missed that Jan 10-11 are still in
the present week (the away week starts Jan 12). Suite green before
commit: 1003 passed.
The week-parity regression test (D-004): walks a two-week alternating
rotation day by day across a FULL 366-day leap year (2020) and a full
365-day year with 53 ISO weeks (2015), asserting the present-day
sequence never breaks or inverts, including January 1st. The
expectation is derived from the anchor's 7-day blocks — each block
uniform, consecutive blocks alternating — not from the engine's own
index formula, so a parity-based implementation that resets on
January 1st breaks block uniformity or alternation (most visibly in
the block spanning the boundary). Year facts asserted: 2020 = 366
days and ISO week 53; 2015 = 365 days and ISO week 53. The test
docstring names itself as the regression guard. Suite: 1004 passed.
Review round 1 (Codex gpt-5.6-terra, MEDIUM): the walk stopped at
December 31st, so the boundary block's next-January days went
unchecked and a year-resetting implementation would pass. The walk
now runs through January 14th of the following year, asserting block
uniformity across the December 31st -> January 1st transition
(including the post-53-week jump), and the walked-year day count is
verified independently of the walk. Suite: 1005 passed.
Review round 2 (Codex gpt-5.6-terra, 2 LOW): 2015-01-06 is a Tuesday,
not a Monday — the anchor is now the computed first Monday of January
(asserted weekday 0); the docstring names the ISO-week rollover
precisely (the Monday on or after January 4th — January 4th in both
walked years, which end ISO week 53) instead of 'January 1st'. Suite:
1005 passed.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Completes Feature 05, merged via gate-approved task PRs #57–#63:
Suite on the feature branch: 1005 passed (~135 new tests in presence.py/dao_presence).
presence.py — a PURE module (no HA imports, no DB access, no timezone logic): PresenceSchedule (child_id, cycle_length_weeks, anchor_date, pattern mapping week index -> frozenset of present weekdays) and PresenceOverride (child_id, inclusive start/end range, real-bool is_present, optional note) with construction that rejects every invalid shape up front: - cycle length: integer 1..4 (the 4 cap mirrors schema.py's documented CHECK); bools/floats/strings rejected. - pattern: dict required; week indices validated as non-bool ints covering 0..n-1 exactly (gaps and extras rejected); weekday sets validated as ints 0..6 (bools rejected — True would silently mean Monday; strings rejected — '02' would silently mean {0,2}); an explicitly empty week is valid (absent all week — distinct from no schedule, the engine's default). - anchor/override dates: datetime.date or strict YYYY-MM-DD (strptime round-trip; '2026-1-5' rejected). - override range: end >= start; is_present must be a real bool; note optional string. - child ids: non-bool ints (SQLite binds True as 1). Serialization: encode() emits the database's pipe-separated CSV (sorted weekdays per week, empty segment for an absent week); decode() derives the cycle length from the segment count (mirroring the schema's segment-count CHECK) and validates strictly, so decode(encode(x)) round-trips losslessly and equal patterns encode identically. Tests: 59 (construction matrix, round-trips, encode shapes, override coverage). Suite: 972 passed.Review round 1 (Codex gpt-5.6-terra, LOW): the boundary walk asserted transitions in {(same, +1)} which even a reset/inverting implementation could satisfy on a two-week cycle. The test now asserts a hand-verified table of expected indices for every day around each of the three Dec-31/Jan-1 boundaries (2019/2020/2021, anchor 2019-01-06), so a parity bug cannot pass by coincidence. Suite: 980 passed.