fix(ci-guard): M-009 — exclude Orval-generated tree from bare-useMutation scan

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.
This commit is contained in:
shankar0123
2026-05-16 05:36:26 +00:00
parent 6acf3559a3
commit c7f3ec6290
@@ -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:"