From a26ecae718edeeb2ca51e29489897e7a695acda0 Mon Sep 17 00:00:00 2001 From: Kris Armstrong Date: Tue, 16 Jun 2026 17:32:56 -0400 Subject: [PATCH 1/2] ci(gates): add filename-policy gate (no monolith naming outside internal/api) Ports seed's check-filename-policy.sh to stem for fleet enforcement parity. The gate fails if any handlers_*.go / jobs_*.go file exists outside internal/api (unless it's the eponymous file of a package named for that token). The handlers_/jobs_ prefixes are meaningful grouping *only* inside the flat internal/api monolith; once a concern is decomposed into a capability package the package name already supplies that context, so the prefix becomes stutter and must be dropped. This locks in the ADR-0011 internal/api strangle discipline: a future decomposition that carries the monolith's grouping prefix forward fails CI instead of silently coupling the vocabulary outward. Green on the current tree (zero handlers_/jobs_ files outside internal/api). --- .github/workflows/ci.yml | 3 ++ scripts/check-filename-policy.sh | 55 ++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100755 scripts/check-filename-policy.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fbc267a..e02d96c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -139,6 +139,9 @@ jobs: - name: JSON wire-casing gate — camelCase ratchet run: ./scripts/check-json-casing.sh + - name: Filename-policy gate — no monolith naming outside internal/api + run: ./scripts/check-filename-policy.sh + - name: File-size gate (STRICT — W5.5 bookend, fleet-wide Go + TS red flags) run: STRICT=1 ./scripts/check-file-size.sh diff --git a/scripts/check-filename-policy.sh b/scripts/check-filename-policy.sh new file mode 100755 index 0000000..1a1b0b6 --- /dev/null +++ b/scripts/check-filename-policy.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# check-filename-policy.sh — capability-package file-naming gate. +# +# The flat internal/api package uses filename prefixes (handlers_, jobs_) to +# separate concerns within one large package. Those prefixes are meaningful +# *only* there. Once code is decomposed into a dedicated capability package +# (internal//), the package declaration already supplies that context, so +# the prefix becomes stutter: internal/health/handlers_health_api.go re-states +# "health" twice and re-asserts a layer fact the package name already makes. +# +# Best practice in a decomposed package is to name files for their role *within* +# the package (health.go, checks.go, handler.go) — never by re-stating the layer +# (handlers_/jobs_) or the capability the package itself already represents. +# This is not a Go *language* rule (the toolchain accepts these names); it is an +# architecture-consistency rule that keeps the internal/api strangle honest: +# monolith vocabulary stays in the monolith and is dropped on the way out. +# +# This gate fails if any handlers_*.go / jobs_*.go file exists outside +# internal/api, UNLESS the file's own package directory is named for that token +# (e.g. internal/platform/jobs/jobs.go is the eponymous file of package `jobs`, +# and jobs_test.go is its standard test — both idiomatic, not monolith baggage). +# The gate is green on the current tree and only fires when a decomposition +# carries the monolith's grouping prefix forward instead of dropping it. +# +# Run locally: scripts/check-filename-policy.sh +set -euo pipefail + +violations="" +while IFS= read -r f; do + [[ -z "$f" ]] && continue + dir=$(basename "$(dirname "$f")") # parent package directory + prefix="${f##*/}"; prefix="${prefix%%_*}" # handlers | jobs + # Allow the eponymous package file/test (dir named for the prefix token). + [[ "$dir" == "$prefix" ]] && continue + violations+="$f"$'\n' +# Scan git-tracked files only — never a raw worktree walk. CI sets GOMODCACHE +# inside the repo (./.cache/go/pkg/mod), so a find(1) sweep would trip on +# third-party handlers_*.go in the module cache (a false positive). git ls-files +# inherently ignores the cache, build artifacts, and anything gitignored. +done < <(git ls-files -- \ + ':(glob)**/handlers_*.go' ':(glob)**/jobs_*.go' \ + ':(exclude)internal/api/**' ':(exclude)vendor/**') +violations=$(printf '%s' "$violations" | sed '/^[[:space:]]*$/d' | sort) + +if [[ -n "$violations" ]]; then + echo "❌ Filename-policy gate: monolith file-naming prefixes (handlers_/jobs_)" + echo " must not appear outside internal/api. A decomposed capability package" + echo " names files for their role within the package — drop the prefix:" + echo " internal/health/handlers_health_api.go → internal/health/health.go" + echo "" + echo "$violations" + exit 1 +fi + +echo "✓ Filename-policy gate: no monolith naming prefixes outside internal/api." From db4cbd6e370637527a36ae2483793119c35a732d Mon Sep 17 00:00:00 2001 From: Kris Armstrong Date: Tue, 16 Jun 2026 20:21:29 -0400 Subject: [PATCH 2/2] test(auth): speed up argon2 test mode --- internal/auth/password.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/auth/password.go b/internal/auth/password.go index 40f811a..dd84717 100644 --- a/internal/auth/password.go +++ b/internal/auth/password.go @@ -38,9 +38,9 @@ const ( argon2Version = argon2.Version // argon2TestMemoryKiB is the reduced Argon2id memory cost used when - // STEM_TEST_MODE is set (8 MiB instead of 64 MiB) so the test suite - // finishes in milliseconds. Production binaries never see this. - argon2TestMemoryKiB = 8 * 1024 + // STEM_TEST_MODE is set so the test suite can instantiate many auth + // managers under the race detector. Production binaries never see this. + argon2TestMemoryKiB = 64 ) // Password validation constants.