### Task 1: Shell 契约测试脚手架 + nginx 失败用例（RED）

**Files:**
- Create: `tests/deploy/baota/fixtures/web-output/.output/server/index.mjs`
- Create: `tests/deploy/baota/run-all.sh`
- Create: `tests/deploy/baota/test-nginx-web-location.sh`
- Test: `tests/deploy/baota/test-nginx-web-location.sh`

**Interfaces:**
- Consumes: none
- Produces: 可执行 `bash tests/deploy/baota/run-all.sh`；nginx 测试当前应对**现有** `nginx.rewrite` **失败**

- [ ] **Step 1: 创建夹具与 runner**

夹具文件内容（最小可执行 stub）：

```js
// tests/deploy/baota/fixtures/web-output/.output/server/index.mjs
console.log('fixture-nuxt')
```

`run-all.sh`：

```bash
#!/bin/bash
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
fail=0
for t in "$ROOT"/tests/deploy/baota/test-*.sh; do
  echo "==> $t"
  bash "$t" || fail=1
done
exit "$fail"
```

- [ ] **Step 2: 写 nginx 契约测试（期望当前失败）**

```bash
#!/bin/bash
# tests/deploy/baota/test-nginx-web-location.sh
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../../.." && pwd)"
NGINX="$ROOT/deploy/baota/nginx.rewrite"

grep -qE 'location[[:space:]]+=[[:space:]]+/web[[:space:]]*\{' "$NGINX"
grep -q 'return 301 /web/;' "$NGINX"
grep -qE 'location[[:space:]]+\^~[[:space:]]+/web/' "$NGINX"
# proxy_pass 必须无尾斜杠
grep -qE 'proxy_pass http://127\.0\.0\.1:3000;' "$NGINX"
! grep -qE 'proxy_pass http://127\.0\.0\.1:3000/;' "$NGINX"

# /web/ 块必须出现在「location / {」兜底之前
web_line="$(grep -nE 'location[[:space:]]+\^~[[:space:]]+/web/' "$NGINX" | head -1 | cut -d: -f1)"
root_line="$(grep -nE '^location / \{' "$NGINX" | head -1 | cut -d: -f1)"
test -n "$web_line" && test -n "$root_line" && test "$web_line" -lt "$root_line"
```

- [ ] **Step 3: 运行确认 RED**

Run: `bash tests/deploy/baota/test-nginx-web-location.sh`  
Expected: FAIL（找不到 `/web` location）

- [ ] **Step 4: Commit**（仅当用户要求提交时执行）

```bash
git add tests/deploy/baota/
git commit -m "test: add baota nginx /web contract tests (red)"
```

---

