chore: fix all hlint warnings, update AGENTS.md pre-commit checklist

- Fix 8 hlint hints across 6 files (unused pragma, newtype, lambda, redundant brackets/\$)
- Add blank line after LANGUAGE pragma in Route.hs (fourmolu)
- Fix HouseholdFormData deriving to use explicit strategies for newtype
- Update AGENTS.md: require hlint clean before every commit, add pre-commit checklist
  (format, lint, build, test), update NB CSS URL to jsdelivr CDN
This commit is contained in:
2026-07-16 07:43:38 -04:00
parent 95ac550191
commit dbfe3a7c66
11 changed files with 42 additions and 27 deletions
+8 -3
View File
@@ -21,7 +21,12 @@ Hyperbole, a serverside web framework. There is zero application JavaScript.
## Haskell Conventions
- **Style:** fourmolu-formatted. Run `./hs fourmolu --mode inplace app/ src/ test/` before committing.
- **Lint:** hlint clean required. Fix any hints before committing.
- **Lint:** hlint clean required — MUST run `./hs hlint app/ src/ test/` and fix all hints before committing. Zero hints is the standard. Do not suppress or ignore hlint suggestions.
- **Pre-commit checklist:** Before every commit, run:
1. `./hs fourmolu --mode inplace app/ src/ test/` — format code
2. `./hs hlint app/ src/ test/` — fix ALL hints (must output "No hints")
3. `./hs stack build --fast` — must compile with zero errors
4. `./hs stack test --fast` — all tests must pass
- **Warnings:** `-Wall -Werror` in `package.yaml`. All warnings are fatal.
- **Module qualifiers:** Use qualified imports with descriptive aliases
(e.g., `import Data.Text qualified as T`).
@@ -37,8 +42,7 @@ Hyperbole, a serverside web framework. There is zero application JavaScript.
- **Framework:** [Hyperbole](https://github.com/seanhess/hyperbole) — Haskell
serverside web framework. All HTML rendered in Haskell.
- **CSS:** [Neo Brutalism](https://unpkg.com/neobrutalismcss@latest) CDN +
`frontend/static/style.css` for custom styles.
- **CSS:** [Neo Brutalism](https://github.com/matifandy8/NeoBrutalismCSS) CDN via jsdelivr + `frontend/static/style.css` for custom styles.
- **Build:** No npm/build step for frontend. All pages rendered in Haskell.
- **Interactive components:** HyperViews with typed Actions and server-side
updates via VirtualDOM over WebSocket.
@@ -86,5 +90,6 @@ sis/
- Run `./scripts/test` before committing. Tests must pass.
## Agent Autonomy
- Run `./hs hlint app/ src/ test/` and fix ALL hints before committing.
- As changes are completed then verify functionality using Playwright
- Once functionality is confirmed then commit and push changes
+1 -1
View File
@@ -17,7 +17,7 @@ import Effectful
import Sis.Database
import Sis.Route
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
import Sis.Style (colorGreen, colorRed, colorYellow, nbBadgeClass, nbBoxClass, nbButtonDefaultClass, nbContainerClass, nbHeadingClass, nbInputClass, nbLabelClass, nbListItemClass)
import Sis.Types
import Sis.View.Layout
import Web.Hyperbole
+2 -2
View File
@@ -18,7 +18,7 @@ import Effectful
import Sis.Database
import Sis.Route
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
import Sis.Style (colorGreen, colorRed, colorYellow, nbBadgeClass, nbBoxClass, nbButtonDefaultClass, nbContainerClass, nbHeadingClass, nbInputClass, nbLabelClass, nbListItemClass)
import Sis.Types
import Sis.View.Layout
import Web.Hyperbole
@@ -86,7 +86,7 @@ scheduleLabel ScheduleSometime = "Sometime"
scheduleLabel (ScheduleOneOff d _) = "One-off on " <> T.pack (show d)
scheduleLabel (ScheduleRecurring p _ mt _ _) =
let pText = case p of PeriodDaily -> "daily"; PeriodWeekly -> "weekly"; PeriodMonthly -> "monthly"
timePart = maybe "" (\t -> " at " <> t) mt
timePart = maybe "" (" at " <>) mt
in "Recurs " <> pText <> timePart
page :: (Hyperbole :> es, DB :> es, IOE :> es) => Page es '[ChoresPage]
+1 -1
View File
@@ -19,7 +19,7 @@ import Effectful
import Sis.Database
import Sis.Route
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
import Sis.Style (colorGreen, colorRed, colorYellow, nbBadgeClass, nbBoxClass, nbButtonDefaultClass, nbContainerClass, nbHeadingClass, nbInputClass, nbLabelClass, nbListItemClass)
import Sis.Types
import Sis.View.Layout
import Web.Hyperbole
+3 -3
View File
@@ -17,7 +17,7 @@ import Effectful
import Sis.Database
import Sis.Route
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
import Sis.Style (colorGreen, colorRed, colorYellow, nbBadgeClass, nbBoxClass, nbButtonDefaultClass, nbContainerClass, nbHeadingClass, nbInputClass, nbLabelClass, nbListItemClass)
import Sis.Types
import Sis.View.Layout
import Web.Hyperbole
@@ -43,7 +43,7 @@ instance (DB :> es, IOE :> es) => HyperView HouseholdPage es where
Just us -> do
hhs <- getUserHouseholds (UserId (usUserId us))
case hhs of
[] -> pure (noHouseholdView)
[] -> pure noHouseholdView
(h : _) -> do
let hid = unHouseholdId (householdId h)
mems <- getMembers hid
@@ -123,7 +123,7 @@ page = do
Just us -> do
hhs <- getUserHouseholds (UserId (usUserId us))
case hhs of
[] -> pure $ hyper HouseholdPage $ noHouseholdView
[] -> pure $ hyper HouseholdPage noHouseholdView
(h : _) -> do
let hid = unHouseholdId (householdId h)
mems <- getMembers hid
+2 -2
View File
@@ -17,7 +17,7 @@ import Effectful
import Sis.Auth (generateToken, hashPassword, verifyPassword)
import Sis.Database
import Sis.Route
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
import Sis.Style (colorGreen, colorRed, colorYellow, nbBadgeClass, nbBoxClass, nbButtonDefaultClass, nbContainerClass, nbHeadingClass, nbInputClass, nbLabelClass, nbListItemClass)
import Sis.Types
import Sis.View.Layout
import Web.Hyperbole
@@ -41,7 +41,7 @@ instance (DB :> es, IOE :> es) => HyperView LoginPage es where
Just u
| verifyPassword (lfPassword formData') (userPasswordHash u) -> do
saveSession (UserSession (unUserId (userId u)))
pure (loginSuccessView)
pure loginSuccessView
_ -> pure (loginView (Just "Invalid email or password"))
update Noop = pure (loginView Nothing)
+2 -2
View File
@@ -18,7 +18,7 @@ import Effectful
import Sis.Auth (generateToken, hashPassword, verifyPassword)
import Sis.Database
import Sis.Route
import Sis.Style (nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass, nbBadgeClass, nbHeadingClass, nbContainerClass, nbListItemClass, colorRed, colorYellow, colorGreen)
import Sis.Style (colorGreen, colorRed, colorYellow, nbBadgeClass, nbBoxClass, nbButtonDefaultClass, nbContainerClass, nbHeadingClass, nbInputClass, nbLabelClass, nbListItemClass)
import Sis.Types
import Sis.View.Layout
import Web.Hyperbole
@@ -49,7 +49,7 @@ instance (DB :> es, IOE :> es) => HyperView SignupPage es where
pwHash <- liftIO (hashPassword (sfPassword form))
uid <- createUser (sfDisplayName form) (sfEmail form) pwHash
saveSession (UserSession (unUserId uid))
pure (signupSuccessView)
pure signupSuccessView
signupSuccessView :: View SignupPage ()
signupSuccessView = do
-1
View File
@@ -1,5 +1,4 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Sis.Route (AppRoute (..)) where
+17 -7
View File
@@ -1,12 +1,22 @@
{-# LANGUAGE OverloadedStrings #-}
-- | Neo Brutalism CSS class name constants for Hyperbole views.
-- Based on https://github.com/matifandy8/NeoBrutalismCSS
module Sis.Style
( nbBoxClass, nbButtonDefaultClass, nbInputClass, nbLabelClass
, nbBadgeClass, nbHeadingClass
, nbNavbarClass, nbNavbarLinkClass, nbContainerClass, nbListItemClass
, colorRed, colorYellow, colorGreen
{- | Neo Brutalism CSS class name constants for Hyperbole views.
Based on https://github.com/matifandy8/NeoBrutalismCSS
-}
module Sis.Style (
nbBoxClass,
nbButtonDefaultClass,
nbInputClass,
nbLabelClass,
nbBadgeClass,
nbHeadingClass,
nbNavbarClass,
nbNavbarLinkClass,
nbContainerClass,
nbListItemClass,
colorRed,
colorYellow,
colorGreen,
) where
import Data.Text (Text)
+3 -2
View File
@@ -292,7 +292,8 @@ data ActivityFormData = ActivityFormData
}
deriving (Show, Eq, Generic, FromForm)
data HouseholdFormData = HouseholdFormData
newtype HouseholdFormData = HouseholdFormData
{ hfdName :: Text
}
deriving (Show, Eq, Generic, FromForm)
deriving stock (Show, Eq, Generic)
deriving anyclass (FromForm)
+2 -2
View File
@@ -15,7 +15,7 @@ import Data.Text (Text)
import GHC.Generics (Generic)
import Sis.Route
import Sis.Style (nbNavbarClass, nbNavbarLinkClass, nbHeadingClass, nbButtonDefaultClass)
import Sis.Style (nbButtonDefaultClass, nbHeadingClass, nbNavbarClass, nbNavbarLinkClass)
import Web.Hyperbole
import Web.Hyperbole.Effect.Session
@@ -23,7 +23,7 @@ import Web.Hyperbole.Effect.Session
-- Session
----------------------------------------------------------------------
data UserSession = UserSession
newtype UserSession = UserSession
{ usUserId :: Int
}
deriving stock (Show, Eq, Generic)