all_done is on exactly when the child owed quests today and owes none
remaining (a zero-quest day stays off so celebrations never fire on
an empty day); present_today mirrors the presence engine's answer
for the HA-local date.
State carries the earliest open quest's title truncated under the
255-character limit with the full shape in attributes; the documented
"none" sentinel is reported when the day is clear.
A False platform unload now returns False and retains the runtime
record so live entities keep their database. Sensor names and device
labels re-derive from the current snapshot so child renames
propagate; unique_ids stay child-id based.
Four sensors per active child read the coordinator snapshot: quests
due today (carrying the panel payload that omits missed instances plus
the admin payload that includes them, D-009), completed, remaining, and
completion percentage (100 on a zero-quest day). Platform forwarding
wired through the harness's entity registry stand-ins.
The setup except path closes the database; the coordinator created
above it now shuts down first so no listener can fire against the
closed connection. Regression test included.
One refresh pass builds the whole entity snapshot: active children,
today's instances with derived open/done/missed states, per-child
presence via the PresenceEngine (records converted to model objects),
rollup counts, and the custody cycle day. Interval configurable via
the new update_interval option, default five minutes.
Admin succeeds on all ten admin-only services; a non-admin and a
no-context call are refused on each; complete_quest stays open; a
kiosk-style id is refused on uncomplete_quest and regenerate; the
registry coverage assertion fails the suite on any gap.
Parametrised test denies no-context calls on every admin-only service;
complete_quest proceeds with no user; scheduled internals stay direct
function calls.
permission_gate wraps all registered services at registration time:
admin-only policy reads call.context.user_id against admin_allowlist
is_admin (fail closed on no context); complete_quest stays open.
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.
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.
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.
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.
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.
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.
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.
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, 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.