# lefthook.yml — local git hooks for yellowjacket
# Install with: lefthook install
# Docs: https://github.com/evilmartians/lefthook

# Conventional Commits. CI runs the same script over every commit in a
# push, so skipping this locally only defers the failure.
commit-msg:
  commands:
    commit-check:
      run: ./scripts/commit-check.sh {1}

pre-commit:
  parallel: true
  commands:
    go-vet:
      glob: "*.go"
      run: go vet ./...

    golangci-lint:
      glob: "*.go"
      run: go tool golangci-lint run --timeout 5m ./...

    # Snapshots the tree either side of the generators and reports only
    # what moved across them.  This used to be `go generate` plus a bare
    # `git diff --name-only`, which is the *whole unstaged worktree* — so
    # any unrelated edit sitting there was reported as stale generated
    # code, and `make generate` then fixed nothing.  See the script.
    codegen-check:
      glob: "*.{go,sql,templ}"
      run: ./scripts/codegen-check.sh

    # frontend/bindings is generated by `wails3`, not `go generate`, so
    # the check above does not cover it.  ~3.5s warm, ~20s on a cold
    # build cache: v3's generator is a static analyser over the whole
    # package graph, where v2's built the app with a `bindings` tag and
    # ran it.
    bindings-check:
      glob: "*.go"
      run: ./scripts/bindings-check.sh

    # .pi/ documents make targets; a stale one sends an agent off a
    # cliff with total confidence.  Instant.
    skill-check:
      glob: "{Makefile,.pi/**/*.md}"
      run: ./scripts/skill-check.sh

    frontend-typecheck:
      glob: "frontend/**/*.{ts,tsx}"
      root: "frontend/"
      run: ./node_modules/.bin/tsc --noEmit

    # A backtick in a comment inside a css`` literal ends the literal.
    # tsc does catch it, as "Class static side incorrectly extends base
    # class static side" pointing at a line of prose; this says what
    # actually happened. Instant.
    css-literals:
      glob: "frontend/**/*.ts"
      root: "frontend/"
      run: node scripts/check-css-literals.mjs

    # A nested rule whose selector starts with an element name is
    # silently dropped by the device's Chrome 113, and by nothing else --
    # so every tier here renders it correctly and only a screenshot of
    # the phone disagrees. Instant.
    css-nesting:
      glob: "frontend/**/*.{ts,css}"
      root: "frontend/"
      run: node scripts/check-css-nesting.mjs

# Deliberately sequential, unlike pre-commit.  `go test -race`
# saturates every core for the better part of a minute and the UI tier
# is a real browser with wall-clock timeouts, so run together the
# browser loses: setup took 106s inside the hook against 63s
# standalone, and a different suite failed each time -- three suites
# failing to fetch setup.ts from Vitest's own dev server on one run, a
# 15s "did not mount itself" on the next, against a suite that passes
# 898/898 on its own. A gate that fails at random is not a gate. The
# ~15s saved is not worth it.
pre-push:
  parallel: false
  commands:
    go-test:
      glob: "*.go"
      run: go test -race -count=1 -timeout 120s ./...

    go-mod-verify:
      run: go mod verify

    # The component and store tier: a real browser, no app. ~2s.
    ui-test:
      glob: "frontend/**/*.ts"
      root: "frontend/"
      run: ./node_modules/.bin/vitest run
