Compare commits

...
1 Commits
Author SHA1 Message Date
orchestrator-agent 13ddf4f5ac Docs refresh: architecture, agents and README reflect what was built (Maestro 3b22e48b) (#214)
CI / ci (push) Successful in 8m0s
Co-authored-by: orchestrator-agent <20+orchestrator-agent@code.cubecraftcreations.com>
2026-10-12 10:58:25 -04:00
3 changed files with 104 additions and 22 deletions
+8 -4
View File
@@ -78,17 +78,18 @@ behaviour, exercise it (hit the endpoint, render the page).
| Path | Purpose |
| --- | --- |
| `cmd/server` | Entry point. Wiring only — no business logic. |
| `cmd/vapidgen` | Prints a new VAPID key pair for Web Push. |
| `internal/config` | `MEALQUEST_*` environment config; secrets from `*_FILE`. |
| `internal/server` | HTTP server, routes, `/health`. |
| `internal/frontend` | `go:embed` of the built PWA with SPA fallback. |
| `internal/*` (planned) | `auth`, `db`, `store`, `secretbox`, `household`, `dinners`, `recipes`, `ai/llm`, `ai/harness`, `ai/skills`, `suggest`, `plan`, `shopping`, `ideas`, `gcal`, `notify`, `push`, `chat`, `jobs`, `scheduler`. |
| `migrations/` | SQL migrations (planned). |
| `internal/*` | `auth`, `forwarded`, `secureurl`, `db`, `store`, `secretbox`, `household`, `dinners`, `recipes`, `ai/llm`, `ai/harness`, `ai/skills`, `ai/aisettings`, `suggest`, `plan`, `shopping`, `ideas`, `home`, `gcal`, `notify`, `push`, `chat`, `vapid`, `jobs`, `scheduler` — one line each in [ARCHITECTURE.md](docs/ARCHITECTURE.md#repository-layout). |
| `migrations/` | SQL migrations, embedded and applied at boot by `internal/store`; sqlc queries in `migrations/queries/`. |
| `web/` | React PWA source. |
| `deploy/` | Deployment files: compose stack, systemd units, [deploy runbook](deploy/RUNBOOK.md) and the automated deploy (`auto-deploy.sh` with `systemd/mealquest-auto-deploy.*`). |
| `docs/` | Project documentation, including [ARCHITECTURE.md](docs/ARCHITECTURE.md). |
| `design/` | Requirements and UI handoff. **Read-only reference** — do not edit. |
| `Dockerfile` | Multi-stage image build, at the repository root. |
| `Makefile` | `dev`, `test`, `build`, `docker`. |
| `Makefile` | `dev`, `test`, `build`, `docker`; `sqlc` and `sqlc-check` regenerate and check `internal/db`. |
| `.gitea/workflows/ci.yml` | CI quality floor. |
Design references:
@@ -104,7 +105,10 @@ Design references:
- **Task branches:** `task/<slug>_<task-uuid>`, branched from the feature branch.
- Task pull requests **squash-merge into the feature branch**.
- The feature pull request merges into `dev`.
- **Only `overseer` merges `dev` → `main`** (a release).
- **Only `overseer` merges `dev` → `main`** (a release). On the production
host, `mealquest-auto-deploy.timer` then deploys the new `main` once its
CI passes ([deploy/RUNBOOK.md](deploy/RUNBOOK.md) "Automated deploys");
nothing deploys `dev` or a feature branch.
- Never push directly to `dev` or `main`.
- Authors never review, approve, merge or close their own work.
- Never store credentials in the repository; environment or ignored files only.
+30 -3
View File
@@ -7,9 +7,27 @@ builds a weekly plan, pushes it to Google Calendar, and generates a shopping
list grouped by dinner, kept as a browsable weekly history. It ships as a
single Go service with the React PWA embedded in the binary.
> Status: this repository is the Feature 01 scaffold (Go service with
> `/health`, embedded PWA skeleton, Makefile, Dockerfile, CI). The planned
> application is described in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
Production runs at <https://mealquest.cubecraftlabs.com> for one household,
signed in through Authentik.
## Stack
- **Backend:** Go 1.25 (`CGO_ENABLED=0`, one static binary), chi router,
SQLite through `modernc.org/sqlite` with sqlc queries and embedded
migrations, Authentik OIDC, `log/slog`. AI calls go to an
OpenAI-compatible, Anthropic or Ollama endpoint over plain HTTP; recipes
come from SearXNG or Brave search and the recipe page's schema.org data.
Google Calendar, Google Chat and Web Push are the integrations.
- **Frontend:** React 19, TypeScript, Vite, React Router 7, TanStack Query 5,
`vite-plugin-pwa`, CSS Modules over the design tokens; tests with Vitest,
Testing Library and jsdom.
- **Deployment:** a Docker Compose stack (the service, Litestream
replication to the NAS, a SearXNG sidecar) behind Traefik, with systemd
units for the firewall, nightly media backup and the automated deploy of
`main`.
The approved dependency list is in [AGENTS.md](AGENTS.md); nothing else is
added without the owner's approval.
## Running it locally
@@ -28,6 +46,15 @@ invalid variable.
| `make test` | `gofmt` check, `go vet`, `go test -race`, web typecheck and tests. |
| `make build` | Build the PWA, embed it, build the static binary `bin/mealquest`. |
| `make docker` | Build the container image from the root `Dockerfile`. |
| `make sqlc-check` | Regenerate `internal/db` with sqlc and fail if it drifted. |
## Deploying and operating
[deploy/RUNBOOK.md](deploy/RUNBOOK.md) is the deploy runbook: build, ship and
restart, verifying the deployed version, rollback and the automated deploy.
Database restore, the boot configuration and other host procedures are in
CTXD: project `mealquest`, file `RUNBOOKS.MD`. Only `overseer` releases, by
merging `dev` into `main`.
## Where to read next
+66 -15
View File
@@ -2,10 +2,8 @@
This mirrors the architecture section of `CONTEXT.MD` in the CTXD project
`mealquest`, which is the source of record; if the two disagree, CTXD wins and
this file should be updated. It describes the **planned** application. The
repository today is the Feature 01 scaffold: `cmd/server`, `internal/config`,
`internal/server` (`/health`), `internal/frontend` (embedded PWA with SPA
fallback), the `web/` skeleton, `Makefile`, `Dockerfile` and CI.
this file should be updated. It describes the application as built; nothing
it names is still only planned.
Decision numbers (D-NNN) refer to `DECISIONS.MD` in CTXD.
@@ -46,20 +44,22 @@ headers, so sign-in works whichever peer the request arrives from.
| `cmd/vapidgen` | Prints a new VAPID key pair for Web Push. |
| `internal/config` | Env-only `MEALQUEST_*` config; secrets from `*_FILE`. |
| `internal/server` | HTTP server, router, middleware. |
| `internal/forwarded` | Client IP and scheme from `X-Forwarded-*`, trusted only from `MEALQUEST_TRUSTED_PROXY`. |
| `internal/secureurl` | The transport rule for URLs that carry credentials (`secureurl.Check`). |
| `internal/frontend` | Embedded PWA, SPA fallback. |
| `internal/auth` | Authentik OIDC, sessions, CSRF. |
| `internal/db`, `internal/store` | SQLite connections, sqlc queries. |
| `internal/db`, `internal/store` | sqlc-generated queries; SQLite connections, migrations at boot, transactions. |
| `internal/secretbox` | AES-256-GCM encryption for `_ct` columns. |
| `internal/household`, `internal/dinners`, `internal/recipes` | Domain. |
| `internal/ai/llm`, `internal/ai/harness`, `internal/ai/skills` | AI provider adapters, skill harness, skills. |
| `internal/suggest`, `internal/plan`, `internal/shopping`, `internal/ideas` | Feature services. |
| `internal/household`, `internal/dinners`, `internal/recipes` | Domain: household settings and local time, dinners (grounding, photo backfill), recipe search, fetch and grounding. |
| `internal/ai/llm`, `internal/ai/harness`, `internal/ai/skills`, `internal/ai/aisettings` | AI provider adapters, skill harness, skills, AI settings. |
| `internal/suggest`, `internal/plan`, `internal/shopping`, `internal/ideas`, `internal/home` | Feature services. |
| `internal/gcal`, `internal/notify`, `internal/push`, `internal/chat` | Integrations. |
| `internal/vapid` | VAPID key generation and validation. |
| `internal/jobs`, `internal/scheduler` | Background jobs and schedules. |
| `migrations/` | SQL migrations (no goose or golang-migrate). |
| `migrations/` | SQL migrations, embedded and applied at boot by `internal/store` (no goose or golang-migrate); sqlc queries in `migrations/queries/`. |
| `web/` | React PWA. |
| `deploy/` | Deployment files. |
| `docs/` | Documentation. |
| `deploy/` | Production compose stack, systemd units, the [deploy runbook](../deploy/RUNBOOK.md) and the automated deploy (see Deployment). |
| `docs/` | Documentation (this file). |
| `design/` | Requirements and UI handoff (read-only reference). |
## Backend stack and conventions
@@ -811,6 +811,20 @@ purge are fixed and not configurable.
Runs are recorded in `schedule_runs` and executed through `jobs`.
Job kinds (`internal/jobs`; each registered when `cmd/server` builds the
service named, before the runner starts). `POST /api/v1/jobs` refuses a
service-only kind; `GET /api/v1/jobs/{id}` reports any job.
| Kind | Registered by | Enqueued by | Service-only |
| --- | --- | --- | --- |
| `noop` | `jobs.RegisterBuiltins` | `POST /api/v1/jobs` (does nothing; proves the runner works) | no |
| `ground_dinner` | `dinners.Service` | Adding or renaming a `pending` dinner | no |
| `backfill_dinner_photos` | `dinners.Service` | `POST /api/v1/settings/photo-backfill` | yes |
| `suggestion_batch` | `suggest.Service` | `POST /api/v1/suggestions/batches`, a deck decision (replenish), `suggestions_weekly` | no |
| `draft_week` | `plan.Service` | `POST /api/v1/plans/{weekStart}/draft`, `plan_draft` | yes |
| `title_supply_run` | `shopping.Service` | Generating an untitled Supply run | yes |
| `ideas_from_ingredients`, `quick_ideas` | `ideas.Service` | `POST /api/v1/ideas/from-ingredients`, `POST /api/v1/ideas/quick` | yes |
`internal/scheduler` runs them. A schedule registers a name, a rule read
from the settings (`scheduler.Daily`, `Weekly` or `WeeklyForNextWeek`, or `DailyAt` for a time read from data) and a
run function; settings changes reach it through `SettingsService.Subscribe`
@@ -1319,10 +1333,12 @@ fewer the server answers 400 `invalid_settings` naming
`onboarding_completed` and changes nothing. Step 3 can be reached with
fewer — by its URL, or after another member removes a Regular — so it then
shows the server's reason with an "Add Regulars" button back to step 2.
Notification permission is never asked for on page load; until Feature 19
wires Web Push, "Turn on notifications" asks for nothing and says plainly
that notifications are not on yet, offering "Finish setting up" (which
completes onboarding as above) instead of a silent success. Offline on the
Notification permission is never asked for on page load. "Turn on
notifications" subscribes this device to Web Push from the tap and says
how it ended — on, blocked by the browser, or unsupported (on iPhone and
iPad, until MealQuest is on the Home Screen) — then offers only "Finish
setting up" (which completes onboarding as above); a dismissed permission
prompt can be asked again. Offline on the
saved pages the settings cannot be read (the service worker never caches `/api/v1/settings`),
so the saved pages open as they are.
@@ -1339,6 +1355,41 @@ are undone if the server refuses them. While any Regular is `pending`, the
list is read again every 3 seconds (not while a change is in flight), and
polling stops once none is.
## Deployment
Production is the compose project `mealquest` on 10.60.1.14
([`deploy/compose.yaml`](../deploy/compose.yaml)): the `mealquest` image built
from the root `Dockerfile`, `litestream` replicating the database to the
NAS over SFTP, and `searxng` on the internal network only. Configuration is
`deploy/.env` and file secrets in `deploy/secrets/`, neither committed. The
services never restart themselves; systemd units in `deploy/systemd/` own
the host:
| Unit | Does |
| --- | --- |
| `mealquest-firewall.service` | Restricts port 8090 to Traefik in `DOCKER-USER`; required before the stack starts. |
| `mealquest.service` | Starts and stops the compose stack. |
| `mealquest-media-backup.timer` / `.service` | Backs up cached photos to the NAS nightly at 03:30. |
| `mealquest-auto-deploy.timer` / `.service` | Automated deploy of `main` (below). |
Releases: only `overseer` merges `dev` into `main`. The image carries its
version (`git describe`: a release tag, else the short SHA), which
`/health` reports. A deploy follows
[`deploy/RUNBOOK.md`](../deploy/RUNBOOK.md) — build, pre-deploy restore
point, boot-config check, ship and restart, verify the deployed SHA and a
real screen — with rollback to the previous release. The automated deploy
([`deploy/auto-deploy.sh`](../deploy/auto-deploy.sh), installed as
`/usr/local/sbin/mealquest-auto-deploy`) runs those same steps unattended:
its timer checks every quarter hour (plus up to 5 minutes' random delay),
and deploys `refs/remotes/origin/main` only when it differs from the last
SHA in the deploy record `/home/overseer/mealquest-auto-deploy.log`, its
Gitea CI status is `success`, its last attempt did not fail, and
`mealquest.service` is active. It stops at the first failure and never
retries a failed SHA; `--ref <ref>` is the manual override. Installing,
seeding the record and enabling the timer are host steps in the runbook's
"Automated deploys". Database restore and the boot configuration are in
CTXD `RUNBOOKS.MD`.
## Boundaries and gotchas
- **Secrets never reach the client** — API keys, OAuth tokens and the Chat