[AZ-474] [AZ-480] Batch 8 - tile-split + nginx/image static checks (Phase A close)

- AZ-474 tile-split + YOLO parser + auto-zoom + indicator +
  malformed (FT-P-51..55, FT-N-10): 13 fast (6 it.fails for
  AC-1..6 + 7 controls) + 2 e2e (test.fail for FT-P-51 +
  FT-P-53). The split surface is QUARANTINED today (D11) —
  no Split-tile button, no parser, no <TileViewer>; all 6
  ACs are documented drift, every it.fails paired with a
  control PASS pinning current behaviour.
- AZ-480 prod image + nginx routing + RAM (NFT-RES-LIM-02
  /03/08/09/10): 4 new static checks promoted into the
  per-commit profile (STC-RES02 500M cap, STC-RES03
  Dockerfile final-stage nginx:alpine no Node, STC-RES09
  exactly 9 /api/* location blocks, STC-RES10 prefix-strip
  on every route). 3 e2e (docker-no-Node probe, runtime
  prefix-strip, long-running RAM soak — all gated on docker
  availability + image build; RAM soak also on
  RUN_LONG_RUNNING=1).

Phase A — One-time baseline setup is now COMPLETE. The
todo/ directory is empty after this batch's archival.
Cumulative review for batches 07-08 is the next autodev
action; after that, Step 7 (Run Tests) auto-chains.

Code review: PASS (0 findings). Fast: 26/26 files, 163
passed / 13 skipped. Static: 29/29 PASS (incl. 4 new
STC-RES* gates).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-11 06:12:29 +03:00
parent cdebfccada
commit f2451944fd
9 changed files with 1196 additions and 1 deletions
+104
View File
@@ -370,6 +370,106 @@ if [ "$RUN_STATIC" = "true" ]; then
return 0
}
# AZ-480 NFT-RES-LIM-02 — nginx body cap is exactly 500M (one hit in the SPA
# server block). Pinning here so a regression that loosens it (or copies a
# second cap into a wrong block) lights up at commit time.
static_check_nginx_body_cap() {
if [ ! -f "$PROJECT_ROOT/nginx.conf" ]; then
echo "nginx.conf missing" >&2
return 1
fi
local hits
hits=$(grep -cE 'client_max_body_size[[:space:]]+500M' "$PROJECT_ROOT/nginx.conf" || true)
if [ "$hits" = "1" ]; then
return 0
fi
echo "expected exactly 1 'client_max_body_size 500M' in nginx.conf, found $hits (NFT-RES-LIM-02)" >&2
return 1
}
# AZ-480 NFT-RES-LIM-03 — production image is nginx:alpine (no Node). The
# e2e runtime probe (`docker run --rm $IMAGE which node`) is the second
# half of this AC; this static gate prevents a Dockerfile change from
# silently switching the final stage to a Node-based image.
static_check_dockerfile_nginx_alpine() {
if [ ! -f "$PROJECT_ROOT/Dockerfile" ]; then
echo "Dockerfile missing" >&2
return 1
fi
if ! grep -qE '^FROM[[:space:]]+nginx:alpine' "$PROJECT_ROOT/Dockerfile"; then
echo "Dockerfile final stage must be 'FROM nginx:alpine' (NFT-RES-LIM-03)" >&2
return 1
fi
# Reject any reference to oven/bun:* or node:* OUTSIDE of the AS build
# stage. The build stage is allowed (it's a multi-stage build); the
# final stage must not reference Node.
if awk '
/^FROM/ { stage = $0; in_final = ($0 !~ /AS[[:space:]]+build/) }
in_final && /^FROM/ && /(node|oven\/bun)/ { exit 1 }
' "$PROJECT_ROOT/Dockerfile"; then
return 0
fi
echo "Dockerfile final stage references Node — must be nginx:alpine only (NFT-RES-LIM-03)" >&2
return 1
}
# AZ-480 NFT-RES-LIM-09 — exactly 9 nginx /api/* location blocks (one per
# suite service: annotations, flights, admin, resource, detect, loader,
# gps-denied-desktop, gps-denied-onboard, autopilot). The non-/api/
# `location /` SPA fallback does NOT count.
static_check_nginx_route_count() {
if [ ! -f "$PROJECT_ROOT/nginx.conf" ]; then
echo "nginx.conf missing" >&2
return 1
fi
local hits
hits=$(grep -cE '^\s*location\s+/api/' "$PROJECT_ROOT/nginx.conf" || true)
if [ "$hits" = "9" ]; then
return 0
fi
echo "expected exactly 9 nginx /api/* location blocks, found $hits (NFT-RES-LIM-09)" >&2
return 1
}
# AZ-480 NFT-RES-LIM-10 — every /api/<service>/ route strips its prefix.
# The `proxy_pass http://<host>:<port>/` form (with trailing slash) is the
# nginx-canonical "strip the matched location prefix" idiom; we assert
# every /api/* location has such a proxy_pass directly underneath it.
# Equivalent `rewrite ^/api/<S>/(.*)$ /$1 break;` would also satisfy the
# AC but is not what nginx.conf uses today.
static_check_nginx_prefix_strip() {
if [ ! -f "$PROJECT_ROOT/nginx.conf" ]; then
echo "nginx.conf missing" >&2
return 1
fi
node -e '
const fs = require("node:fs");
const conf = fs.readFileSync("nginx.conf", "utf8");
const lines = conf.split(/\r?\n/);
const fails = [];
for (let i = 0; i < lines.length; i++) {
const m = lines[i].match(/^\s*location\s+(\/api\/[^\s{]+)/);
if (!m) continue;
// Look ahead within this block for either:
// proxy_pass http://...:<port>/ (note trailing slash)
// rewrite ^/api/<S>/(.*)$ /$1 break;
const route = m[1];
let depth = 0, found = false;
for (let j = i; j < lines.length; j++) {
if (lines[j].includes("{")) depth++;
if (lines[j].includes("}")) { depth--; if (depth === 0) break; }
if (/proxy_pass\s+https?:\/\/[^/\s;]+(:\d+)?\/\s*;/.test(lines[j])) { found = true; break; }
if (/rewrite\s+\^\/api\/[^/]+\/\(\.\*\)\$\s+\/\$1\s+break;/.test(lines[j])) { found = true; break; }
}
if (!found) fails.push(route);
}
if (fails.length) {
console.error("location blocks without prefix-strip: " + fails.join(", ") + " (NFT-RES-LIM-10)");
process.exit(1);
}
'
}
# AZ-479 NFT-PERF-01 / NFT-RES-LIM-01 — initial JS bundle ≤ 2 MB gzipped.
# Same threshold + measurement as scripts/run-performance-tests.sh; this
# entry routes the gate through the static profile so every commit is
@@ -417,6 +517,10 @@ if [ "$RUN_STATIC" = "true" ]; then
run_static "STC-B1" "vite build succeeds" "AC-6" "n/a" static_check_vite_build
run_static "STC-S5" "mission-planner not in dist/" "AC-31" "n/a" static_check_dist_no_mission_planner
run_static "STC-PERF01" "initial JS bundle ≤ 2 MB gz" "NFT-PERF-01" "40" static_check_bundle_size
run_static "STC-RES02" "nginx client_max_body_size 500M" "NFT-RES-LIM-02" "n/a" static_check_nginx_body_cap
run_static "STC-RES03" "Dockerfile final stage nginx:alpine no Node" "NFT-RES-LIM-03" "n/a" static_check_dockerfile_nginx_alpine
run_static "STC-RES09" "nginx exactly 9 /api/* location blocks" "NFT-RES-LIM-09" "n/a" static_check_nginx_route_count
run_static "STC-RES10" "nginx prefix-strip on every /api/<S>/ route" "NFT-RES-LIM-10" "n/a" static_check_nginx_prefix_strip
run_static "STC-SEC1B" "no literal OWM key in dist/" "SEC-09" "63" static_check_no_owm_key_in_dist
if [ "$STATIC_FAIL" = "1" ]; then