|
| 1 | +# Project Notes |
| 2 | + |
| 3 | +This is the only hand-written project document in `docs/`. |
| 4 | + |
| 5 | +Keep this file short, current, and operational. Do not add planning docs, migration diaries, redesign notes, or parallel architecture narratives back into this directory. |
| 6 | + |
| 7 | +The only other file that belongs in `docs/` is the generated OpenAPI contract at [`docs/api/v1/openapi.yaml`](/Users/gil/versioned/html2rss/html2rss-web/docs/api/v1/openapi.yaml). |
| 8 | + |
| 9 | +## System Snapshot |
| 10 | + |
| 11 | +- Backend: Ruby + Roda under the `Html2rss::Web` namespace. |
| 12 | +- Frontend: Preact + Vite, built into `public/frontend`. |
| 13 | +- Feed extraction: delegated to the `html2rss` gem. |
| 14 | +- Distribution: Docker Compose / Dev Container first. |
| 15 | + |
| 16 | +## Source Of Truth |
| 17 | + |
| 18 | +- Runtime behavior: application code plus tests. |
| 19 | +- HTTP contract: request specs plus generated OpenAPI. |
| 20 | +- This file: contributor conventions and current project rules only. |
| 21 | + |
| 22 | +## Verification |
| 23 | + |
| 24 | +Primary local gate: |
| 25 | + |
| 26 | +```text |
| 27 | +docker compose -f .devcontainer/docker-compose.yml exec -T app make ready |
| 28 | +``` |
| 29 | + |
| 30 | +Useful commands: |
| 31 | + |
| 32 | +```text |
| 33 | +docker compose -f .devcontainer/docker-compose.yml exec -T app make setup |
| 34 | +docker compose -f .devcontainer/docker-compose.yml exec -T app make dev |
| 35 | +docker compose -f .devcontainer/docker-compose.yml exec -T app bundle exec rspec |
| 36 | +docker compose -f .devcontainer/docker-compose.yml exec -T app make openapi |
| 37 | +docker compose -f .devcontainer/docker-compose.yml exec -T app make openapi-verify |
| 38 | +docker compose -f .devcontainer/docker-compose.yml exec -T app make openapi-lint |
| 39 | +``` |
| 40 | + |
| 41 | +Frontend verification lives at `http://127.0.0.1:4001/` while the dev container is running. |
| 42 | + |
| 43 | +## Backend Structure Rules |
| 44 | + |
| 45 | +- `app/` is the Zeitwerk root for `Html2rss`. |
| 46 | +- `app/web/**` maps directly to `Html2rss::Web::*`. |
| 47 | +- Match constant, filename, and directory exactly. |
| 48 | +- Keep route composition in `app/web/routes/**`. |
| 49 | +- Keep `/api/v1` contract-specific code in `app/web/api/**`. |
| 50 | +- Keep feed fetching, caching, and orchestration in `app/web/feeds/**`. |
| 51 | +- Keep auth, token handling, SSRF strategy, and security logging in `app/web/security/**`. |
| 52 | +- Keep request-scoped context in `app/web/request/**`. |
| 53 | +- Keep boot/runtime setup in `app/web/boot/**`. |
| 54 | +- Do not create generic buckets such as `services`, `helpers`, `utils`, or `concerns`. |
| 55 | + |
| 56 | +## API Contract Rules |
| 57 | + |
| 58 | +- `docs/api/v1/openapi.yaml` is generated output, not hand-edited design prose. |
| 59 | +- Backend behavior and request specs define the contract. |
| 60 | +- Regenerate with `make openapi`. |
| 61 | +- Drift must fail with `make openapi-verify`. |
| 62 | +- Quality must fail with `make openapi-lint`. |
| 63 | +- Frontend generated client code under `frontend/src/api/generated` is machine-generated only. |
| 64 | + |
| 65 | +## Runtime Flags |
| 66 | + |
| 67 | +Managed flags: |
| 68 | + |
| 69 | +| Name | Env key | Type | Default | |
| 70 | +| --------------------------------- | --------------------------------- | -------------- | ---------------------------------------- | |
| 71 | +| `auto_source_enabled` | `AUTO_SOURCE_ENABLED` | boolean | `true` in development/test, else `false` | |
| 72 | +| `async_feed_refresh_enabled` | `ASYNC_FEED_REFRESH_ENABLED` | boolean | `false` | |
| 73 | +| `async_feed_refresh_stale_factor` | `ASYNC_FEED_REFRESH_STALE_FACTOR` | integer `>= 1` | `3` | |
| 74 | + |
| 75 | +Rules: |
| 76 | + |
| 77 | +- Invalid managed flag values must fail fast at boot. |
| 78 | +- Unknown managed feature-style env keys must fail fast at boot. |
| 79 | +- Add or change flags in code, tests, and this table together. |
| 80 | + |
| 81 | +## Observability Contract |
| 82 | + |
| 83 | +Canonical event fields: |
| 84 | + |
| 85 | +- `event_name` |
| 86 | +- `schema_version` |
| 87 | +- `request_id` |
| 88 | +- `route_group` |
| 89 | +- `actor` |
| 90 | +- `outcome` |
| 91 | + |
| 92 | +Optional request context fields: |
| 93 | + |
| 94 | +- `path` |
| 95 | +- `method` |
| 96 | +- `strategy` |
| 97 | +- `started_at` |
| 98 | +- `details` |
| 99 | + |
| 100 | +Critical-path event families: |
| 101 | + |
| 102 | +- auth |
| 103 | +- feed create |
| 104 | +- feed render |
| 105 | +- request errors |
| 106 | + |
| 107 | +## Documentation Policy |
| 108 | + |
| 109 | +- Prefer deleting stale docs over archiving them in-place. |
| 110 | +- If a rule matters to contributors, keep it here. |
| 111 | +- If a detail is generated from code, keep it out of prose docs. |
| 112 | +- If a design idea is temporary, keep it in the PR or issue, not under `docs/`. |
0 commit comments