Files
compass_teams_nhs/docs/phase-4-both-children-notes.md
Sam Rolfe 207a4978a9 Phase 4: per-child sessions, scan both children, items separated
Fetch and parse Harry and Finn with a stored Compass session per child and
store every item against the right children.id.

- cmd/school/session.go + internal/store/session.go: read and write the
  per-child harness session state in the SQLite `session` table. `show`
  prints metadata only; the state is never printed.
- scripts/compass-fetch-patchright.mjs: reuse the stored session, refresh it
  after login, resolve the child's userId from the parent page by name, and
  keep the shared calendar out of the per-child run (it would leak the
  eldest's timetable into the younger child).
- scripts/scan-compass.sh + make scan: scan one child or both; restore the
  session, fetch, persist the session, then parse. A failing child is
  recorded in `scans` with ok = 0 and the script exits non-zero (FR-12).
- cmd/school/scan.go: `school scan record` writes a fetch failure to the
  scans table. cmd/school/items.go: `school items purge` removes rows that a
  shared page stored against the wrong child.
- internal/store/store_test.go: prove items and sessions are separated by
  child. docs/phase-4-both-children-notes.md; README + image tag 0.3.0-phase4.

Live evidence: Harry 70 tasks / 1 message / 35 events (6 overdue); Finn
42 tasks / 1 message / 0 events (2 overdue). Finn's subjects are Year 7,
Harry's are Year 11. Both sessions stored.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-15 17:40:52 +11:00

3.9 KiB

Phase 4 — both children, per-child sessions

Status: done. The Compass scan now fetches and parses Harry and Finn with a stored session per child, and every item is stored against the right children.id. See AUD-108.

What runs

# Both children, with the stored session per child.
make scan

# One child only (a scan can target one child).
make scan SCAN_CHILD=Finn

# Same thing without make.
scripts/scan-compass.sh
scripts/scan-compass.sh --child Harry --child Finn --classify none

scripts/scan-compass.sh runs the patchright fetcher on the host and the school subcommands in a one-off container. It never restarts the service.

Per-child session (FR-2, §7 session table)

The Compass login is one parent account, but each child gets its own browser session state so a scan is independent and targetable.

Step Command Notes
Restore school session get --child NAME --out FILE Reads session.state; writes a 0600 file. No row means "log in fresh".
Fetch node scripts/compass-fetch-patchright.mjs --child NAME --state FILE Loads the state; logs in only when Compass rejects it; saves the refreshed state.
Persist school session put --child NAME --file FILE Upserts session.state for that child.
Inspect school session show [--child NAME] [--json] Prints bytes, cookie and origin counts, and updated_at. Never the state.

The session state is a secret. It lives in the database volume only. No command prints it, and the handoff file exists only inside the run's scratch directory.

What each run stores

One run directory per child under var/raw/<child>/<runId>/ (git-ignored):

Page Source Becomes
home parent landing page message rows (news feed)
learning-tasks-all POST /Services/LearningTasks.svc/GetAllLearningTasksByUserId task rows (authoritative per-child list)
child-profile /Records/User.aspx?userId=N no grid; used only when it carries the legacy grid
learning-tasks /Records/UserNew.aspx?userId=N#learningTasks task rows (legacy grid fallback)

The per-child Compass userId is discovered from the parent landing page by child name (Harrison → Harry, Finnegan → Finn) and recorded in the manifest.

Why the calendar is not fetched per child

The parent calendar page renders only the calendars that the parent has enabled. In this account only Harry's calendar is enabled; Finn's is hidden. Fetching that page inside Finn's run therefore stored Harry's Year 11 timetable against Finn — a leak between children.

Until per-child calendar attribution exists, the shared calendar stays out of the per-child loop. Harry's events remain from Phase 3. A caller can still fetch it explicitly with the fetcher's --pages option. The school items purge command removes rows that a shared page stored against the wrong child.

Fail loudly (FR-12)

  • A fetch failure records scans.ok = 0 with the message through school scan record --child NAME --ok=false --message TEXT.
  • A parse failure records its own failed scan inside school parse.
  • The scan script finishes every child, prints each failure by name, and exits non-zero. A failed child is never shown as an empty day.

Evidence (2026-10-15, live)

scripts/scan-compass.sh --classify auto
Child tasks messages events overdue (home-page recon.)
Harry 70 1 35 6
Finn 42 1 0 2

Rows are separated by child_id. A check for items with no matching child returns 0. Finn's task subjects are all Year 7 (7MA, 7DRA, 7DES); Harry's are all Year 11 (11MGMA, 11HLEG, 11EENG).

Known gaps

  • children.compass_id is not populated yet. The manifest holds the userId per run; a later phase can copy it into the children table.
  • Per-child calendar attribution is deferred (see above).
  • Teams (FR-15) is a separate source and is not scanned here.