I built a check, and the first thing it caught was me
Production AI systems · Verification gates
What you will leave with
Add a route registry, a duplication ratchet, a CI workflow, and a negative test that proves both gates reject.
The gate you will ship
Two detectors, one required workflow, and proof that red really blocks.
- 01Route registryOne named source for internal paths.
- 02AST ruleRejects new hardcoded internal links.
- 03jscpdMeasures structural duplication as a ratchet.
- 04CI jobRuns both checks on every change.
- 05Branch rulePrevents a red job from merging.
Build it in sequence
Centralise internal routes
Use constants for fixed routes and functions for parameterised routes.
export const ROUTES = {
HOME: '/',
DASHBOARD: '/dashboard',
occupation: (code: string) => `/occupations/${code}` as const,
reportDetail: (id: string) => `/dashboard/reports/${id}` as const,
} as constReject hardcoded internal links
The real custom rule checks router push/replace, redirects, Link, and anchor href values as syntax-tree nodes. It ignores protocol-relative URLs and dynamic expressions.
function isInternalPath(value) {
return typeof value === 'string'
&& value.startsWith('/')
&& !value.startsWith('//')
}
function checkLiteral(context, node) {
if (node?.type === 'Literal' && isInternalPath(node.value)) {
context.report({
node,
message: 'Use a ROUTES constant instead of a hardcoded path.',
})
}
}
// The production rule calls checkLiteral for router.push/replace,
// redirect/permanentRedirect, and JSX href attributes.Add a duplication ratchet
Measure the current baseline first. Set the threshold slightly above it, then lower it as duplication is removed.
{
"path": ["src"],
"format": ["typescript", "tsx"],
"mode": "mild",
"threshold": 3.6,
"reporters": ["console"],
"ignore": ["**/*.test.ts", "**/*.test.tsx", "**/*.d.ts"]
}The repository measured a 3.06% baseline and set 3.6% as the first enforceable ceiling. Do not reuse that number without measuring your own tree.
Run both checks raw in CI
Do not pipe the duplication command through grep or tee without preserving its status. The detector must own the exit code.
duplication:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- name: Copy-paste detection (jscpd)
run: npx --yes --package=jscpd@4.0.5 jscpd src --config .jscpd.jsonProve the failure path
Commit one forbidden link and one duplicated block on a throwaway branch. Capture the red output, then revert.
error Use a named route from src/lib/routes.ts
ERROR: duplication 4.37% exceeds threshold 3.60%
Process completed with exit code 1Do not let “no checks yet” mean green
The repository cannot rely on paid branch protection, so its sanctioned merge script waits for every always-on check to register, complete, and succeed. Missing stays pending; red refuses the merge.
REQUIRED_CHECKS=(verify duplication type-drift permission-snapshot e2e-filter)
for check in "${REQUIRED_CHECKS[@]}"; do
state="$(status_of "$check" "$ROLLUP")"
if [ "$state" != "COMPLETED" ]; then
pending+=("$check:$state")
fi
done
# Missing is never success. Any failed conclusion exits without merging.Definition of done
- Existing call sites use the registry.
- New hardcoded internal links fail lint.
- Duplication threshold is based on a measured baseline.
- CI preserves each detector’s exit code.
- The workflow is a required branch check.
- Both negative tests have been observed failing.
What breaks and what it means
The duplication job prints an error but CI stays green.
A pipe or wrapper swallowed the detector’s exit code. Run it directly or enable pipefail.
The route rule produces hundreds of errors.
Migrate first, enable as warning during the sweep, then switch to error in the same change that reaches zero.
Questions, corrections, and useful disagreement
Sign in with GitHub to join the conversation. Comments are public. Article views are anonymous and counted once per browser, per article, each day.