From b4bbd6d3f916d6b13c1108d2f92995842820582f Mon Sep 17 00:00:00 2001 From: Sam Rolfe Date: Thu, 15 Oct 2026 19:58:55 +1100 Subject: [PATCH] Phase 9: publish the documentation set (README, map, Outline, Vikunja) - README: Phase 9 status, the architecture diagram + map link, and the explicit "Teams (FR-15) is DEFERRED" statement. - docs/architecture.mmd + docs/diagrams/architecture.png (mmdc). - docs/archify/architecture.architecture.json -> docs/architecture-map.html (Archify showcase, 9/9 checks). - docs/outline/: the five AI-context documents (STE style). - docs/vikunja-tasks.json: the live Vikunja board (project 21). Co-Authored-By: Paperclip --- README.md | 73 +- docs/archify/architecture.architecture.json | 326 + docs/architecture-map.html | 13698 ++++++++++++++++++ docs/architecture.mmd | 30 + docs/diagrams/architecture.png | Bin 0 -> 71056 bytes docs/outline/00-index.md | 43 + docs/outline/01-system-overview.md | 70 + docs/outline/02-config-and-env.md | 101 + docs/outline/03-code-map.md | 82 + docs/outline/04-agent-runbook.md | 98 + docs/vikunja-tasks.json | 19 + 11 files changed, 14534 insertions(+), 6 deletions(-) create mode 100644 docs/archify/architecture.architecture.json create mode 100644 docs/architecture-map.html create mode 100644 docs/architecture.mmd create mode 100644 docs/diagrams/architecture.png create mode 100644 docs/outline/00-index.md create mode 100644 docs/outline/01-system-overview.md create mode 100644 docs/outline/02-config-and-env.md create mode 100644 docs/outline/03-code-map.md create mode 100644 docs/outline/04-agent-runbook.md create mode 100644 docs/vikunja-tasks.json diff --git a/README.md b/README.md index e30a9ef..e76f649 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,71 @@ # 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. +A school service for Harry and Finn: **Compass** gives the daily schedule, the +homework and the notices. 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 8** (the -> Hub API). See §11 of the spec for the phase order. +> **Teams (FR-15) is DEFERRED.** No Teams section is scanned. Compass answers +> what is due and overdue. This is a known gap, not a defect. + +> Build spec: [`AGENTS.md`](AGENTS.md). This repository is at **Phase 9** +> (documentation). See §11 of the spec for the phase order. + +## Status — Phase 9 + +Phase 9 publishes the project documentation across five places. + +| Layer | Artifact | Location | +|---|---|---| +| Technical README | This file | the Gitea repo | +| Architecture source | [`docs/architecture.mmd`](docs/architecture.mmd) | repo | +| Rendered diagram | [`docs/diagrams/architecture.png`](docs/diagrams/architecture.png) | repo | +| Interactive map | [`docs/architecture-map.html`](docs/architecture-map.html) | | +| Human note | Obsidian `010 inbox/` on `.27` | filed by the sorter | +| AI context | [`docs/outline/`](docs/outline/) | Outline collection *Compass / NHS (School)* | +| Tasks | [`docs/vikunja-tasks.json`](docs/vikunja-tasks.json) | Vikunja project id 21 | + +The daily trigger is **06:00** in `Australia/Melbourne` (n8n). The briefs go out +on **Email and WhatsApp** through Apprise on `.35:8210`: one brief per boy, plus +one combined brief to Jo and Sam. Important school events go to the Home +Assistant Local Calendar `calendar.school`. The Hub reads items through the +read-only `POST /ask` contract. + +The accepted scraper is **patchright** (headless Playwright), not the +pi-browser-harness: the harness cannot pass the Cloudflare managed challenge on +the Compass login POST. The Compass parent login required **no** two-factor code +during the Phase 2 proof. + +## Architecture + +```mermaid +flowchart LR + N8N["n8n on .13
06:00 daily trigger"] + SAM["Sam
browser dashboard"] + CADDY["Caddy on .35
school.lab.audasmedia.com.au"] + SVC["Compass / NHS service
Go + SQLite :8792"] + PATCH["patchright scraper
headless Playwright"] + COMPASS["Compass NHS
read-only login + pages"] + OMNI["OmniRoute
importance classification"] + HA["Home Assistant calendar
calendar.school"] + APPRISE["Apprise on .35:8210
Email + WhatsApp"] + BOYS["Harry and Finn
per-boy morning brief"] + PARENTS["Jo and Sam
combined morning brief"] + HUB["Hub :8788
read-only /ask"] + + N8N -->|"scripts/morning-brief.sh"| SVC + SAM -->|HTTPS| CADDY + CADDY --> SVC + SVC -->|scan| PATCH + PATCH -->|"login + fetch pages"| COMPASS + SVC -.->|"important only"| OMNI + SVC -->|"create event"| HA + SVC -->|briefs| APPRISE + APPRISE -->|"email + WhatsApp"| BOYS + APPRISE -->|"email + WhatsApp"| PARENTS + HUB -->|"Bearer /ask"| SVC +``` + +*Interactive map:* ## Status — Phase 8 @@ -126,7 +186,7 @@ Built and deployed on `.13`: - Its own container and port **8792**, behind Caddy at `school.lab.audasmedia.com.au`. -Not built yet (later phases): Teams (FR-15) and the Phase 9 documentation set. +Not built yet: Teams (FR-15). It is deferred on purpose. The service itself does **not** scan the school; n8n triggers the scan. ### Fail loudly (FR-12) @@ -155,6 +215,7 @@ 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) +docs/ phase notes, the architecture source and map, the Outline set, Vikunja tasks ``` ## Run diff --git a/docs/archify/architecture.architecture.json b/docs/archify/architecture.architecture.json new file mode 100644 index 0000000..0f5d5a3 --- /dev/null +++ b/docs/archify/architecture.architecture.json @@ -0,0 +1,326 @@ +{ + "schema_version": 1, + "diagram_type": "architecture", + "meta": { + "title": "Compass / NHS school service", + "output": "architecture-map.html", + "quality_profile": "showcase", + "viewBox": [ + 1180, + 610 + ], + "views": [ + { + "id": "morning-run", + "label": "Morning run", + "focus": [ + "n8n", + "service", + "patchright", + "compass", + "apprise", + "family" + ], + "note": "n8n runs the scan and the sends at 06:00 every day." + }, + { + "id": "read-path", + "label": "Read path", + "focus": [ + "sam", + "caddy", + "service", + "hub" + ], + "note": "Sam and the Hub both read the same Go service over HTTP." + }, + { + "id": "writes", + "label": "Writes outside the school", + "focus": [ + "service", + "ha", + "apprise", + "omni" + ], + "note": "The calendar write and the briefs are the only writes." + } + ] + }, + "components": [ + { + "id": "n8n", + "type": "external", + "label": "n8n (.13)", + "sublabel": "06:00 daily trigger", + "pos": [ + 40, + 200 + ], + "size": [ + 150, + 60 + ] + }, + { + "id": "sam", + "type": "external", + "label": "Sam", + "sublabel": "browser dashboard", + "pos": [ + 40, + 430 + ], + "size": [ + 150, + 60 + ] + }, + { + "id": "caddy", + "type": "security", + "label": "Caddy (.35)", + "sublabel": "session login", + "pos": [ + 250, + 315 + ], + "size": [ + 150, + 60 + ], + "tag": "school.lab" + }, + { + "id": "service", + "type": "backend", + "label": "Compass / NHS service", + "sublabel": "Go + SQLite :8792", + "pos": [ + 470, + 310 + ], + "size": [ + 165, + 70 + ], + "tag": "source of truth" + }, + { + "id": "patchright", + "type": "backend", + "label": "patchright scraper", + "sublabel": "headless Playwright", + "pos": [ + 700, + 315 + ], + "size": [ + 150, + 60 + ] + }, + { + "id": "compass", + "type": "external", + "label": "Compass NHS", + "sublabel": "read-only login + pages", + "pos": [ + 920, + 315 + ], + "size": [ + 160, + 60 + ] + }, + { + "id": "omni", + "type": "cloud", + "label": "OmniRoute", + "sublabel": "importance only", + "pos": [ + 470, + 120 + ], + "size": [ + 160, + 60 + ] + }, + { + "id": "ha", + "type": "cloud", + "label": "Home Assistant", + "sublabel": "calendar.school", + "pos": [ + 700, + 120 + ], + "size": [ + 160, + 60 + ] + }, + { + "id": "hub", + "type": "external", + "label": "Hub", + "sublabel": "read-only /ask :8788", + "pos": [ + 250, + 500 + ], + "size": [ + 150, + 60 + ] + }, + { + "id": "apprise", + "type": "cloud", + "label": "Apprise (.35)", + "sublabel": "Email + WhatsApp :8210", + "pos": [ + 470, + 500 + ], + "size": [ + 160, + 60 + ] + }, + { + "id": "family", + "type": "external", + "label": "Family briefs", + "sublabel": "per-boy + combined", + "pos": [ + 790, + 500 + ], + "size": [ + 160, + 60 + ] + } + ], + "boundaries": [ + { + "kind": "region", + "label": "Home lab - Docker on .13, Caddy and Apprise on .35", + "wraps": [ + "caddy", + "service", + "patchright", + "omni", + "ha", + "apprise", + "hub" + ] + } + ], + "connections": [ + { + "id": "n8n-to-service", + "from": "n8n", + "to": "service", + "label": "scripts/morning-brief.sh", + "variant": "dashed" + }, + { + "id": "sam-to-caddy", + "from": "sam", + "to": "caddy", + "label": "HTTPS", + "variant": "emphasis", + "fromSide": "top", + "toSide": "bottom" + }, + { + "id": "caddy-to-service", + "from": "caddy", + "to": "service" + }, + { + "id": "service-to-patchright", + "from": "service", + "to": "patchright", + "label": "scan", + "labelDy": 30 + }, + { + "id": "patchright-to-compass", + "from": "patchright", + "to": "compass", + "label": "login + fetch" + }, + { + "id": "service-to-omni", + "from": "service", + "to": "omni", + "label": "importance", + "variant": "dashed", + "fromSide": "top", + "toSide": "bottom" + }, + { + "id": "service-to-ha", + "from": "service", + "to": "ha", + "label": "create event", + "fromSide": "right", + "toSide": "left" + }, + { + "id": "service-to-apprise", + "from": "service", + "to": "apprise", + "label": "briefs", + "fromSide": "bottom", + "toSide": "top" + }, + { + "id": "apprise-to-family", + "from": "apprise", + "to": "family", + "label": "email + WhatsApp" + }, + { + "id": "hub-to-service", + "from": "hub", + "to": "service", + "label": "Bearer /ask", + "variant": "dashed" + } + ], + "cards": [ + { + "dot": "cyan", + "title": "Compass / NHS service", + "items": [ + "Go and SQLite are the source of truth", + "Go stores items, scans and the send log", + "Serves the dashboard, the Hub API and the briefs" + ] + }, + { + "dot": "amber", + "title": "Read-only against the school", + "items": [ + "patchright logs in and fetches pages only", + "The Cloudflare login POST needs a real browser", + "No post, message or submit ever runs" + ] + }, + { + "dot": "rose", + "title": "Deferred: Teams (FR-15)", + "items": [ + "Teams homework is DEFERRED and not built", + "Compass answers what is due and overdue", + "No Teams section is scanned" + ] + } + ] +} \ No newline at end of file diff --git a/docs/architecture-map.html b/docs/architecture-map.html new file mode 100644 index 0000000..e174a08 --- /dev/null +++ b/docs/architecture-map.html @@ -0,0 +1,13698 @@ + + + + + + + Compass / NHS school service Diagram + + + + + + + + + + + +
+ +
+
+
+

Compass / NHS school service

+
+
+ + + + + + +
+ + Compass / NHS school service + A architecture diagram generated by Archify. + + + + + + + + + + + + + + + + + + + + + + + + + Home lab - Docker on .13, Caddy and Apprise on .35 + + + + + + + + + + + + + + + + n8n (.13) · 06:00 daily trigger · Architecture component + + + + n8n (.13) + 06:00 daily trigger + + + + Sam · browser dashboard · Architecture component + + + + Sam + browser dashboard + + + + Caddy (.35) · session login · Home lab - Docker on .13, Caddy and Apprise on .35 · school.lab + + + + Caddy (.35) + session login + school.lab + + + + Compass / NHS service · Go + SQLite :8792 · Home lab - Docker on .13, Caddy and Apprise on .35 · source of truth + + + + Compass / NHS service + Go + SQLite :8792 + source of truth + + + + patchright scraper · headless Playwright · Home lab - Docker on .13, Caddy and Apprise on .35 + + + + patchright scraper + headless Playwright + + + + Compass NHS · read-only login + pages · Architecture component + + + + Compass NHS + read-only login + pages + + + + OmniRoute · importance only · Home lab - Docker on .13, Caddy and Apprise on .35 + + + + OmniRoute + importance only + + + + Home Assistant · calendar.school · Home lab - Docker on .13, Caddy and Apprise on .35 + + + + Home Assistant + calendar.school + + + + Hub · read-only /ask :8788 · Home lab - Docker on .13, Caddy and Apprise on .35 + + + + Hub + read-only /ask :8788 + + + + Apprise (.35) · Email + WhatsApp :8210 · Home lab - Docker on .13, Caddy and Apprise on .35 + + + + Apprise (.35) + Email + WhatsApp :8210 + + + + Family briefs · per-boy + combined · Architecture component + + + + Family briefs + per-boy + combined + + + + + + scripts/morning-brief.sh + + + + HTTPS + + + + + scan + + + + login + fetch + + + + importance + + + + create event + + + + briefs + + + + email + WhatsApp + + + + Bearer /ask + + + + + +

+ + + + + + + + +
+ + +
+
+
+
+

Compass / NHS service

+
+
    +
  • • Go and SQLite are the source of truth
  • +
  • • Go stores items, scans and the send log
  • +
  • • Serves the dashboard, the Hub API and the briefs
  • +
+
+ +
+
+
+

Read-only against the school

+
+
    +
  • • patchright logs in and fetches pages only
  • +
  • • The Cloudflare login POST needs a real browser
  • +
  • • No post, message or submit ever runs
  • +
+
+ +
+
+
+

Deferred: Teams (FR-15)

+
+
    +
  • • Teams homework is DEFERRED and not built
  • +
  • • Compass answers what is due and overdue
  • +
  • • No Teams section is scanned
  • +
+
+
+ +
+ + + + diff --git a/docs/architecture.mmd b/docs/architecture.mmd new file mode 100644 index 0000000..ca47681 --- /dev/null +++ b/docs/architecture.mmd @@ -0,0 +1,30 @@ +flowchart LR + N8N["n8n on .13
06:00 daily trigger"] + SAM["Sam
browser dashboard"] + CADDY["Caddy on .35
school.lab.audasmedia.com.au"] + SVC["Compass / NHS service
Go + SQLite :8792"] + PATCH["patchright scraper
headless Playwright"] + COMPASS["Compass NHS
read-only login + pages"] + OMNI["OmniRoute
importance classification"] + HA["Home Assistant calendar
calendar.school"] + APPRISE["Apprise on .35:8210
Email + WhatsApp"] + BOYS["Harry and Finn
per-boy morning brief"] + PARENTS["Jo and Sam
combined morning brief"] + HUB["Hub :8788
read-only /ask"] + TEAMS["Teams homework
FR-15 DEFERRED"] + + N8N -->|"scripts/morning-brief.sh"| SVC + SAM -->|HTTPS| CADDY + CADDY --> SVC + SVC -->|scan| PATCH + PATCH -->|"login + fetch pages"| COMPASS + SVC -.->|"important only"| OMNI + SVC -->|"create event"| HA + SVC -->|briefs| APPRISE + APPRISE -->|"email + WhatsApp"| BOYS + APPRISE -->|"email + WhatsApp"| PARENTS + HUB -->|"Bearer /ask"| SVC + TEAMS -.->|"not built"| SVC + + click SVC "https://maps.lab.audasmedia.com.au/compass_teams_nhs/docs/" "View Archify map" + click COMPASS "https://maps.lab.audasmedia.com.au/compass_teams_nhs/docs/" "View Archify map" diff --git a/docs/diagrams/architecture.png b/docs/diagrams/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..40c38e21eb5853089fa1aefb1629343f2a2349eb GIT binary patch literal 71056 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­!R$1d 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�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 **Target**: AI Agents +> **Writing Style**: ASD-STE100 Simplified Technical English. + +## Document Catalog (Inject Specific Document) + +Read this index first. Inject the exact file you need. Do not search every file. + +| Document Title | Path | Scope | +| :--- | :--- | :--- | +| **System Overview** | `docs/outline/01-system-overview.md` | States the purpose, the main parts and the live services. | +| **Configuration and Environment** | `docs/outline/02-config-and-env.md` | Lists variables, paths, ports and secrets. | +| **Code Map** | `docs/outline/03-code-map.md` | Shows the folder layout, entry points and dependencies. | +| **Agent Runbook** | `docs/outline/04-agent-runbook.md` | Gives the commands to build, test, deploy and restart. | + +## System Summary + +This project reads the school systems for two children, Harry and Finn. It stores +what is due, finds overdue work, writes important events to the family calendar, +and sends morning briefs. + +The service is one Go binary with SQLite. It runs in its own container on `.13`, +port 8792, behind Caddy on `.35` at `school.lab.audasmedia.com.au`. + +**Teams (FR-15) is DEFERRED.** The project does not scan Teams. Compass answers +what is due and overdue. + +## Core Configuration + +- **Env File**: `environment.d/school-service.env` (gitignored) +- **Env Example**: `environment.d/school-service.env.example` +- **Config Code**: `internal/config/` +- **Active Ports**: `8792` (service), `8210` (Apprise on `.35`), `8788` (Hub) +- **Database**: SQLite in the Docker volume `compass-teams-nhs-data` + +## Agent Restart Steps + +1. Read the Document Catalog above. Inject only the required files. +2. Check the checkout: `git status`. +3. Read the recent history: `git log -n 5 --oneline`. +4. Run the checks: `make test` and `make verify-phase8`. +5. Read the current state: `curl -s http://127.0.0.1:8792/health`. diff --git a/docs/outline/01-system-overview.md b/docs/outline/01-system-overview.md new file mode 100644 index 0000000..9382295 --- /dev/null +++ b/docs/outline/01-system-overview.md @@ -0,0 +1,70 @@ +# Compass NHS — System Overview + +## Purpose + +The service reads the school systems for Harry and Finn. It answers three +questions: what is due, what is overdue, and what important events are coming. + +Sam sees both children on one page. The family receives the briefs before work is +late. Nobody logs into Compass each morning. + +## Users + +| User | Role | +| :--- | :--- | +| Sam | Owner. Uses the dashboard. | +| Jo, Harry, Finn | Read-only. Receive the briefs. | + +## Main Parts + +| Part | Function | +| :--- | :--- | +| Go service | Serves the dashboard and the Hub API. Holds the state. | +| SQLite store | Keeps children, items, scans, notifications and calendar pushes. | +| patchright scraper | Logs into Compass and fetches the pages. | +| n8n workflow | Runs the scan and the sends at 06:00 daily. | +| Apprise | Sends the briefs on Email and WhatsApp. | +| Home Assistant | Holds the calendar `calendar.school`. | +| Hub | Calls the read-only `/ask` endpoint. | + +## The Data Flow + +1. n8n triggers `scripts/morning-brief.sh` at 06:00. +2. The script scans Compass for each child and stores the raw pages. +3. The parser turns the pages into items: tasks, messages and events. +4. The classifier marks important events. OmniRoute helps only here. +5. The brief builder makes one brief per boy and one combined brief. +6. Apprise sends the briefs on Email and WhatsApp. +7. The calendar sync writes today's and future important events to Home Assistant. + +## Read-Only Rule + +The service never writes to the school. It logs in and fetches pages only. The +scraper never posts, messages or submits anything. + +## Fail Loudly + +A missing scan or a failed scan is never an empty day. The dashboard, the briefs +and `/ask` each state the real scan state. A failed scan says so in plain words. + +## Teams + +**Teams (FR-15) is DEFERRED.** The project does not scan Teams. This is a known +gap, not a defect. Compass gives the schedule, the tasks and the due dates. + +## Live Targets + +| System | Address | +| :--- | :--- | +| Dashboard | `https://school.lab.audasmedia.com.au` | +| Service container | `.13`, port 8792 | +| Health | `http://127.0.0.1:8792/health` | +| Apprise API | `.35`, port 8210 | +| Hub inbound | `http://192.168.20.13:8788/ask` | + +## Repository + +- Git: `ssh://git@gitea.lab.audasmedia.com.au:2222/sam/compass_teams_nhs.git` +- Branch: `main` +- Spec: `AGENTS.md` +- Interactive map: `https://maps.lab.audasmedia.com.au/compass_teams_nhs/docs/` diff --git a/docs/outline/02-config-and-env.md b/docs/outline/02-config-and-env.md new file mode 100644 index 0000000..c810f05 --- /dev/null +++ b/docs/outline/02-config-and-env.md @@ -0,0 +1,101 @@ +# Compass NHS — Configuration and Environment + +## Rules for Agents + +1. Never write a credential, token, cookie or session value into a document. +2. Keep every secret in `environment.d/school-service.env`. The file is gitignored. +3. Copy the shape from `environment.d/school-service.env.example`. Never copy a value. +4. Docker Compose interpolates `$VAR` inside `env_file`. Escape each bcrypt `$` as `$$`. + +## Paths + +| Item | Path | +| :--- | :--- | +| Real env file | `environment.d/school-service.env` | +| Env example | `environment.d/school-service.env.example` | +| Config code | `internal/config/` | +| SQLite schema | `internal/store/migrations/` | +| Service binary | `/app/school` inside the container | +| Database | `/data/school.db` inside the container | +| Docker volume | `compass-teams-nhs-data` | + +## Ports + +| Port | System | Host | +| :--- | :--- | :--- | +| 8792 | This service | `.13` | +| 8210 | Apprise API | `.35` | +| 8788 | Hub inbound API | `.13` | +| 8123 | Home Assistant | `.30` | +| 5678 | n8n | `.13` | + +Caddy on `.35` publishes `school.lab.audasmedia.com.au` and proxies to +`192.168.20.13:8792`. + +## Variables + +### Service + +| Variable | Meaning | +| :--- | :--- | +| `SCHOOL_ADDR` | Listen address. Default `:8792`. | +| `SCHOOL_DB_PATH` | SQLite path. Container value `/data/school.db`. | +| `SCHOOL_SESSION_SECRET` | Signs the session cookie. Secret. Minimum 16 characters. | +| `SCHOOL_ADMIN_USERNAME` | The single dashboard login. | +| `SCHOOL_ADMIN_PASSWORD_HASH` | Bcrypt hash. Prefer this over the plaintext. | +| `SCHOOL_ADMIN_PASSWORD` | Plaintext fallback. | +| `SCHOOL_COOKIE_SECURE` | `true` behind HTTPS. | +| `SCHOOL_SESSION_TTL` | Session life. Default `12h`. | +| `SCHOOL_TIMEZONE` | Family timezone for due dates. | +| `SCHOOL_LOG_LEVEL` | Log level. | +| `SCHOOL_APP_ENV` | Environment name. | + +### Home Assistant Calendar + +| Variable | Meaning | +| :--- | :--- | +| `SCHOOL_HA_URL` | Home Assistant base URL. | +| `SCHOOL_HA_TOKEN` | Long-lived access token. Secret. | +| `SCHOOL_HA_CALENDAR_ENTITY` | Calendar entity. Default `calendar.school`. | +| `SCHOOL_HA_INSECURE` | Skips TLS verification. Use only for the internal `.lab` URL. | +| `SCHOOL_HA_TIMEOUT` | Per-request timeout. Default `20s`. | + +### Apprise + +| Variable | Meaning | +| :--- | :--- | +| `SCHOOL_APPRISE_URL` | Apprise API base. Default `http://192.168.20.35:8210`. | +| `SCHOOL_APPRISE_KEY` | Stored configuration key. Secret. | +| `SCHOOL_APPRISE_TOKEN` | Optional bearer token. | +| `SCHOOL_APPRISE_TIMEOUT` | Per-request timeout. Default `15s`. | +| `SCHOOL_APPRISE_CHANNELS` | `email,whatsapp`. | +| `SCHOOL_APPRISE_TAGS` | Recipients as `Name=tag-base`. | +| `SCHOOL_APPRISE_PARENTS` | Who receives the combined brief. `Jo,Sam`. | +| `SCHOOL_NOTIFY_TOKEN` | Bearer token for `POST /notify/morning`. Secret. | + +### Hub API + +| Variable | Meaning | +| :--- | :--- | +| `SCHOOL_HUB_TOKEN` | Shared bearer token. Secret. Empty turns the Hub endpoints off with `503`. | + +The Hub holds the same token as `HUB_PROJECT_COMPASS_NHS_TOKEN`. The Hub reaches +this service through `HUB_PROJECTS=compass_nhs=http://192.168.20.13:8792`. + +## Secrets Outside This Repo + +| Secret | Location | +| :--- | :--- | +| Apprise destinations (address, number, SMTP) | Apprise stored configuration on `.35` | +| Hub token copy | `~/.config/environment.d/10-secrets.conf` on `.13` | +| Compass account and password | Paperclip secret or the env file | +| n8n trigger SSH key | `/home/sam/.ssh/id_ed25519_school_scan` | + +The Apprise destinations stay outside this repo on purpose. This service names a +tag such as `harry_email` only. + +## Environment Access for an Agent + +The service does not scan the school. n8n runs `scripts/morning-brief.sh` +through a restricted SSH key. The key has a forced command and `restrict`. It +cannot open a shell. diff --git a/docs/outline/03-code-map.md b/docs/outline/03-code-map.md new file mode 100644 index 0000000..6752983 --- /dev/null +++ b/docs/outline/03-code-map.md @@ -0,0 +1,82 @@ +# Compass NHS — Code Map + +## Module + +`gitea.lab.audasmedia.com.au/sam/compass_teams_nhs` + +Language: Go 1.25. Database: SQLite through `modernc.org/sqlite`. Passwords use +`golang.org/x/crypto/bcrypt`. There is no other direct dependency. + +## Entry Point + +`cmd/school/main.go` holds the entry point. The first argument selects a +subcommand. With no argument it starts the HTTP service. + +| Subcommand | Function | +| :--- | :--- | +| `run` (default) | Starts the HTTP service, migrates the schema and seeds the children. | +| `parse` | Turns stored raw pages into items. | +| `session` | Shows or clears the harness session per child. | +| `scan` | Fetches and parses one child or both. | +| `items` | Reads or deletes items. | +| `brief` | Builds a brief. | +| `notify` | Sends briefs and shows the send status. | +| `calendar` | Pushes events to Home Assistant. | +| `hash-password` | Prints a bcrypt hash. | +| `gen-secret` | Prints a fresh secret. | +| `version` | Prints the version. | + +## Folders + +| Folder | Function | +| :--- | :--- | +| `cmd/school/` | One file per subcommand plus `main.go`. | +| `internal/web/` | HTTP server, handlers, HTMX templates, static assets. Holds `hub_api.go`. | +| `internal/auth/` | Session login with signed cookies and CSRF. | +| `internal/store/` | SQLite open, migrations and queries. | +| `internal/store/migrations/` | `001_init`, `002_items_parse`, `003_calendar`, `004_notifications_send`. | +| `internal/parse/` | HTML to items. Readings for tasks, news, calendar and importance. | +| `internal/llm/` | OmniRoute client for importance classification only. | +| `internal/dashboard/` | Builds the per-child buckets. Classifies overdue and due-today. | +| `internal/brief/` | Builds and delivers the morning briefs. | +| `internal/apprise/` | Apprise API client. Uses tags and redacts errors. | +| `internal/calendar/` | Home Assistant client and the idempotent sync. | +| `internal/hub/` | The Hub contract: capabilities and read-only answers. | +| `internal/config/` | Reads the environment. | +| `deploy/` | Caddy snippet, n8n workflow, import and key scripts, verify scripts. | +| `scripts/` | Scrapers, parse and morning-brief wrappers, calendar push. | + +## The Read-Only Boundary + +`internal/hub` depends on a `Reader` interface. The interface has three read +methods and no write method. Test `TestHubAskReadsOnly` proves that `/ask` does +not change the `items`, `scans` or `notifications` row counts. + +## The Scraper + +`scripts/compass-fetch-patchright.mjs` logs into Compass with patchright. The +login POST meets a Cloudflare managed challenge. patchright, a headless +Playwright build, passes the challenge. The pi-browser-harness does not. + +The Compass parent login required no two-factor code during the Phase 2 proof. + +## Database Tables + +| Table | Holds | +| :--- | :--- | +| `children` | id, name, year, Compass id. | +| `items` | Child, type, subject, teacher, title, body, due date, URL. | +| `scans` | Start time, child, pages fetched, items found, error. | +| `notifications` | Recipient, scope, summary, channel, status, sent time. | +| `session` | Harness session state per child. | +| `calendar_pushes` | Calendar write attempts, result and error. | + +The raw Compass pages are not in the database. The fetcher writes them to +`var/raw///` on the host. Each folder holds the HTML, a meta +file per page, and a manifest with a SHA-256 and a byte count per page. + +## Tests + +Run `make test`. The suite uses standard Go tests. The tests cover the store, +the parser, the dashboard buckets, the brief, the Apprise client, the calendar +sync, the Hub API and the read-only rule. diff --git a/docs/outline/04-agent-runbook.md b/docs/outline/04-agent-runbook.md new file mode 100644 index 0000000..20e02f0 --- /dev/null +++ b/docs/outline/04-agent-runbook.md @@ -0,0 +1,98 @@ +# Compass NHS — Agent Runbook + +## First Checks + +Run these commands before any change. + +```bash +git -C /home/sam/paseo/projects/compass_teams_nhs status +git -C /home/sam/paseo/projects/compass_teams_nhs log -n 5 --oneline +curl -s http://127.0.0.1:8792/health +``` + +`/health` must return `"ok": true`. It also shows the scan state and +`hub_api`. A red scan state is real data, not a test failure. + +## Build and Test + +The host `.13` has no Go toolchain. Build and test inside `golang:1.25-alpine` +through `make`. Bound every Docker command. + +```bash +make test # go test ./... in golang:1.25-alpine +make vet # go vet ./... +make build # build the container image +``` + +## Deploy and Restart + +```bash +make up # docker compose up -d --build +make ps # container state +make logs # follow the service log +make restart # restart the service only +``` + +The container is `compass-teams-nhs`. The image tag is +`compass-teams-nhs/school:0.6.1-phase8`. + +## Operate + +```bash +make scan # fetch and parse both children +make scan SCAN_CHILD=Finn # one child +make parse CHILD=Harry RUN=latest # parse stored pages +make preview # print the briefs, send nothing +make notify # send the briefs from the last scan +make morning-brief # scan, then send (the n8n job) +make calendar CALENDAR_ARGS='push --dry-run' +make calendar CALENDAR_ARGS='status' +``` + +The send log: + +```bash +docker exec compass-teams-nhs /app/school notify status +``` + +## Verify + +```bash +make verify-phase1 # /health, the login, the Caddy route +make verify-phase8 # /capabilities, /ask, /notify auth +make verify-phase8 ARGS=--send # also deliver one real /notify message +``` + +## The n8n Schedule + +```bash +make n8n-key # dry run: print the key plan +make n8n-key APPLY=1 # create the restricted key +make n8n-import # dry run: print the import plan +make n8n-import APPLY=1 # import, publish and restart n8n +docker exec n8n n8n list:workflow --active=true +``` + +The workflow runs at 06:00 in `Australia/Melbourne`. The trigger key has a forced +command and `restrict`. It can run one command only. + +## Documentation + +```bash +# Publish the maps and diagrams to the LAN maps site. +~/.agents/skills/project-docs/scripts/publish-maps.sh \ + /home/sam/paseo/projects/compass_teams_nhs + +# Force one Obsidian sorter pass on .27. +ssh sam@192.168.20.27 'obsidian-sort watch --once' +``` + +## Rules + +1. **Read-only against the school.** Never post, message or submit. +2. **Fail loudly.** Never report "nothing due" when a scan failed. +3. **Never commit a secret.** Credentials, tokens and cookies stay in + `environment.d/school-service.env` or a Paperclip secret. +4. **Never write personal page content into a document.** +5. Use one commit and one push for a change. End the message with + `Co-Authored-By: Paperclip `. diff --git a/docs/vikunja-tasks.json b/docs/vikunja-tasks.json new file mode 100644 index 0000000..a917c1b --- /dev/null +++ b/docs/vikunja-tasks.json @@ -0,0 +1,19 @@ +{ + "title": "Compass / NHS (School) - Human Task Overview", + "description": "High-level task checklist for human review. The live board is the Vikunja project 'Compass / NHS (School)' (id 21). This file is the repo copy.", + "vikunja_project": { + "name": "Compass / NHS (School)", + "id": 21, + "url": "https://vikunja.lab.audasmedia.com.au/projects/21" + }, + "teams_status": "DEFERRED - FR-15 is not built. No Teams section is scanned.", + "tasks": [ + { "id": 196, "title": "Open the dashboard on a phone and confirm overdue work is first for Harry and Finn", "done": false }, + { "id": 197, "title": "Confirm the 06:00 n8n run sends the per-boy and combined briefs on Email and WhatsApp", "done": false }, + { "id": 198, "title": "Check the important school events appear in the Home Assistant calendar calendar.school", "done": false }, + { "id": 199, "title": "Ask the Hub 'what does Harry have on today at school?' and check the answer", "done": false }, + { "id": 200, "title": "Confirm a failed scan shows an error and never an empty day", "done": false }, + { "id": 201, "title": "Review the published docs: README, Obsidian note, Archify map and Outline collection", "done": false }, + { "id": 202, "title": "Decide the Teams scope (FR-15): build it, or close the project with Teams deferred", "done": false } + ] +}