Files
royalroadupdates/AGENTS.md
2026-07-12 02:49:54 +02:00

103 lines
5.3 KiB
Markdown

# Agent Instructions — `royalroadupdates`
## Project Purpose
A small, self-contained Go CLI tool that checks RoyalRoad stories from a
PocketBase bookmarks collection and reports their last-update times.
## Repository Layout
```
.
├── cmd/royalroadupdates/ Entry point and integration tests
│ ├── main.go - CLI bootstrap, flag parsing (--json)
│ └── e2e_test.go - End-to-end test: compiles binary, runs it,
│ asserts JSON shape and entry count
├── internal/
│ ├── pocketbase/
│ │ └── client.go - Authenticates and queries the
│ │ /api/collections/bookmarks/records endpoint.
│ │ Accepts filter, perPage, and field params.
│ │ Returns a list of structs with URL fields.
│ ├── royalroad/
│ │ └── scraper.go - Given a URL, fetches the page, confirms it
│ │ is a RoyalRoad fiction page, extracts the
│ │ JSON-LD script block, parses name and
│ │ dateModified into a struct.
│ │ Returns zero-value + error for any failure
│ │ so the caller can skip silently.
│ ├── formatter/
│ │ ├── table.go - Renders []Story as a plain-text aligned
│ │ │ table with relative-time "Last Update"
│ │ │ column.
│ │ ├── json.go - Renders []Story as JSON (used with --json).
│ │ └── relative.go - Computes "X minutes/hours/days/months/years
│ │ ago" strings from time.Time.
│ └── app.go - Orchestrates the full pipeline:
│ 1. Load credentials (compile-time vars).
│ 2. Query PocketBase for URLs.
│ 3. Iterate URLs sequentially.
│ 4. Collect valid RoyalRoad results.
│ 5. Sort by dateModified descending.
│ 6. Dispatch to appropriate formatter.
├── .env - Real credentials (gitignored).
├── .env.example - Template showing required env vars.
├── .gitignore - Must include .env and the built binary.
├── Makefile - All build, test, and check targets.
├── go.mod - Standard Go module file.
└── go.sum - Standard Go checksum file.
```
## Code Style Rules
- **Formatting:** All Go source files must pass `gofmt`. The `make check` target
will reject any diff.
- **Imports:** Group standard library first, then third-party (none expected),
then local/internal. `goimports` ordering is acceptable.
- **Naming:** Exported identifiers use PascalCase; unexported use camelCase.
Acronyms like HTTP, URL, JSON keep all-caps when exported (`FetchURL`,
`ParseJSON`).
- **Error handling:** Always check errors explicitly. Do not swallow errors
silently unless the design calls for silent skipping (RoyalRoad per-item
failures). Return errors wrapped with `fmt.Errorf("...: %w", err)` for
context.
- **Comments:** Every exported type and function has a concise GoDoc comment.
Internal helpers may omit comments if their purpose is obvious from the name.
- **No globals:** Avoid package-level mutable state. Pass dependencies (HTTP
client, configuration) as struct fields or constructor parameters.
- **HTTP client reuse:** Create one `http.Client` with a configured timeout and
share it across requests. Do not use `http.DefaultClient` unwrapped.
## Build Rules
- **No local Go installation.** Use `make` targets which wrap
`docker run --rm -v $(pwd):/src -w /src golang:1.22`.
- **Credential injection:** The Makefile reads `.env` and passes values as
`-ldflags '-X main.PB_URL=... -X main.PB_USER=... -X main.PB_PASSWORD=...'`.
- **Output:** The compiled binary is written to `./royalroadupdates` in the
repository root.
- **Targets:**
- `make build` — compile the binary.
- `make check` — full quality loop (format, vet, test, e2e test).
- `make clean` — remove the compiled binary.
## Testing Rules
- **Unit tests:** Cover pure helpers (e.g., `relative.go`, URL filtering logic)
with table-driven tests. Use `t.Parallel()` where independent.
- **End-to-end test (`e2e_test.go`):**
- Uses the `.env` credentials and hits real services.
- Compiles the binary via `os/exec` so it tests the actual build path.
- Runs `./royalroadupdates --json`.
- Assertions:
1. Exit code 0.
2. Stderr is empty.
3. Stdout is valid JSON.
4. JSON is a non-empty array.
5. Array contains at least 3 entries.
6. Each entry has a non-empty `name` and a parseable `date`.
- Since this test hits live services, it is inherently non-deterministic in
data content but deterministic in shape. Treat network/Cloudflare flakiness
as a signal to tune delays rather than a code defect.
- **Fix-and-rerun cycle:** If `make check` fails at any step, fix the root cause
and rerun `make check` until all gates pass.