Commit Graph
79 Commits
Author SHA1 Message Date
orchestrator-agent 8cd43141a9 Serialize the missed sweep's watermark claim
The whole watermark read-query-fire-write span runs under a
per-database sweep lock; a racing second sweep re-reads the watermark
inside the lock and announces nothing.
2026-09-19 14:16:50 -04:00
orchestrator-agent 3de9f4b970 Add the nightly missed-quest sweep
Fires one nestquest_quest_missed event per past-due open instance at
the configured rollover time and at startup, watermark-guarded in the
new nestquest_meta_state table (migration 7, strictly additive).
Read-only over the domain tables; never reports an instance that was
ever completed.
2026-09-19 14:15:21 -04:00
orchestrator-agent 35e66bd9e6 Carry completion timing under the lock; shut down before close
The completed event's was_on_time travels on the CompletionResult
(decided with the write) instead of re-reading latest state a racing
reversal could replace; unload clears coordinator listeners before
the database close.
2026-09-19 13:29:25 -04:00
orchestrator-agent a5dbae5d50 Serialize forced coordinator refreshes per entry
NestQuestCoordinator.async_refresh holds a per-entry lock so
overlapping service-path refreshes never interleave; the queued pass
always publishes after the in-flight one.
2026-09-19 13:21:54 -04:00
orchestrator-agent ad56d13c6e Refresh entities immediately after completion service calls
complete_quest and uncomplete_quest force a coordinator refresh when
the call appended, so entity states land the moment the service call
returns; scheduled internals are unaffected.
2026-09-19 13:16:12 -04:00
orchestrator-agent 2a4eec658f Decide event transitions under the completion lock
complete/uncomplete return a CompletionResult str subclass carrying
appended (decided with the write under the lock), so duplicate
concurrent calls fire one transition event; day-complete only
evaluates for an instance due today.
2026-09-19 12:03:42 -04:00
orchestrator-agent fe38e5040e Fire the documented bus events from the completion service paths
quest_completed and quest_uncompleted fire on actual transitions with
documented payloads; child_day_complete fires when a completion clears
the child's day; quest_missed is defined as Feature 11's contract.
Payloads documented in the README for automation authors.
2026-09-19 12:01:05 -04:00
orchestrator-agent 5bca24910a Add household rollup and cycle-day sensors
Household quests due/completed today sum the active children's
snapshots; cycle day reports the first scheduled child's cycle
position (0 when nobody is scheduled), anchor-date arithmetic only.
2026-09-19 11:58:12 -04:00
orchestrator-agent df4c9eb21a Add per-child all-done and present-today binary sensors
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.
2026-09-19 11:56:10 -04:00
orchestrator-agent 1f7a7ba46c Add the per-child next-quest sensor
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.
2026-09-19 11:54:19 -04:00
orchestrator-agent 7a0c8d1885 Honor failed platform unload; derive labels from the snapshot
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.
2026-09-19 11:53:27 -04:00
orchestrator-agent 00409a76db Add per-child daily count and percentage sensors
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.
2026-09-19 11:50:18 -04:00
orchestrator-agent 5c99987e7f Shut the coordinator down when first refresh fails setup
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.
2026-09-19 11:44:11 -04:00
orchestrator-agent 979eafc5a2 Add the shared NestQuest data update coordinator
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.
2026-09-19 11:41:12 -04:00
orchestrator-agent 37d7b7e6c3 Gate every NestQuest service behind the admin permission check
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.
2026-09-19 08:36:14 -04:00
orchestrator-agent 4281b9d01c Add admin-only versus open service policy registry
One mapping in service_policy.py: complete_quest is open, every other
registered nestquest service is admin_only.
2026-09-18 16:05:12 -04:00
orchestrator-agent b1c42f10d0 Regenerate child instances after presence service writes
Presence pattern and override handlers now call regenerate_for_child.
Definition edit/activate pass HA-local today and configured horizon.
2026-09-18 16:03:42 -04:00
orchestrator-agent 2d30f0ce77 Register canonical NestQuest HA services with schemas
Domain-global handlers for the ENTITIES-AND-SERVICES inventory plus
regenerate. No permission decorator yet.
2026-09-18 15:59:02 -04:00
overseer 318d77f443 Derive missed for past-due open instances (Feature 08, task 6) (#93) 2026-09-18 07:09:21 -04:00
overseer 2ec016ebfe Compute was_on_time at completion (Feature 08, task 5) (#92) 2026-09-18 07:02:29 -04:00
overseer c3495bb277 uncomplete_instance reversal event (Feature 08, task 4) (#91) 2026-09-18 06:52:42 -04:00
overseer 775a9906e7 complete_instance (Feature 08, task 3) (#90) 2026-09-18 06:47:45 -04:00
overseer 293efe9705 Derive instance state from latest event (Feature 08, task 2) (#89) 2026-09-18 06:35:50 -04:00
overseer 22797cbc7e Completion business layer wrapping append-only DAO (Feature 08, task 1) (#88) 2026-09-18 06:31:01 -04:00
overseer 6e32761b0c Guard against stale due_time overwrite (Feature 07 fix) (#87) 2026-09-17 17:17:57 -04:00
overseer 3add2d2f48 Thread configured horizon_days (Feature 07 integration fix) (#86) 2026-09-17 17:07:10 -04:00
overseer 353d88e3af Clamp start to today (no past instances) (Feature 07, task 9) (#83) 2026-09-17 16:12:08 -04:00
overseer f6aad9d20f HA-local timezone + DST boundary tests (Feature 07, task 8) (#82) 2026-09-17 16:06:41 -04:00
overseer 5706d8b7a6 Startup backfill + regenerate service (Feature 07, task 7) (#81) 2026-09-17 15:42:10 -04:00
overseer be6367b600 Schedule the daily generation run at rollover (Feature 07, task 6) (#80) 2026-09-17 15:10:57 -04:00
overseer ca26ced59f Regenerate future instances on presence change (Feature 07, task 5) (#79) 2026-09-17 14:50:50 -04:00
overseer 2146531665 Regenerate future instances on definition change (Feature 07, task 4) (#78) 2026-09-17 14:24:22 -04:00
overseer 168bb7b2f1 Skip completed instances during materialization (Feature 07, task 3) (#77) 2026-09-17 14:09:42 -04:00
overseer 9836a6f524 Core generation walk: assignees x windows fan-out with presence (Feature 07, task 1) (#75) 2026-09-17 14:01:20 -04:00
overseer fa40e21dc2 Fix feature-integration review findings F06-001 and F06-002 (#74) 2026-09-17 12:06:47 -04:00
overseer 1432dba74c Quest definition query helpers for materialization (Feature 06, task 6) (#71) 2026-09-17 09:49:54 -04:00
overseer 1b6973efd3 Deactivate and reactivate a quest definition (Feature 06, task 5) (#70) 2026-09-17 09:40:15 -04:00
overseer 4606d7ddd0 Assign and unassign children on a quest definition (Feature 06, task 4) (#69) 2026-09-17 09:21:21 -04:00
overseer 802258d431 Edit a quest definition's metadata, schedule rule, and windows (Feature 06, task 3) (#68) 2026-09-17 09:06:11 -04:00
overseer d9cdac87d7 Create a quest definition with rule, assignees, and windows (Feature 06, task 2) (#67) 2026-09-17 08:41:56 -04:00
overseer cbd29f5ab7 Map ScheduleRule models to and from schedule_rules storage (Feature 06, task 1) (#66) 2026-09-17 08:11:19 -04:00
Joshua King 1bb0617471 Next-N-present-days preview (Feature 05, task 6)
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.
2026-09-15 18:19:53 -04:00
Joshua King bff2f4ff5e Review fix: freeze the engine's overrides snapshot
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.
2026-09-15 18:13:48 -04:00
Joshua King 8f04d93363 Overrides take precedence over the pattern (Feature 05, task 5)
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.
2026-09-15 18:12:16 -04:00
Joshua King 51b1388233 Fix: inverted ranges skip the overlap probe and hit the schema CHECK
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.
2026-09-15 18:03:49 -04:00
Joshua King 744a40dcb5 Presence override overlap rejection naming the conflict (Feature 05, task 4)
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.
2026-09-15 18:03:00 -04:00
Joshua King 6d48f433b7 Review fix: validate engine keys as child ids; every-day three-child walk
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.
2026-09-15 17:58:51 -04:00
Joshua King 540f59a353 PresenceEngine with is_present and the always-present default (Feature 05, task 3)
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.
2026-09-15 17:57:02 -04:00
Joshua King 5885d5e972 Anchor-date cycle position arithmetic (Feature 05, task 2)
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.
2026-09-15 17:51:28 -04:00
Joshua King cb3c347d61 Review fix: reject datetime anchors, freeze the pattern mapping
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.
2026-09-15 17:46:30 -04:00