4.1 KiB
4.1 KiB
AGENTS.md
Project: Roux
A personal/family recipe management site. See 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 Haskellroux-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 Docker
image. Adopted from the pattern in the Atlas project.
Setup
- Create the
hswrapper script in the project root:
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
- Create
.stack-root/in the project root to persist GHC/cache across runs:
mkdir -p .stack-root
- Initialize a Haskell project with
stackorhpack:
./hs stack new cooklang-hs simple --dir cooklang-hs
# or for an existing project:
./hs stack build
Usage examples
./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 toghcr.io/flipstone/haskell-tools:debian-ghc-9.10.3-5d6640d)
Agent conventions
- Prefer the
hswrapper over local tooling — match the Docker-first dev environment. - Run
./hs stack buildand./hs stack testbefore 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 inpackage.yamlformat. - 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 fromIOactions. Always usecasewith an explicit branch for each constructor, or use pattern-matching helpers (maybe,either). - Never call partial functions:
head,tail,init,last,fromJust,read(usereadMaybe),succ,pred,(!!). Use total alternatives:foldl'forhead/tailon lists,Data.Map.lookupfor map access,take 1or pattern matching onNonEmpty. - Never use
error,undefined, orpanicfor recoverable failures. UseEither,Maybe, or explicitIOerror 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
caseover<-pattern binds indonotation when the pattern could conceivably fail at runtime. - Always format code and fix all hlint warnings before committing and pushing.