# Compass / NHS A school service for Harry and Finn: **Compass** for the daily schedule and notices, **Teams** for homework and assignments. It stores what is due per child, puts overdue work first, and tells the family before work is late. > Build spec: [`AGENTS.md`](AGENTS.md). This repository is at **Phase 7** (notify > and send via Apprise). See §11 of the spec for the phase order. ## Status — Phase 7 Phase 7 builds and sends the morning briefs through the house **Apprise** API on `.35:8210` (FR-9, FR-14): one brief per boy, plus one combined brief to Jo and Sam, on **Email and WhatsApp**. Every attempt is recorded in `notifications` and shown on the dashboard, so a failed send is loud rather than silent (FR-12). `POST /notify/morning` is the endpoint the 6:00am n8n schedule calls. See [`docs/phase-7-notify-notes.md`](docs/phase-7-notify-notes.md). ```bash make notify # send the briefs from the latest scan make preview # print the briefs, send nothing make morning-brief # scan both children, then send (the n8n job) # Delivery log: per recipient per channel, plus the scan state. docker exec compass-teams-nhs /app/school notify status ``` The destinations live in the Apprise configuration, not here: this service only names a tag such as `harry_email` (AGENTS.md §10 rule 3). ## Status — Phase 6 Phase 6 writes the **important** school events to **Sam's Home Assistant calendar** through the Home Assistant REST API (FR-8). It is idempotent (a re-run never duplicates an event) and it fails loudly (a failed write is shown on the dashboard and recorded in `calendar_pushes`). See [`docs/phase-6-calendar-notes.md`](docs/phase-6-calendar-notes.md). The target is a **Home Assistant Local Calendar**, `calendar.school` (added 2026-10-15 after Sam chose it over the Google-backed entities that Home Assistant had). The entity stays configurable with `SCHOOL_HA_CALENDAR_ENTITY`. ```bash scripts/calendar-push.sh entities # list the writable calendar entities scripts/calendar-push.sh status # target, reachability, pending, last push scripts/calendar-push.sh push --dry-run scripts/calendar-push.sh push # write the pending important events scripts/calendar-push.sh events # read the calendar back make calendar CALENDAR_ARGS='push --dry-run' ``` ## Status — Phase 5 Phase 5 adds the per-child dashboard: one tab per child, **overdue first**, then due today, due this week and messages, plus the item detail screen and the Delete actions. See [`docs/phase-5-dashboard-notes.md`](docs/phase-5-dashboard-notes.md). ```bash # Phone-width evidence screenshots (writes under var/, git-ignored). SCHOOL_USER=sam SCHOOL_PASS=... node scripts/screenshot-phase5.mjs # Build a draft brief for one child. docker exec compass-teams-nhs /app/school brief build --child Harry ``` ## Status — Phase 4 Phase 4 fetches and parses **Harry and Finn** with a stored session per child, and stores every item against the right `children.id`. See [`docs/phase-4-both-children-notes.md`](docs/phase-4-both-children-notes.md). ```bash make scan # both children make scan SCAN_CHILD=Finn # one child school session show # session state per child (metadata only, no secrets) ``` ## Status — Phase 3 Phase 3 parses the raw pages stored by Phase 2 into the SQLite `items` table (tasks, messages, events). See [`docs/phase-3-parse-notes.md`](docs/phase-3-parse-notes.md). ```bash scripts/parse-compass.sh --child Harry --run latest --classify auto ``` ## Status — Phase 1 Built and deployed on `.13`: - Go service, SQLite schema for `children`, `items`, `scans`, `notifications`, `session` (`internal/store/migrations/001_init.sql`). - The service's **own session login** — a signed cookie, not SSO. - Server-rendered **HTMX** shell using the Family Home Lab design tokens (`internal/web/static/tokens.css`), phone-friendly with dark mode. - `GET /health`. - Its own container and port **8792**, behind Caddy at `school.lab.audasmedia.com.au`. Not built yet (later phases): the Hub API. The service itself does **not** scan the school; n8n triggers the scan. Teams (FR-15) is a separate source. ### Fail loudly (FR-12) A missing or failed scan is shown as an explicit warning or error, never as an empty day. The dashboard reads the `scans` table and shows one of: - **NO SCAN HAS RUN YET** — amber banner. - **SCAN FAILED** — red banner with the failure on record. - **Last scan completed** — green banner. ## Layout ``` cmd/school/ service entry point (run, parse, session, scan, items, hash-password, gen-secret) internal/config/ environment configuration internal/store/ SQLite: schema, migrations, queries internal/parse/ HTML -> structured items (tasks, messages, events) internal/llm/ OmniRoute client (importance classification only) internal/apprise/ Apprise API client (tag-based, redacts errors) internal/brief/ build + deliver the morning briefs (FR-9, FR-14) internal/calendar/ Home Assistant calendar client + the idempotent sync internal/auth/ session login (signed cookies, CSRF) internal/web/ HTTP handlers, templates, static assets deploy/ Caddy snippet, the n8n workflow, deployment notes, verify script scripts/ Compass fetchers, the parse and morning-brief wrappers, the calendar push environment.d/ env example (the real file is gitignored) ``` ## Run ```bash cp environment.d/school-service.env.example environment.d/school-service.env chmod 600 environment.d/school-service.env # then fill in the secrets docker compose up -d --build ``` Generate secrets with the built image: ```bash docker run --rm --entrypoint /app/school compass-teams-nhs/school:0.5.0-phase7 gen-secret docker run --rm --entrypoint /app/school compass-teams-nhs/school:0.5.0-phase7 hash-password 'the-password' ``` Note: Docker Compose interpolates `$VAR` inside `env_file`. A bcrypt hash contains `$`, so escape each one as `$$` in the env file. ## Verify ```bash make verify-phase1 ``` It checks the container is healthy, `GET /health` returns 200, the login page renders and a session login succeeds, and Caddy serves the public URL. ## Rules - **Read-only against the school.** Never post, message, or submit anything. - A failed scan must be loud — never report an empty day as "nothing due". - School credentials and harness session state are secrets; never commit them. - This project has its own folder, container, and port.