From 5208c84a2f903056bc040da592646ff39119e592 Mon Sep 17 00:00:00 2001 From: Timo Klerx Date: Sun, 21 Jun 2026 20:20:56 +0200 Subject: [PATCH 1/3] chore: enforce text encoding conventions --- .editorconfig | 6 +++ .githooks/pre-commit | 7 ++- .prettierrc | 1 + AGENTS.md | 2 +- CONTINUE.md | 27 ++++++---- CONTINUE_LOG.md | 24 +++++++++ package.json | 1 + scripts/check-text-conventions.mjs | 82 ++++++++++++++++++++++++++++++ 8 files changed, 137 insertions(+), 13 deletions(-) create mode 100644 .editorconfig create mode 100644 scripts/check-text-conventions.mjs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..14c1d8c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,6 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 2ffff93..14909d7 100644 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -1,6 +1,9 @@ #!/usr/bin/env sh set -eu -"$(dirname "$0")/require-continuity.sh" +hook_dir=${0%/*} -exec "$(dirname "$0")/run-validate.sh" all +"$hook_dir/require-continuity.sh" +node "$hook_dir/../scripts/check-text-conventions.mjs" + +exec "$hook_dir/run-validate.sh" precommit diff --git a/.prettierrc b/.prettierrc index 7229070..f92d457 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,6 @@ { "semi": true, "singleQuote": false, + "endOfLine": "lf", "trailingComma": "all" } diff --git a/AGENTS.md b/AGENTS.md index 72e5a43..cde97e0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,7 +40,7 @@ pnpm test; pnpm run lint TypeScript 5.9 on Next.js 16 App Router (React 19): Follow standard conventions -- Repository text files use UTF-8 encoding (UTF-8 with or without BOM) +- Repository text files use UTF-8 encoding without BOM and LF (`\n`) line endings ## Recent Changes diff --git a/CONTINUE.md b/CONTINUE.md index 787a217..d13eda6 100644 --- a/CONTINUE.md +++ b/CONTINUE.md @@ -1,32 +1,39 @@ # Continue - + ## Current Snapshot -- Updated: 2026-06-20 15:56:22 +- Updated: 2026-06-21 20:15:38 - Branch: `main` ## Recent Non-Continuity Commits +- 115543e feat: add exception-aware supply-chain audit - 5f786d8 chore: refresh specs overview - 8492b6b feat: add ops health dashboard (#5) - 8047615 feat: expose runtime build metadata (#4) - 6c81729 feat: add azure deployment smoke verification (#3) -- 3d52264 fix: move state queue logging to dedicated resource ## Git Status -- Porting rag-agent-style supply-chain audit coverage into this repo. -- Added app/worker dependency-audit Docker targets, exception registry/docs, and dependency audit evidence inside `scripts/supply-chain-audit.ps1`. -- Local verification passed for script smoke, host `pnpm audit` parsing, and host worker `uv audit`; Docker image-context scans are currently blocked because the local Docker daemon is unavailable. +- M .githooks/pre-commit +- M .githooks/pre-merge-commit +- M .githooks/pre-push +- M .prettierrc +- M AGENTS.md +- M README.md +- M eslint.config.mjs +- M package.json +- M validate.ps1 +- ?? .editorconfig +- ?? scripts/check-text-conventions.mjs ## Active Specs -- None +- No active spec folders detected. ## Next Recommended Actions -1. Re-run `pnpm run supply-chain:audit` or `.\validate.ps1 full` with Docker available. -2. Review and commit the supply-chain audit port. -3. Watch CI validation for the Docker-backed image-context audits. +1. Review and commit repository UTF-8/LF convention and pre-commit check updates. +2. No unchecked tasks detected in the active specs. diff --git a/CONTINUE_LOG.md b/CONTINUE_LOG.md index 37ff9d3..9cc4840 100644 --- a/CONTINUE_LOG.md +++ b/CONTINUE_LOG.md @@ -1579,3 +1579,27 @@ - Validation passed: focused ops unit/integration tests, focused ops Playwright tests, `.\validate.ps1 quality`, `.\validate.ps1 all`, and `.\validate.ps1 full` including Trivy/container scans and full Playwright E2E. - Active specs: `021-ops-health-dashboard` implemented and validated; PR cleanup remains. - Next focus: commit, push, open a PR, and confirm GitHub validation. +## 2026-06-21 18:00:22 + +- Branch snapshot refreshed for `main`. +- Latest non-continuity commit: 115543e feat: add exception-aware supply-chain audit. +- Active specs: none. +- Next focus: no next task. +## 2026-06-21 18:21:00 + +- Branch snapshot refreshed for `main`. +- Latest non-continuity commit: 115543e feat: add exception-aware supply-chain audit. +- Active specs: none. +- Next focus: no next task. +## 2026-06-21 20:13:32 + +- Added repository-wide UTF-8/LF guidance in `AGENTS.md`. +- Added `.editorconfig` defaults for UTF-8, LF, and final newlines. +- Set Prettier `endOfLine` to `lf`; existing `.gitattributes` already normalizes text files to LF. +- Verified touched convention files have no BOM and no CRLF line endings. +## 2026-06-21 20:15:38 + +- Added `scripts/check-text-conventions.mjs` to validate staged text files for UTF-8 without BOM and LF line endings. +- Wired the text convention check into `.githooks/pre-commit` before validation. +- Added `pnpm run check:text` for manual invocation. +- Verification passed: `pnpm run check:text` and `node --check scripts/check-text-conventions.mjs`. diff --git a/package.json b/package.json index 1e84773..e90791b 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "quality:ts": "pnpm run lint && pnpm run architecture && pnpm run duplication", "quality:python": "node scripts/check-python-quality.mjs", "quality:cli": "node scripts/check-cli-quality.mjs", + "check:text": "node scripts/check-text-conventions.mjs", "logging:guard": "node scripts/check-logging-guard.mjs", "smoke:azure": "node scripts/run-azure-deploy-smoke.mjs", "supply-chain:audit": "pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/supply-chain-audit.ps1", diff --git a/scripts/check-text-conventions.mjs b/scripts/check-text-conventions.mjs new file mode 100644 index 0000000..24e6f6a --- /dev/null +++ b/scripts/check-text-conventions.mjs @@ -0,0 +1,82 @@ +#!/usr/bin/env node +import { execFileSync } from "node:child_process"; +import { TextDecoder } from "node:util"; + +const utf8Decoder = new TextDecoder("utf-8", { fatal: true }); + +function git(args, options = {}) { + return execFileSync("git", args, { + maxBuffer: 100 * 1024 * 1024, + ...options, + }); +} + +function getStagedFiles() { + const output = git( + ["diff", "--cached", "--name-only", "-z", "--diff-filter=ACMR"], + { encoding: "buffer" }, + ); + + return output + .toString("utf8") + .split("\0") + .filter(Boolean); +} + +function getStagedBlob(path) { + return git(["show", `:${path}`], { encoding: "buffer" }); +} + +function isBinary(buffer) { + return buffer.includes(0); +} + +function hasUtf8Bom(buffer) { + return ( + buffer.length >= 3 && + buffer[0] === 0xef && + buffer[1] === 0xbb && + buffer[2] === 0xbf + ); +} + +function validateTextBlob(path, buffer) { + const failures = []; + + if (hasUtf8Bom(buffer)) { + failures.push("UTF-8 BOM"); + } + + let text = ""; + try { + text = utf8Decoder.decode(buffer); + } catch { + failures.push("invalid UTF-8"); + } + + if (text.includes("\r")) { + failures.push("CRLF/CR line endings"); + } + + return failures.map((failure) => `${path}: ${failure}`); +} + +const failures = []; + +for (const path of getStagedFiles()) { + const buffer = getStagedBlob(path); + + if (isBinary(buffer)) { + continue; + } + + failures.push(...validateTextBlob(path, buffer)); +} + +if (failures.length > 0) { + console.error("Text convention check failed. Use UTF-8 without BOM and LF line endings."); + for (const failure of failures) { + console.error(`- ${failure}`); + } + process.exit(1); +} From d4e1489004a2c1082574c1adfaf46e574ea54174 Mon Sep 17 00:00:00 2001 From: Timo Klerx Date: Sun, 21 Jun 2026 20:35:37 +0200 Subject: [PATCH 2/3] chore: fix text convention CI checks --- CONTINUE.md | 25 ++++++++++++------------- CONTINUE_LOG.md | 11 +++++++++++ scripts/check-text-conventions.mjs | 9 ++++----- specs/OVERVIEW.md | 2 +- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/CONTINUE.md b/CONTINUE.md index d13eda6..5d0fac8 100644 --- a/CONTINUE.md +++ b/CONTINUE.md @@ -4,7 +4,7 @@ ## Current Snapshot -- Updated: 2026-06-21 20:15:38 +- Updated: 2026-06-21 20:34:43 - Branch: `main` ## Recent Non-Continuity Commits @@ -17,17 +17,16 @@ ## Git Status -- M .githooks/pre-commit -- M .githooks/pre-merge-commit -- M .githooks/pre-push -- M .prettierrc -- M AGENTS.md -- M README.md -- M eslint.config.mjs -- M package.json -- M validate.ps1 -- ?? .editorconfig -- ?? scripts/check-text-conventions.mjs +- M .githooks/pre-merge-commit +- M .githooks/pre-push +- M CONTINUE.md +- M CONTINUE_LOG.md +- M README.md +- M eslint.config.mjs +- M package.json +- M scripts/check-text-conventions.mjs +- M specs/OVERVIEW.md +- M validate.ps1 ## Active Specs @@ -35,5 +34,5 @@ ## Next Recommended Actions -1. Review and commit repository UTF-8/LF convention and pre-commit check updates. +1. Commit and push CI fixes for PR #6 (`prettier`, `spec-overview`). 2. No unchecked tasks detected in the active specs. diff --git a/CONTINUE_LOG.md b/CONTINUE_LOG.md index 9cc4840..0f97933 100644 --- a/CONTINUE_LOG.md +++ b/CONTINUE_LOG.md @@ -1579,27 +1579,38 @@ - Validation passed: focused ops unit/integration tests, focused ops Playwright tests, `.\validate.ps1 quality`, `.\validate.ps1 all`, and `.\validate.ps1 full` including Trivy/container scans and full Playwright E2E. - Active specs: `021-ops-health-dashboard` implemented and validated; PR cleanup remains. - Next focus: commit, push, open a PR, and confirm GitHub validation. + ## 2026-06-21 18:00:22 - Branch snapshot refreshed for `main`. - Latest non-continuity commit: 115543e feat: add exception-aware supply-chain audit. - Active specs: none. - Next focus: no next task. + ## 2026-06-21 18:21:00 - Branch snapshot refreshed for `main`. - Latest non-continuity commit: 115543e feat: add exception-aware supply-chain audit. - Active specs: none. - Next focus: no next task. + ## 2026-06-21 20:13:32 - Added repository-wide UTF-8/LF guidance in `AGENTS.md`. - Added `.editorconfig` defaults for UTF-8, LF, and final newlines. - Set Prettier `endOfLine` to `lf`; existing `.gitattributes` already normalizes text files to LF. - Verified touched convention files have no BOM and no CRLF line endings. + ## 2026-06-21 20:15:38 - Added `scripts/check-text-conventions.mjs` to validate staged text files for UTF-8 without BOM and LF line endings. - Wired the text convention check into `.githooks/pre-commit` before validation. - Added `pnpm run check:text` for manual invocation. - Verification passed: `pnpm run check:text` and `node --check scripts/check-text-conventions.mjs`. + +## 2026-06-21 20:34:43 + +- Checked GitHub CI for PR #6; `Validate / validate` failed on `prettier` and `spec-overview`. +- Ran Prettier on `CONTINUE.md`, `CONTINUE_LOG.md`, and `scripts/check-text-conventions.mjs`. +- Regenerated `specs/OVERVIEW.md` with `pnpm run specs:overview:update`. +- Focused verification passed: `pnpm run check:text` and Prettier check for the affected files. diff --git a/scripts/check-text-conventions.mjs b/scripts/check-text-conventions.mjs index 24e6f6a..d80d7e3 100644 --- a/scripts/check-text-conventions.mjs +++ b/scripts/check-text-conventions.mjs @@ -17,10 +17,7 @@ function getStagedFiles() { { encoding: "buffer" }, ); - return output - .toString("utf8") - .split("\0") - .filter(Boolean); + return output.toString("utf8").split("\0").filter(Boolean); } function getStagedBlob(path) { @@ -74,7 +71,9 @@ for (const path of getStagedFiles()) { } if (failures.length > 0) { - console.error("Text convention check failed. Use UTF-8 without BOM and LF line endings."); + console.error( + "Text convention check failed. Use UTF-8 without BOM and LF line endings.", + ); for (const failure of failures) { console.error(`- ${failure}`); } diff --git a/specs/OVERVIEW.md b/specs/OVERVIEW.md index 35ae01f..96e427d 100644 --- a/specs/OVERVIEW.md +++ b/specs/OVERVIEW.md @@ -1,6 +1,6 @@ # Business App Starter Specs Overview -Last Updated: 2026-06-20 +Last Updated: 2026-06-21 Purpose: Track the status of all planned features, their implementation progress, and next steps. From 395a878a482d6184e449f259e3031e2af45382ca Mon Sep 17 00:00:00 2001 From: Timo Klerx Date: Sun, 21 Jun 2026 20:47:44 +0200 Subject: [PATCH 3/3] chore: split local validation hooks --- .githooks/pre-merge-commit | 4 +++- .githooks/pre-push | 8 +++++++- README.md | 13 +++++++++++-- eslint.config.mjs | 10 ++++++++++ package.json | 2 ++ validate.ps1 | 16 +++++++++------- 6 files changed, 42 insertions(+), 11 deletions(-) diff --git a/.githooks/pre-merge-commit b/.githooks/pre-merge-commit index a6882f7..3856d31 100644 --- a/.githooks/pre-merge-commit +++ b/.githooks/pre-merge-commit @@ -1,4 +1,6 @@ #!/usr/bin/env sh set -eu -exec "$(dirname "$0")/run-validate.sh" full +hook_dir=${0%/*} + +exec "$hook_dir/run-validate.sh" full diff --git a/.githooks/pre-push b/.githooks/pre-push index a6882f7..9282644 100644 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -1,4 +1,10 @@ #!/usr/bin/env sh set -eu -exec "$(dirname "$0")/run-validate.sh" full +hook_dir=${0%/*} + +if [ "${RUN_FULL_VALIDATION_ON_PUSH:-0}" = "1" ]; then + exec "$hook_dir/run-validate.sh" full +fi + +exec "$hook_dir/run-validate.sh" prepush diff --git a/README.md b/README.md index 501e72b..159c114 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,8 @@ Guidelines: ## Validation ```powershell +.\validate.ps1 precommit +.\validate.ps1 prepush .\validate.ps1 all .\validate.ps1 e2e .\validate.ps1 full @@ -79,6 +81,11 @@ testing. Set an explicit `DATABASE_URL=file:./e2e.db` only when you intentionall need the legacy SQLite E2E path. Set `E2E_REUSE_SERVER=1` only when you are not resetting the database between runs. +`precommit` is the fast local hook phase: TypeScript typecheck, +dependency-cruiser architecture, and jscpd duplication. +`prepush` is the medium local hook phase: ESLint complexity/function-size +ratchets plus Python quality/complexity checks. Set +`RUN_FULL_VALIDATION_ON_PUSH=1` to run `full` before pushing instead. `all` includes TypeScript, Python, and CLI quality checks plus dependency cooldown validation for pnpm and uv support. `full` also runs the Trivy supply-chain audit gate before production dependency @@ -92,8 +99,9 @@ pnpm run quality:python pnpm run quality:cli ``` -`quality:ts` runs blocking ESLint complexity, SonarJS cognitive-complexity, and -jscpd duplication thresholds plus dependency-cruiser import-cycle analysis. +`quality:ts` runs blocking ESLint complexity, SonarJS cognitive-complexity, +function-size, and jscpd duplication thresholds plus dependency-cruiser +import-cycle analysis. `quality:python` runs Ruff plus blocking Xenon and complexipy complexity thresholds with Radon reporting for the worker package. `quality:cli` runs Go formatting checks, `go vet`, Staticcheck, a blocking @@ -104,6 +112,7 @@ regressions by default: - TypeScript cyclomatic complexity: 56 - TypeScript cognitive complexity: 24 +- TypeScript function size: 520 nonblank, non-comment lines - Python Xenon: max absolute F, max module C, max average B - Python Radon cyclomatic complexity: 44 - Python complexipy cognitive complexity: 46 diff --git a/eslint.config.mjs b/eslint.config.mjs index a18f091..8e57538 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -5,6 +5,7 @@ const thresholdSeverity = process.env.QUALITY_THRESHOLDS_BYPASS === "1" ? "warn" : "error"; const maxCyclomaticComplexity = 56; const maxCognitiveComplexity = 24; +const maxFunctionLines = 520; const config = [ { @@ -30,6 +31,15 @@ const config = [ }, rules: { complexity: [thresholdSeverity, { max: maxCyclomaticComplexity }], + "max-lines-per-function": [ + thresholdSeverity, + { + max: maxFunctionLines, + skipBlankLines: true, + skipComments: true, + IIFEs: true, + }, + ], "sonarjs/cognitive-complexity": [ thresholdSeverity, maxCognitiveComplexity, diff --git a/package.json b/package.json index e90791b..efd469f 100644 --- a/package.json +++ b/package.json @@ -25,12 +25,14 @@ "smoke:azure": "node scripts/run-azure-deploy-smoke.mjs", "supply-chain:audit": "pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/supply-chain-audit.ps1", "validate:runtime-credentials": "pwsh -NoProfile -ExecutionPolicy Bypass -File scripts/validate-runtime-credentials.ps1 -SelfTest", + "validate:precommit": "pwsh -NoProfile -ExecutionPolicy Bypass -File validate.ps1 precommit", "worker:lint": "cd worker && uv run ruff check src tests", "worker:complexity": "node scripts/check-python-quality.mjs --complexity-only", "semgrep": "semgrep scan --error --config auto --config .semgrep.yml", "continuity:update": "node scripts/update-continuity.mjs", "specs:overview:update": "node scripts/update-spec-overview.mjs", "specs:overview:check": "node scripts/update-spec-overview.mjs --check", + "validate:prepush": "pwsh -NoProfile -ExecutionPolicy Bypass -File validate.ps1 prepush", "validate": "pnpm run validate:runtime-credentials && pnpm run typecheck && pnpm run quality:ts && pnpm run quality:python && pnpm run quality:cli && pnpm run logging:guard && pnpm run semgrep && pnpm run test", "validate:full": "pnpm run validate && pnpm run test:e2e", "template:stamp": "node scripts/stamp-template-origin.mjs", diff --git a/validate.ps1 b/validate.ps1 index 291e530..e038e16 100644 --- a/validate.ps1 +++ b/validate.ps1 @@ -6,9 +6,11 @@ .DESCRIPTION Usage: ./validate.ps1 [phase] Phases: - all - typecheck + TS/Python/CLI quality + semgrep + test (default, pre-commit) + all - typecheck + TS/Python/CLI quality + semgrep + test (default) full - all quality checks + Trivy supply-chain audit + shipped-deps audit + Playwright E2E tests (recommended before merge; skips continuity freshness) continuity - check whether CONTINUE.md / CONTINUE_LOG.md need a refresh + precommit - fast local sanity: TS typecheck + architecture + duplication + prepush - medium local gate: ESLint ratchets + Python quality/complexity quick - typecheck only (use during scaffolding before tests exist) test - tests only e2e - Playwright E2E tests only @@ -21,7 +23,7 @@ #> param( - [ValidateSet("all", "full", "continuity", "quick", "test", "e2e", "quality", "commit")] + [ValidateSet("all", "full", "continuity", "precommit", "prepush", "quick", "test", "e2e", "quality", "commit")] [string]$Phase = "all" ) @@ -902,7 +904,7 @@ function Get-AuditPackageStatuses($audit) { $failures = @() -if ($Phase -in "all", "full", "quick", "commit") { +if ($Phase -in "all", "full", "precommit", "quick", "commit") { Write-Step "Typecheck (tsc --noEmit)" try { $exitCode = Invoke-NativeCommand "pnpm run typecheck" @@ -934,7 +936,7 @@ if ($Phase -in "all", "full", "quick", "commit") { } } -if ($Phase -in "all", "full", "quality", "commit") { +if ($Phase -in "all", "full", "prepush", "quality", "commit") { Write-Step "Lint (eslint)" try { $result = Invoke-NativeCommandCaptured "pnpm run lint" @@ -1005,7 +1007,7 @@ if ($Phase -in "all", "full", "quality", "commit") { } } -if ($Phase -in "all", "full", "quality", "commit") { +if ($Phase -in "all", "full", "precommit", "quality", "commit") { Write-Step "Architecture (dependency-cruiser)" try { $result = Invoke-NativeCommandCaptured "pnpm run architecture" @@ -1032,7 +1034,7 @@ if ($Phase -in "all", "full", "quality", "commit") { } } -if ($Phase -in "all", "full", "quality", "commit") { +if ($Phase -in "all", "full", "precommit", "quality", "commit") { Write-Step "Duplication (jscpd)" try { $threshold = Get-DuplicationThreshold @@ -1067,7 +1069,7 @@ if ($Phase -in "all", "full", "quality", "commit") { } } -if ($Phase -in "all", "full", "quality", "commit") { +if ($Phase -in "all", "full", "prepush", "quality", "commit") { Write-Step "Python quality (ruff, xenon, radon, complexipy)" try { $result = Invoke-NativeCommandCaptured "pnpm run quality:python"