Phase 6b/7b: per-child decoded periods, separate brief blocks, idempotent calendar (AUD-119)

- Parse each child's own "Today" day widget for the timetable (FR-16); drop the
  school-wide week calendar's lesson grid and clean the leaked routine rows.
- Add the editable codes map (FR-17): subjects, rooms, teachers and period
  labels, seeded, an unknown code falls back to the raw code; `school codes` CLI.
- Brief: Finn's block first, then Harry's, four sections always present (FR-9);
  periods decoded into plain English; the undated tail is one counted line (FR-18).
- Calendar: write periods + work due + work overdue + major events, per child,
  on every pass. Identity is the stable per-item key, so a re-run never re-inserts
  (FR-8). morning-brief.sh now runs scan -> calendar push -> notify.
- Day-widget labels are 12-hour: 1-6 read as PM, fixing afternoon periods.

Proven live on .13: image 0.6.2-phase6b7b; both children's briefs decoded and
separate; 35 events in calendar.school; runs 2 and 3 of the scan added nothing.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
2026-10-16 10:25:55 +11:00
parent 4950755b3d
commit 047af0f31d
40 changed files with 2049 additions and 160 deletions

View File

@@ -84,13 +84,16 @@ Items marked **not verified** need a decision or a test.
| FR-6 | Detect overdue work and work due today and this week. |
| FR-7 | Show a per-child view: overdue, due today, due this week, and messages. |
| FR-8 | Write to a **dedicated Home Assistant calendar** (suggested `School`), per child, on **EVERY pass**: (a) **that day's periods**, (b) **work due** — any future date, (c) **work overdue**, (d) **major events** (concerts, excursions, exams). Every item carries a **date from Compass**, so the same item is seen on many passes — a piece of work due in two weeks, or a concert, appears on **every** pass until it passes. **It must never be re-inserted.** Identity is a **stable per-item key**, not the date. |
| FR-9 | Send a **morning brief to each boy** (Harry, Finn) **by email**, in this order: (a) **today's periods**; (b) **today's major events**; (c) **work overdue**; (d) **work due in the future**. Lists only, no fluff. |
| FR-9 | Send a brief **for each boy, SEPARATED — Finn's block first, then Harry's**. **Never interleave them** in one list. Each child's block has, in this order: (a) **today's periods**, **decoded into plain English**; (b) **today's major events**; (c) **work overdue**; (d) **work due in the future**. Lists only, no fluff. |
| FR-10 | Provide a **Delete** action and a **Send** action on items and summaries. |
| FR-11 | Hub API: `GET /capabilities`, `POST /ask` (read-only), `POST /notify`. |
| FR-12 | Fail **loudly** on a scrape failure. Never report "nothing due" when the scan failed. |
| FR-13 | Log every scan: time, child, pages, items, errors. |
| FR-14 | Send a **combined brief** for both boys to **Jo and Sam** via Apprise. |
| FR-15 | **Teams — DEFERRED.** Do not build. Compass Learning Tasks supplies the due work (see §13). |
| FR-16 | **Periods are PER-CHILD.** Each child's timetable comes from **their own** schedule page. Never treat it as school-wide. Finn is Year 7, Harry is Year 11 — they must **never** show the same periods. |
| FR-17 | **Decode periods into plain English** from an editable `codes` map: subject code → subject name, room code → room, teacher initials → teacher name. An unknown code renders as the **raw code**, never blank. |
| FR-18 | **Drop the "NO DUE DATE" dump.** Do not list an unbounded set of undated tasks. If shown at all, cap it to a single line with a count, linking to the dashboard. |
## 5. Screens and UX flow
@@ -166,12 +169,25 @@ Real examples from the Phase 2 capture:
Decoding `11MGMAC2`: `11` = Year level · `M` = Faculty (Maths) · `GM` = Subject
(General Maths) · `AC2` = timetable block.
**Decoding is OPTIONAL and NOT yet instructed.** The raw codes are what Compass
supplies, and rendering them as-is (`11XMUSA6 · A1 · JBR`) is acceptable.
**Decoding is REQUIRED (FR-17).** Sam wants periods in plain English, not codes.
If Sam later asks for readable names, store them as an **editable `codes` map** —
never hardcode, because initials and room codes change between years. An unknown code
must render as the **raw code**, never blank.
Store the mappings as an **editable `codes` map** — never hardcode, because teacher
initials and room codes change between years and new subject codes appear each term.
Examples to seed it:
| Raw | Reads as |
|---|---|
| `7SCF` | Year 7 Science |
| `7DESF` | Year 7 Design |
| `7TUTF` | Year 7 Tutorial |
| `11MGMAC2` | Year 11 General Maths |
| `11XMUSA6` | Year 11 Music |
| `B11` / `M6` / `A1` | Room B11 / M6 / A1 |
| `GT` / `ANE` / `JBR` | teacher surname |
An unknown code renders as the **raw code** — never blank. Seed the map in Phase 6b
and let Sam correct it in the dashboard.
## 8. Integrations
@@ -302,8 +318,16 @@ Resolved:
key, never the date or title.
15. **Brief order (FR-9):** today's periods → today's major events → work overdue →
future due. Lists only.
16. **Codes are NOT decoded (yet).** Raw Compass codes (`11XMUSA6 · A1 · JBR`) are
acceptable. Decoding was raised as a question, not an instruction — see §7.
16. **Codes ARE decoded (FR-17).** Periods must read as plain English:
"Period 1 — Year 11 General Maths — Room B11 — Mr GT". Editable `codes` map, never
hardcoded, unknown codes render raw.
17. **Kids are separated in the email.** Finn's block first, then Harry's. Never
interleaved — the current combined single list is wrong.
18. **Periods are per-child (FR-16).** A bug was found where Finn's brief carried
Harry's Year 11 periods. Finn is Year 7 (`7SCF - H14LAB - KNI`). The timetable
must come from each child's OWN page, never treated as school-wide.
19. **No undated-task dump (FR-18).** The current brief lists 20+ "no due date" items,
truncated mid-list. Drop it, or cap to one line with a count.
13. **Not spoken unless asked.** The HA calendar stays silent unless Sam adds an
automation. Google Calendar is separate and already speaks.
14. **Email is the report channel.** Each boy gets his own by email; Jo and Sam get