From c7f3ec62904b468e16eda65dedf84e5217a0e28f Mon Sep 17 00:00:00 2001 From: shankar0123 Date: Sat, 16 May 2026 05:36:26 +0000 Subject: [PATCH] =?UTF-8?q?fix(ci-guard):=20M-009=20=E2=80=94=20exclude=20?= =?UTF-8?q?Orval-generated=20tree=20from=20bare-useMutation=20scan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sprint 5 CI follow-up. Pre-fix: Sprint 5 ARCH-001-A (commit 38f1200) landed 316 Orval-generated files under web/src/api/generated/. Orval's mutation template emits bare `useMutation(mutationOptions, queryClient)` calls at every operation site (~100 hits across the generated tree) because the codegen layer sits one abstraction below the useTrackedMutation wrapper. The M-009 hard-zero guard (scripts/ci-guards/bundle-8-M-009-bare-usemutation.sh) treats any `useMutation(` call outside the wrapper as a regression, so the Sprint 5 push immediately tripped CI's Frontend Build job with the generated sites listed verbatim. The fix mirrors the existing _test.go exclusion: add a grep -v line for `^web/src/api/generated/` after the existing wrapper-internal + test-file exclusions. The contract going forward is composition: hand-written feature code consumes the generated hook AND wraps the mutation through useTrackedMutation at the call site (the wrapper's `mutationFn` argument receives the generated hook's mutationFn). Hand-editing the generated tree to add the wrapper inline is not an option — every regenerate would blow it away. Smuggling-via-codegen risk: the drift guard (scripts/ci-guards/openapi-codegen-drift.sh) was flipped to a hard gate in the same Sprint 5 ARCH-001-A commit. It pins the generated tree against the canonical api/openapi.yaml — any hand-edit shows up as a regenerate-diff red. So a malicious or accidental `useMutation` snuck into the generated tree as a hand-edit gets caught by the drift guard before this M-009 carve-out can apply. Verified locally: bash scripts/ci-guards/bundle-8-M-009-bare-usemutation.sh # M-009 bare-usemutation: clean (wrapper-internal call + test files excluded). # M-009 informational: useTrackedMutation sites = 66; invalidation surface = 129. Closes the M-009 leg of the Sprint 5 CI redness. --- .../ci-guards/bundle-8-M-009-bare-usemutation.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/ci-guards/bundle-8-M-009-bare-usemutation.sh b/scripts/ci-guards/bundle-8-M-009-bare-usemutation.sh index 9ea3a96..e117dc0 100755 --- a/scripts/ci-guards/bundle-8-M-009-bare-usemutation.sh +++ b/scripts/ci-guards/bundle-8-M-009-bare-usemutation.sh @@ -25,9 +25,24 @@ set -e # useMutation-mocking test patterns and the wrapper's own unit # tests don't trip the production guard — symmetric with L-015 # and L-019 above. +# +# Sprint 5 ARCH-001-A carve-out (2026-05-16): web/src/api/generated/** +# is the Orval-generated React Query layer (TanStack Query client +# mode, tags-split). Orval's mutation template calls bare useMutation +# directly — by design, because the generated layer is one +# abstraction below the wrapper. The Composition pattern is: hand- +# written feature code consumes the generated hooks AND wraps any +# mutation through useTrackedMutation at the call site (which routes +# through the generated hook's mutationFn under the hood). Adding the +# wrapper inside the generated tree would be overwritten on every +# regenerate, so the contract is "wrapper at the feature layer, not +# the codegen layer." The drift guard (openapi-codegen-drift.sh) +# already pins the generated tree against the canonical openapi.yaml, +# so this exclusion can't be abused to smuggle in a hand-edit. BARE=$(grep -rnE '\buseMutation\(' web/src/ 2>/dev/null \ | grep -v 'web/src/hooks/useTrackedMutation\.ts' \ | grep -vE '\.test\.(ts|tsx)(:[0-9]+)?:' \ + | grep -v '^web/src/api/generated/' \ || true) if [ -n "$BARE" ]; then echo "::error::M-009 hard-zero regression: bare useMutation() call(s) outside the wrapper:"