Files
jbrechtel dbc2026422
Build and Deploy / build-and-deploy (push) Successful in 13m50s
Format code with fourmolu
2026-05-27 21:31:00 -04:00

116 lines
4.1 KiB
Markdown

# AGENTS.md
## Project: Roux
A personal/family recipe management site. See [README.org](./README.org) for full project description.
### Key facts
- **Backend**: Haskell + SQLite
- **Frontend**: Static HTML + Pico CSS
- **Recipe format**: Cooklang (https://cooklang.org/)
- **Development**: Docker-based; nothing required locally except Docker
- **Subprojects**:
- `cooklang-hs/` — Cooklang parser in Haskell
- `roux-server/` — Web server, file indexing, HTML rendering
### Development environment
All Haskell tooling runs via the `hs` wrapper script, which runs inside the
[flipstone/haskell-tools](https://github.com/flipstone/haskell-tools) Docker
image. Adopted from the pattern in the Atlas project.
#### Setup
1. Create the `hs` wrapper script in the project root:
```bash
cat > hs << 'SCRIPT'
#!/usr/bin/env bash
# Thin wrapper to run Haskell tooling (stack, hpack, fourmolu, hlint, ...)
# inside the flipstone/haskell-tools Docker image. Usage: ./hs <cmd> [args]
# e.g. ./hs stack build, ./hs stack test, ./hs hpack, ./hs fourmolu
set -euo pipefail
IMAGE="${HAWAT_HASKELL_TOOLS_IMAGE:-ghcr.io/flipstone/haskell-tools:debian-ghc-9.10.3-5d6640d}"
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
STACK_ROOT_HOST="${PROJECT_DIR}/.stack-root"
mkdir -p "${STACK_ROOT_HOST}"
exec docker run --rm -i $([ -t 0 ] && printf -- -t) \
-v "${PROJECT_DIR}:/work" \
-v "${STACK_ROOT_HOST}:/stack-root" \
-e STACK_ROOT=/stack-root \
-w /work \
"${IMAGE}" \
"$@"
SCRIPT
chmod +x hs
```
2. Create `.stack-root/` in the project root to persist GHC/cache across runs:
```bash
mkdir -p .stack-root
```
3. Initialize a Haskell project with `stack` or `hpack`:
```bash
./hs stack new cooklang-hs simple --dir cooklang-hs
# or for an existing project:
./hs stack build
```
#### Usage examples
```bash
./hs stack build # Build all subprojects
./hs stack test # Run tests
./hs stack ghci # REPL
./hs hpack # Regenerate .cabal from package.yaml
./hs fourmolu -i **/*.hs # Format Haskell sources
./hs hlint src/ # Lint sources
```
#### Environment variables
- `HAWAT_HASKELL_TOOLS_IMAGE` — override the Docker image tag (defaults to
`ghcr.io/flipstone/haskell-tools:debian-ghc-9.10.3-5d6640d`)
### Agent conventions
- Prefer the `hs` wrapper over local tooling — match the Docker-first dev environment.
- Run `./hs stack build` and `./hs stack test` before marking work as complete.
- When adding a new subproject (e.g. `roux-server`), initialize it with `./hs stack new`.
- Use `hpack` (via `./hs hpack`) for package metadata in `package.yaml` format.
- When agent-created files are owned by root, fix ownership with `sudo chown -R $(id -u):$(id -g) .`.
### Type safety and pattern matching
This project compiles with `-Wall -Werror`, including `-Wincomplete-uni-patterns`
and `-Wincomplete-record-updates`. Partial functions and irrefutable pattern
matches (`let (Just x) = ...`, `(Just x, _) <- ...`) will fail at runtime or
be rejected by the compiler.
**Rules:**
- **Never** use irrefutable/partial pattern matches on `Maybe`, `Either`, or
tuple results from `IO` actions. Always use `case` with an explicit branch
for each constructor, or use pattern-matching helpers (`maybe`, `either`).
- **Never** call partial functions: `head`, `tail`, `init`, `last`, `fromJust`,
`read` (use `readMaybe`), `succ`, `pred`, `(!!)`. Use total alternatives:
`foldl'` for `head`/`tail` on lists, `Data.Map.lookup` for map access,
`take 1` or pattern matching on `NonEmpty`.
- **Never** use `error`, `undefined`, or `panic` for recoverable failures.
Use `Either`, `Maybe`, or explicit `IO` error handling (`ioError`,
`Control.Exception.throwIO`).
- **Always** fully handle all constructors in case expressions. Use a wildcard
(`_ ->`) only when you genuinely don't care about the value, and add a
comment explaining why.
- **Prefer** `case` over `<-` pattern binds in `do` notation when the pattern
could conceivably fail at runtime.
- **Always** format code and fix all hlint warnings before committing
and pushing.