openvibers/openvibe.shoes Commits

AGENTS.md: the context an agent starts from (#140)

OP OpenVibers committed 1 days ago · ad4fe66368 · 2 files, +101 −0

Adds `AGENTS.md` (115 lines) as the context an agent starts from, plus `CLAUDE.md` (`@AGENTS.md`).

It covers: what Shoes owns (catalogue, offers, daily snapshots, drops, watches, cited facts) and the
source/checkout limits it must never cross; the Deals/eBay/official-calendar/Watch/Today calls; the
PostgreSQL tables and job-run rule; the server-rendered frontend; SSO and the person's own token;
deploy; tests; conventions (all-in prices, first snapshot per day wins, cite or say "not known
yet"); and the no-raffle/no-affiliate/no-scraping pitfalls.

Checked: every cited path exists on main; no code, dependency or version changed.

Changes

ddiff --git a/AGENTS.md b/AGENTS.md
nnew file mode 100644
iindex 0000000..1941155
---- /dev/null
++++ b/AGENTS.md
@@@ -0,0 +1,100 @@
++# AGENTS.md — OpenVibe.Shoes
++
++## Project overview
++
++OpenVibe.Shoes is the network's shoe resource at `https://open.shoes`: one page per model with every
++store's all-in offer, Shoes' own daily price history and size-price curve, cited specs/fit/care notes,
++and an official drops calendar. A person can watch a price or paste a listing link, which Shoes hands
++to OpenVibe.Watch.
++
++- **Entry:** `server/index.js` (Express); app wiring in `server/http/router.js`; port `5180`,
++ service id `shoes`, env prefix `SHOES`.
++- **Pages:** `server/http/pages.js` (server-rendered HTML via `server/render/layout.js`/`html.js`,
++ `server/views/index.js`); own CSS in `public/css/`. No SPA and no build step.
++- **API:** `server/http/api.js` (`/api/v1`), OpenAPI in `server/http/openapi.js`, discovery in
++ `server/http/discovery.js` (robots, sitemap, llms.txt), SSO in `server/auth/`.
++- **Domain:** `server/domain/parse.js` (pure source parsers), `server/domain/drops.js` (release
++ calendar); jobs in `server/jobs/` (`offers.js`, `drops.js`, `ebay.js`).
++- **Identity:** `server/identity/` and `server/auth/` for Network sessions; the person's own token is
++ forwarded to Watch and Today.
++
++## Owns, does not own
++
++Owns the shoe catalogue, offers and daily snapshots, drops, watches stored on Shoes, cited facts and
++job-run records (`shoe_models`, `shoe_offers`, `shoe_snapshots`, `shoe_drops`, `shoe_watches`,
++`shoe_facts`, `shoe_job_runs`, `account_data_events`, `telemetry_samples`). It publishes a search
++index document and its share of account export/deletion.
++
++Does not own: store catalogues or prices (Deals and the official APIs are the sources), a person's
++general watch list (OpenVibe.Watch — Shoes forwards the link and keeps only its own `shw_` row), the
++calendar (OpenVibe.Today), or any checkout. **No raffle, draw, reservation or checkout is automated,
++ever; no affiliate links, no sponsored ordering and no Marketplace scraping.**
++
++## Who calls it, what it calls
++
++Public pages and `GET /api/v1/*` need no token; a signed-in person's writes (watch, remind) use their
++Network session. Shoes calls OpenVibe.Deals' public offer/product APIs, the eBay Browse API only
++when a developer key pair is configured, the official release calendar/rSS feeds, and the person's
++own Network token to call OpenVibe.Watch (`shw_` ↔ remote watch) and OpenVibe.Today (drop
++reminders). A missing source key turns that source off with a plain note, never an error.
++
++## Database and migrations
++
++PostgreSQL through `openvibe-sdk/db`; migrations `migrations/NNNN_*.sql` (0001 initial, 0002 search
++index, 0003 shares, 0004 outbox, 0005 product catalog), applied at boot with the owner role. Tests use
++PGlite; `npm run test:pg` runs the same suite through PgBouncer. Never edit a migration that has run;
++a schema removal waits for a contract migration. Each job writes one `shoe_job_runs` row (counts and
++errors) and refuses to start under `SHOES_DRILL=1`; tests call `runOnce()`.
++
++## Frontend loading model
++
++Every page is server-rendered HTML with the shared Frame (`openvibe-shared/shell`, navbar/footer,
++`/shared/*`); page-specific CSS is in `public/css`. There is no bundler and no route-feature loader.
++Interactive bits (watch form, reminder, the small price chart) are inline server output plus small
++scripts; keep pages usable without JavaScript and phone-first.
++
++## Auth
++
++Network SSO (RS256 JWT + session cookie) using the `shoes` OAuth client; public reads are anonymous.
++A person's Watch/Today call uses their own Network token — never a service token on their behalf. A
++session without a usable token can still save the watch on Shoes, marked "not connected". Forms are
++origin-checked; `/internal/events` is HMAC-verified and loopback-only.
++
++## Deploy
++
++Install `deploy/systemd/openvibe-shoes.service` and `deploy/nginx/open.shoes.conf`; the unit reads
++`/etc/openvibe/shoes.env`, works in `/opt/openvibe.shoes`, runs as `ubuntu:ubuntu` on
++`127.0.0.1:5180`. The vhost proxies `/api/`, `/auth/` and the forms with their own limits.
++`sudo ovhost deploy shoes` is the production path (git-checkout, install on lockfile change,
++restart, wait for `/api/ready`); rollback with `sudo ovhost rollback shoes --to <sha>`.
++
++## Commands and tests
++
++```bash
++npm ci --include=dev
++npm start # http://127.0.0.1:5180
++npm test # every test/*.test.js, PGlite, mock Network
++npm test -- importer # only matching files
++npm run test:pg # the same through PgBouncer
++npm run seed # one run of every job against live sources (needs the network)
++```
++
++`test/run.js` uses `openvibe-shared/test-runner` (90 s, serial); files get a temp PGlite database and
++an in-process Network mock, never the network. Tests cover the parsers and all-in price, snapshots
++(first check per offer per day wins), the jobs' stop/error behavior, the API and pages, Watch/Today
++forwarding, account export/deletion and the shared discovery artifacts. `VERBOSE=1` shows each file's
++output.
++
++## Conventions and pitfalls
++
++- CommonJS, 4-space indent, single quotes, semicolons; no linter. Paths and domain logic stay in
++ `server/domain/` so a parser is testable without HTTP.
++- **Prices are all-in** (price + stated shipping); keep currency and the source's own check time, and
++ mark a stale offer rather than hiding it.
++- **The first snapshot of an offer per day wins** (unique key); do not overwrite history.
++- **Cite or say "not known yet":** never invent a spec, fit note or release date; a parser that
++ cannot read a date leaves it out.
++- **Respect the source rules:** Deals public pages, eBay's official API only with keys, official
++ release pages; no scraping of Marketplace or Facebook, no affiliate URLs.
++- New environment variables go in `.env.example` and the README list; secrets live only in
++ `/etc/openvibe/shoes.env`.
ddiff --git a/CLAUDE.md b/CLAUDE.md
nnew file mode 100644
iindex 0000000..43c994c
---- /dev/null
++++ b/CLAUDE.md
@@@ -0,0 +1 @@