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

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 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 Docker image. Adopted from the pattern in the Atlas project.

Setup

  1. Create the hs wrapper 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
  1. Create .stack-root/ in the project root to persist GHC/cache across runs:
mkdir -p .stack-root
  1. Initialize a Haskell project with stack or hpack:
./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 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.