Files
compass_teams_nhs/docs/phase-6b7b-notes.md
Sam Rolfe 047af0f31d 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>
2026-10-16 10:25:55 +11:00

244 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 6b/7b — per-child periods, decoded brief, idempotent calendar writes
Status: **done** (AUD-119). Supersedes the scope notes in
[`phase-6-calendar-notes.md`](phase-6-calendar-notes.md) and
[`phase-7-notify-notes.md`](phase-7-notify-notes.md) where they differ.
Spec: `AGENTS.md` §4 FR-8, FR-9, FR-16, FR-17, FR-18; §7 data model; §11
Phase 6/7. Image `compass-teams-nhs/school:0.6.2-phase6b7b`.
## 1. Periods are per child (FR-16) — the bug fixed
The Compass **week calendar** is school-wide: its lesson grid belongs to the
eldest child, so every child's items carried Harry's Year 11 periods. Finn (Year
7) was shown `11MGMAC2 - B11 - GT`.
The per-child timetable is the **day widget** Compass renders on each child's own
pages (`/Records/User.aspx?userId=…` and the Learning Tasks page), labelled
"Today":
```
8:55: 1 - 7SCF - H14LAB - KNI
```
- `internal/parse/daywidget.go` reads that widget from the child's own pages and
stores the periods against that child.
- `internal/parse/calendar.go` now drops routine lessons from the school-wide
week calendar and keeps only real school events.
- A re-parse deletes the child's leaked routine `calendar`-page rows, so the
wrong periods cannot survive.
The day-widget label prints a 12-hour clock with no AM/PM (`1:26` is the
afternoon period), so the parser applies the school-day rule: 1–6 are PM, 7–11
are AM, 12 is noon. The widget's grid offset is *not* used, because its start
hour moves with the day's first event (07:00 on one page, 06:00 on another).
## 2. The decode map (FR-17)
`codes` (migration `005_codes.sql`) is the editable map: `subject`, `room`,
`teacher` and `period`. It is seeded on first read and never overwrites a
correction. An unknown code renders as the **raw code**, never blank.
```
=== FINN ===
PERIODS TODAY
- Period 1 — Year 7 Science — Room H14LAB — Mr KNI (08:55)
...
- Period 4 — Year 7 Physical Education — Gym 4 — Mr LTA (13:26)
```
Sam edits it without a rebuild:
```bash
docker exec compass-teams-nhs /app/school codes list
docker exec compass-teams-nhs /app/school codes set --kind teacher --code KNI --name 'Mr Knight'
docker exec compass-teams-nhs /app/school codes delete --kind room --code Z9
```
Teacher rows are placeholders (`KNI` -> `Mr KNI`) because Compass gives only
initials; Sam replaces them with surnames. Real unknown-code fallback from the
live brief: Harry's period 2 teacher field is `GT AGE_13`, and `AGE_13` is not
mapped, so it renders raw (`Mr GT / AGE_13`).
Seed size: 65 rows — 23 subjects, 22 teachers, 18 rooms, 2 period labels.
## 3. The calendar (FR-8)
For **each child separately**, every pass writes to `calendar.school`:
- that day's **periods**;
- **work due** — any future date;
- **work overdue**;
- **major school events** (concerts, excursions, exams).
Selection: `internal/store/calendar.go` adds `CalendarPeriodCandidates` (today's
day-widget periods) and `CalendarTaskCandidates` (outstanding work with a due
date); the existing `CalendarCandidates` keeps the important school events.
Submitted work and work with no due date are never written.
Idempotency is unchanged and still uses the stable per-item key: an item with a
Home Assistant event id is not a candidate, and an event whose description
carries `source: [compass-nhs:<item id>]` is adopted instead of written again.
Periods and work are per child; a school-wide event is still written once and
recorded against every child.
`scripts/morning-brief.sh` now runs **scan → calendar push → notify**, so the
6:00 pass writes the calendar. `scripts/calendar-push.sh events` gained `--past`
so overdue work (which sits at its Compass due date) can be read back.
## Evidence (2026-10-15 UTC / 2026-10-16 AEDT)
### The rendered brief — real pass
`Finn` block, first, with his own decoded Year 7 periods:
```
=== FINN ===
PERIODS TODAY
- Period 1 — Year 7 Science — Room H14LAB — Mr KNI (08:55)
- Period 2 — Year 7 Design — Room B1 — Mr PSI (09:54)
- Connect — Year 7 Tutorial — Room G7 — Mr KDI (11:18)
- Period 3 — Year 7 English — Room G7 — Mr DCA (11:47)
- Period 4 — Year 7 Physical Education — Gym 4 — Mr LTA (13:26)
- Period 5 — Year 7 Drama — Theatre — EBA (14:20)
EVENTS TODAY
- JAZZ ENSEMBLE - ENS - KWO (07:45)
- JUNIOR GUITAR ENSEMBLE - THEATRE - HHL, OFG (07:45)
- HHL GUITAR E - IM3 - HHL (09:54)
OVERDUE
- 7PE PHYSICAL EDUCATION: CAT 3: Cardiorespiratory Fitness (due Thu 03 Sep 16:00) [overdue]
- 7DES PRODUCT DESIGN: CAT 2: Eco Light - Research and Development (due Thu 17 Sep 16:00) [overdue]
DUE IN FUTURE
- 7DRA DRAMA: CAT 2: Ancient Greek Theatre (due Mon 26 Oct 16:00)
- 7TUT CONNECT: Learning Behaviour Report - Self Reflection Term 4 (due Tue 03 Nov 11:45)
- 7DES PRODUCT DESIGN: CAT 3: Eco Light - Making and Evaluating (due Thu 19 Nov 15:15)
- 7MA MATHEMATICS: CAT 6: Measurement and Geometry Test (due Fri 20 Nov 16:00)
- 7DRA DRAMA: CAT 3: Performance or Research Project (due Mon 07 Dec 16:00)
- 8 task(s) with no due date (see the dashboard)
```
`Harry` block, second, with his own Year 11 periods:
```
=== HARRY ===
PERIODS TODAY
- Period 1 — Year 11 General Maths — Room B11 — Mr GT / SMH (08:55)
- Period 2 — Year 11 General Maths — Room B11 — Mr GT / AGE_13 (09:54)
- Connect — Year 11 Tutorial — Room M6 — Mr ANE (11:18)
- Period 3 — Year 11 Art Creative Practice — Room C1 — Mr BDR (11:47)
- Period 4 — Year 11 English — Room H3 — Mr CHA (13:26)
- Period 5 — Year 11 Music — Room A1 — Mr JBR (14:20)
EVENTS TODAY
- none
```
The combined brief for Jo and Sam carries the same two blocks, `=== FINN ===`
first and `=== HARRY ===` second, never interleaved. The undated tail is one
line with a count (`17 task(s) with no due date …`), never the old 20+ dump.
### The Home Assistant calendar — per child (summary + start)
Read back from the Home Assistant API. 35 events in total.
Finn — 16 events:
```
2026-09-03T16:00 Finn — 7PE PHYSICAL EDUCATION: CAT 3: Cardiorespiratory Fitness
2026-09-17T16:00 Finn — 7DES PRODUCT DESIGN: CAT 2: Eco Light - Research and Development
2026-10-16T07:45 Finn — JAZZ ENSEMBLE - ENS - KWO
2026-10-16T07:45 Finn — JUNIOR GUITAR ENSEMBLE - THEATRE - HHL, OFG
2026-10-16T08:55 Finn — Period 1 — Year 7 Science — Room H14LAB — Mr KNI
2026-10-16T09:54 Finn — HHL GUITAR E - IM3 - HHL
2026-10-16T09:54 Finn — Period 2 — Year 7 Design — Room B1 — Mr PSI
2026-10-16T11:18 Finn — Connect — Year 7 Tutorial — Room G7 — Mr KDI
2026-10-16T11:47 Finn — Period 3 — Year 7 English — Room G7 — Mr DCA
2026-10-16T13:26 Finn — Period 4 — Year 7 Physical Education — Gym 4 — Mr LTA
2026-10-16T14:20 Finn — Period 5 — Year 7 Drama — Theatre — EBA
2026-10-26T16:00 Finn — 7DRA DRAMA: CAT 2: Ancient Greek Theatre
2026-11-03T11:45 Finn — 7TUT CONNECT: Learning Behaviour Report - Self Reflection Term 4
2026-11-19T15:15 Finn — 7DES PRODUCT DESIGN: CAT 3: Eco Light - Making and Evaluating
2026-11-20T16:00 Finn — 7MA MATHEMATICS: CAT 6: Measurement and Geometry Test
2026-12-07T16:00 Finn — 7DRA DRAMA: CAT 3: Performance or Research Project
```
Harry — 17 events:
```
2026-06-09T12:00 Harry — 11SBIO 11 SCIENCE - BIOLOGY: Unit 1 Biology Exam
2026-09-01T16:00 Harry — 11XMUS 11 PERF ARTS - MUSIC: SAC #2: Creating
2026-09-01T16:00 Harry — 11XMUS 11 PERF ARTS - MUSIC: VCE Unit #2 Outcome #2 Creating
2026-10-05T16:00 Harry — 11XMUS 11 PERF ARTS - MUSIC: SAC #1: Performing
2026-10-05T16:00 Harry — 11XMUS 11 PERF ARTS - MUSIC: VCE Unit #2 Outcome #1 Performing
2026-10-09T16:00 Harry — 11HLEG 11 HUMANITIES - LEGAL STUDIES: SAC 6: Human Rights Presentation
2026-10-16T08:55 Harry — Period 1 — Year 11 General Maths — Room B11 — Mr GT / SMH
2026-10-16T09:54 Harry — Period 2 — Year 11 General Maths — Room B11 — Mr GT / AGE_13
2026-10-16T11:18 Harry — Connect — Year 11 Tutorial — Room M6 — Mr ANE
2026-10-16T11:47 Harry — Period 3 — Year 11 Art Creative Practice — Room C1 — Mr BDR
2026-10-16T13:26 Harry — Period 4 — Year 11 English — Room H3 — Mr CHA
2026-10-16T14:20 Harry — Period 5 — Year 11 Music — Room A1 — Mr JBR
2026-10-19T16:00 Harry — 11EENG 11 ENGLISH - ENGLISH: SAC 3: Responding to Texts: The Handmaid's Tale
2026-10-30T16:00 Harry — 11MGMA 11 MATHS - GENERAL MATHS: SAC 4: Networks
2026-11-03T10:45 Harry — 11TUT CONNECT: Learning Behaviour Report - Self Reflection Term 4
2026-11-11T16:00 Harry — 11XMUS 11 PERF ARTS - MUSIC: SAC #3: Analysing and Responding
2026-11-11T16:00 Harry — 11XMUS 11 PERF ARTS - MUSIC: VCE Unit #2 Outcome #3 Analysing and Responding
```
School-wide — 2 events (written once, shared by both children):
```
2026-10-15T15:05 Senior Sub-School Community Service for Uniform Infringement - A2 - PSU, CMA, JGE, LHT, QLM, ECU
2026-10-15T18:00 End of Year Senior Music Concert - HALL (Senior Music Concert) - KSH, CFR, AMG, OFG, NBA, CNI, JDF, SLT, RLE
```
### The 3-run idempotency proof
Three real passes (`scripts/scan-compass.sh` then `scripts/calendar-push.sh
push`), read back after each:
| Pass | `candidates` | `created` | `failed` | HA events in `calendar.school` |
|---|---|---|---|---|
| 1 | 33 | 33 | 0 | 35 |
| 2 | 0 | 0 | 0 | 35 |
| 3 | 0 | 0 | 0 | 35 |
Runs 2 and 3 added nothing; the event list after run 2 and after run 3 is
byte-identical.
## Files changed
```
internal/store/migrations/005_codes.sql the editable codes table
internal/store/codes.go CodeMap, seed, list/set/delete
internal/store/calendar.go period + work candidate queries, leak cleanup
internal/store/store.go DeleteItemsFromPages
internal/period/period.go lesson-label split (period/subject/room/teacher)
internal/decode/decode.go plain-English lesson rendering + raw fallback
internal/parse/daywidget.go the per-child "Today" widget
internal/parse/parse.go, calendar.go, importance.go per-child hook, week-calendar filter
internal/dashboard/dashboard.go brief buckets + the required 4-section renderers
internal/brief/brief.go Finn-first order, codes decoder, new renderers
internal/calendar/sync.go periods + due + overdue + events, decoded, once
internal/calendar/ha.go Event JSON exposes start/end for evidence
cmd/school/{main,parse,calendar,codes,items,notify}.go wiring, codes CLI, purge by page
scripts/{morning-brief,scan-compass,calendar-push,parse-compass}.sh calendar step, version
docker-compose.yml, Makefile image 0.6.2-phase6b7b
```
## Not tested
- A **real email/WhatsApp send** of the new brief was not repeated; the send path
is unchanged and was last proven in Phase 7. The dry run prints exactly what is
sent.
- The **6:00 n8n job** was not re-published. It calls `scripts/morning-brief.sh`,
which now includes the calendar step; Sam owns the shared n8n service.
- The **week calendar** is still not fetched by the daily scan, so a *new*
major event is only written if a run fetches that page. The two existing
school-wide events remain in the calendar.
- **Teacher names** are placeholders; only Sam can supply the surnames.