diff --git a/AGENTS.md b/AGENTS.md index 570ac0e..461b6b2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -86,3 +86,28 @@ mkdir -p .stack-root - 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. diff --git a/src/Roux/Server.hs b/src/Roux/Server.hs index 7d20e83..2a7e3bd 100644 --- a/src/Roux/Server.hs +++ b/src/Roux/Server.hs @@ -243,13 +243,19 @@ readProcessBytes cmd args = do { std_out = CreatePipe , std_err = CreatePipe } - (Just outH, Just errH, _, ph) <- createProcess process - hSetBinaryMode outH True - hSetBinaryMode errH True - out <- LB.hGetContents outH - err <- LB.hGetContents errH - ec <- waitForProcess ph - return (ec, out, err) + result <- createProcess process + case result of + (Just outH, Just errH, _, ph) -> do + hSetBinaryMode outH True + hSetBinaryMode errH True + out <- LB.hGetContents outH + err <- LB.hGetContents errH + ec <- waitForProcess ph + pure (ec, out, err) + _ -> + ioError $ + userError $ + "[roux] createProcess did not return std_out/std_err pipes for " <> cmd -- | Locate the scrape-recipe script: check PATH first, then relative to project. findScrapeScript :: IO FilePath