repos: # Commit message validation (Conventional Commits via commitlint) - repo: local hooks: - id: commitlint name: Validate commit message (Conventional Commits) entry: bash -c 'cd apps && pnpm exec commitlint --edit "$1"' -- language: system stages: [commit-msg] always_run: true # Harness guidance hooks - repo: local hooks: - id: harness-lint name: Harness file lint entry: python3 .github/scripts/lint-harness-files.py language: system pass_filenames: true - id: docs-catalog name: Documentation catalog validation entry: python3 scripts/list-docs.py validate language: system files: ^docs/(decisions|specs)/.*\.md$|^scripts/(list-docs|spec_metadata|lint-spec-files)\.py$|^scripts/(list-docs|lint-spec-files)\.test\.py$ pass_filenames: false - id: architecture-lint name: Architecture lint and compatibility expiry entry: python3 scripts/lint-architecture.py --all language: system pass_filenames: false - id: specification-lint name: Specification structure and size lint entry: python3 scripts/lint-spec-files.py --all language: system files: ^docs/specs/.*|^scripts/lint-spec-files\.py$|^scripts/lint-spec-files\.test\.py$ pass_filenames: false # Backend (Go) hooks - repo: local hooks: - id: gofmt name: gofmt (backend) entry: bash -c 'gofmt -w "$@"' -- language: system files: ^apps/backend/.*\.go$ pass_filenames: true - id: go-lint name: Go lint (changed packages) entry: bash scripts/lint-go-changed language: system files: ^apps/backend/.*\.go$ pass_filenames: true require_serial: true # Web app (TypeScript/React) hooks - repo: local hooks: - id: prettier-format name: Prettier format (web/cli/packages) entry: bash -c 'cd apps || exit 1; args=(); for f in "$@"; do args+=("${f#apps/}"); done; pnpm exec prettier --write --ignore-unknown "${args[@]}"' -- language: system files: ^apps/(web|cli|packages)/.*\.(ts|tsx|js|jsx|mjs|cjs|json|md|mdx|yml|yaml|css|scss)$ pass_filenames: true - id: web-lint name: Web lint (changed files only) entry: bash -c 'cd apps/web || exit 1; args=(); for f in "$@"; do args+=("${f#apps/web/}"); done; pnpm exec eslint --max-warnings 0 "${args[@]}"' -- language: system files: ^apps/web/.*\.(ts|tsx|js|jsx)$ pass_filenames: true # New user-facing copy must go through t()/, even in a directory the # i18n migration has not reached. Judges the change, not the file: added # files whole, modified files only on touched lines — so it never asks you # to migrate code you did not write. Mirrors the go-lint hook above. - id: i18n-new-code name: i18n guard (new code only) entry: bash -c 'unset GIT_DIR GIT_WORK_TREE; cd apps/web && node scripts/check-new-code-i18n.mjs && node scripts/check-guard-allowlist.mjs' language: system files: ^apps/web/(components|app|hooks|lib|src)/.*\.tsx?$|^apps/web/eslint\.i18n\.options\.mjs$ pass_filenames: false # New E2E waits must name their cause, or name a category and a reason via # dwell()/injectLatency(). Raw page.waitForTimeout() and hand-rolled # promise sleeps are the suite's most reliable source of flake. Same # judge-the-change contract as the i18n hook above: added files whole, # modified files only on touched lines, so the ~126 sleeps that predate it # never block an unrelated commit. - id: e2e-sleep-ratchet name: E2E sleep guard (new code only) entry: bash -c 'unset GIT_DIR GIT_WORK_TREE; cd apps/web && node scripts/check-new-e2e-sleeps.mjs' language: system files: ^apps/web/e2e/.*\.tsx?$|^apps/web/eslint-rules/no-unsanctioned-sleep\.mjs$ pass_filenames: false # Public copy hooks - repo: local hooks: - id: public-copy-em-dash name: Public copy em-dash guard entry: node apps/web/scripts/check-no-em-dash-ui.mjs language: system pass_filenames: false always_run: true