Task e63a8e64 (Feature: Multiple presence patterns per child), task-branch PR into the feature branch.
Done-condition: a child holds zero or more patterns (name, kind home|away, cycle 1..4, anchor, per-week weekday set); overrides win; away beats home; a child with no patterns is present every day; a child with home patterns but none covering the date is absent (old single-schedule behaviour); migration 9 converts every presence_schedules row into a home pattern named "Home schedule", retires the old table, and proves presence results unchanged for a pre-migration fixture; admin CRUD routes for patterns with 422 naming the field and 404 for unknown child/pattern; old one-per-child route removed; overrides routes unchanged; TZ=UTC uv run pytest -q passes.
Developer evidence at ef6940d937: TZ=UTC uv run pytest -q = 1976 passed, 0 failed; mutation checks confirm the suite catches a wrong default rule (15 fail), ISO-week arithmetic (10 fail) and home-before-away (2 fail); new tests/test_presence_patterns.py covers the owner scenario week by week, the 2026->2027 boundary, the 2028 leap day, no-patterns, overrides-beat-patterns and away-beats-home.
Spec amendment flagged by the developer and accepted by the orchestrator: rule 4 is "a child with at least one home pattern, none covering the date, is absent", not "otherwise present" — otherwise a home pattern could never make a child absent and the unchanged-migration test could not pass.
Task e63a8e64 (Feature: Multiple presence patterns per child), task-branch PR into the feature branch.
Done-condition: a child holds zero or more patterns (name, kind home|away, cycle 1..4, anchor, per-week weekday set); overrides win; away beats home; a child with no patterns is present every day; a child with home patterns but none covering the date is absent (old single-schedule behaviour); migration 9 converts every presence_schedules row into a home pattern named "Home schedule", retires the old table, and proves presence results unchanged for a pre-migration fixture; admin CRUD routes for patterns with 422 naming the field and 404 for unknown child/pattern; old one-per-child route removed; overrides routes unchanged; TZ=UTC uv run pytest -q passes.
Developer evidence at ef6940d937502c20feed5f25059097d282621e14: TZ=UTC uv run pytest -q = 1976 passed, 0 failed; mutation checks confirm the suite catches a wrong default rule (15 fail), ISO-week arithmetic (10 fail) and home-before-away (2 fail); new tests/test_presence_patterns.py covers the owner scenario week by week, the 2026->2027 boundary, the 2028 leap day, no-patterns, overrides-beat-patterns and away-beats-home.
Spec amendment flagged by the developer and accepted by the orchestrator: rule 4 is "a child with at least one home pattern, none covering the date, is absent", not "otherwise present" — otherwise a home pattern could never make a child absent and the unchanged-migration test could not pass.
A child now carries zero or more home/away presence patterns instead of
at most one schedule. Migration 9 creates presence_patterns, copies every
presence_schedules row across as a 'home' pattern named "Home schedule"
(cycle, anchor and pattern verbatim), and drops the old table in one
transaction. Migration 1 keeps creating the retired table from a frozen
copy of its DDL; schema.py no longer declares it.
PresenceEngine resolves override -> any covering away pattern (absent)
-> any covering home pattern (present) -> otherwise absent when the child
has a home pattern, else present. The home-pattern default keeps every
migrated child's presence unchanged.
The admin plane's one-per-child presence-schedule routes are replaced by
pattern CRUD: GET/POST /children/{child_id}/presence-patterns and
PATCH/DELETE /presence-patterns/{pattern_id}.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The admin client still called the removed one-per-child
/presence-schedule route, so the Schedule tab could not load or save
(review finding F1 on PR #231). The client now uses the presence-pattern
routes: list and create under /children/{id}/presence-patterns, and
PATCH / DELETE on /presence-patterns/{id}.
The Schedule screen lists every pattern of the selected child (name,
Home/Away, cycle length, weekday set) with Add, Edit and Delete, and a
pushed pattern editor sets name, kind, cycle length, anchor date and the
per-week weekday set. Edits send only the changed fields. The month grid
shows the combined presence with the core's first-match rule: override,
then any covering away pattern, then any covering home pattern, then
away when the child has a home pattern, else home. The grid keeps the
Sunday-first week; stored weekdays stay Monday=0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The create and edit pattern models typed weekday values as lax int, so
JSON true, "1" and 1.0 were coerced to weekday 1. Type them StrictInt
so each is a 422 naming the pattern field, and add create and edit
regression cases for bool, string, float and out-of-range weekdays.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pattern editor disabled Save unless a weekday was selected, but an
all-empty weekday set is valid in the core model and the API, and
migration 9 produces one for a legacy all-absent schedule. Save now only
requires a name (plus a valid anchor date and a change to save).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Task e63a8e64 (Feature: Multiple presence patterns per child), task-branch PR into the feature branch.
Done-condition: a child holds zero or more patterns (name, kind home|away, cycle 1..4, anchor, per-week weekday set); overrides win; away beats home; a child with no patterns is present every day; a child with home patterns but none covering the date is absent (old single-schedule behaviour); migration 9 converts every presence_schedules row into a home pattern named "Home schedule", retires the old table, and proves presence results unchanged for a pre-migration fixture; admin CRUD routes for patterns with 422 naming the field and 404 for unknown child/pattern; old one-per-child route removed; overrides routes unchanged; TZ=UTC uv run pytest -q passes.
Developer evidence at
ef6940d937: TZ=UTC uv run pytest -q = 1976 passed, 0 failed; mutation checks confirm the suite catches a wrong default rule (15 fail), ISO-week arithmetic (10 fail) and home-before-away (2 fail); new tests/test_presence_patterns.py covers the owner scenario week by week, the 2026->2027 boundary, the 2028 leap day, no-patterns, overrides-beat-patterns and away-beats-home.Spec amendment flagged by the developer and accepted by the orchestrator: rule 4 is "a child with at least one home pattern, none covering the date, is absent", not "otherwise present" — otherwise a home pattern could never make a child absent and the unchanged-migration test could not pass.
A child now carries zero or more home/away presence patterns instead of at most one schedule. Migration 9 creates presence_patterns, copies every presence_schedules row across as a 'home' pattern named "Home schedule" (cycle, anchor and pattern verbatim), and drops the old table in one transaction. Migration 1 keeps creating the retired table from a frozen copy of its DDL; schema.py no longer declares it. PresenceEngine resolves override -> any covering away pattern (absent) -> any covering home pattern (present) -> otherwise absent when the child has a home pattern, else present. The home-pattern default keeps every migrated child's presence unchanged. The admin plane's one-per-child presence-schedule routes are replaced by pattern CRUD: GET/POST /children/{child_id}/presence-patterns and PATCH/DELETE /presence-patterns/{pattern_id}. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>Independent Codex gpt-5.6-terra review completed for this exact commit.