# 阶段 4 真实 hesupipe 端到端验收证据(Plan 4 · Task 8 —— 浏览器时刻) **verdict:PASS**(断言总表 23 项脚本断言全过 + 浏览器时刻 6 截图无破版无横向溢出;三处验收内修补——预览服务器 Location 头 ASCII 化(§5.2)、CSP 放行内联样式(§5.3)、管线资产键编码归一(§5.5,spec 复审)——均有回归测试锁定;观察项未触发主题补规则,见 §5.1) | 项 | 值 | |---|---| | 日期 | 2026-10-22 | | 环境 | Windows 11 · Node v24.13.0 · pnpm 10.7.1 · tsx 直吃 `.mjs` 编排脚本(`@sf/*` 经根 tsconfig paths 解析,与 sf CLI 同机制) | | 数据库 | 本项目 PG(docker `sf-postgres`,postgres:16,localhost:5434)临时库 `sf_phase4_hesupipe`(验后已 DROP,见 §7) | | 站点/输入 | 复用阶段 3 同源副本(`storage/incoming/` 的 WXR + 媒体 + 侧车;storage/ 不入库)——site `hesupipe-real`、origin=https://hesupipe.com、locale=en | | 全链 job(spec 复审复跑,权威) | import `c7b4fd9d-61d6-46d5-8022-86259a1675ac`(succeeded_with_issues,57,198.6ms)· publish 192 → upToDate · build `e059bf41-0ba6-4539-a9b4-6eb8a75c1b6e`(succeeded,9,497.9ms)· validation run `6459e0df-dd53-4158-b300-7c610100eeef` PASS(min-gates-v1,p0=0 p2=0)。首轮 job:import `9d82df35…`(51,123.8ms)· build `619c440c…`(11,885.4ms,artifacts/ 本地留档) | | 产物面 | dist 1029 文件(190 html + 836 媒体 + sitemap.xml + robots.txt + 404.html)· route-manifest 2772 行(html 190 / redirect 1746 / asset 836)· dist SHA-256 `b97ecbda…aa6276` | | 断言明细 | `docs/evidence/phase4-assertions.json`(脚本逐断言落盘,verdict=PASS 23/23) | | 截图 | `docs/evidence/screenshots/phase4-{mobile-375,desktop-1440}-{home,product-detail,news-detail}.png` ×6(§3) | | 编排脚本 | `scripts/e2e-real.mjs`(`chain` 全链+断言 / `serve` 截图会话常驻预览,两个子命令) | ## 1. 全链命令原文(仓库根 `D:\project\cms-one`,Git Bash) ```bash # 临时库(阶段 3 的 sf_phase3_hesupipe 已在其任务末 DROP;本任务新建同构库) docker exec sf-postgres psql -U siteforge -d postgres -c "CREATE DATABASE sf_phase4_hesupipe;" DATABASE_URL="postgres://siteforge:siteforge_dev@localhost:5434/sf_phase4_hesupipe" pnpm --filter @sf/api db:migrate # 全链 + 断言(register 副本复用 → import → publish dry-run/confirm → build → 进程内预览 # 断言 ①-⑤⑦⑧⑨ + SQL/产物计数对拍 → docs/evidence/phase4-assertions.json) DATABASE_URL_TEST="postgres://siteforge:siteforge_dev@localhost:5434/sf_phase4_hesupipe" \ pnpm exec tsx scripts/e2e-real.mjs chain --site hesupipe-real # 截图会话(serve:对既有 succeeded build 起常驻预览 127.0.0.1:4571,Playwright MCP 对其取证) DATABASE_URL_TEST="postgres://siteforge:siteforge_dev@localhost:5434/sf_phase4_hesupipe" \ pnpm exec tsx scripts/e2e-real.mjs serve --build e059bf41-0ba6-4539-a9b4-6eb8a75c1b6e --port 4571 ``` 互斥纪律(Task 5 备忘):链路执行期间零并发测试/零并发构建(vitest 全套在链路完成后单独跑,见 §5.3);astro 计时探针冻结输入重跑(`SF_BUILD_INPUT` 指向 job input 目录,前后清主题暂存 dist)。 ## 2. 断言总表(预期 vs 实测 vs 判定) 预期来源:Plan 4 Task 8 节 + 快线 §8.7。①-⑤⑦⑨ 与计数对拍由 `chain` 子命令逐项断言(明细 JSON 同步落盘);⑥⑧ 在浏览器时刻/计时节单独取证(§3/§4)。 | # | 断言项 | 预期 | 实测 | 判定 | |---|---|---|---|---| | ①a | 首页 `/` | HTTP 200 + 真实内容 | 200;站点名在体 + 最新 3 条新闻标题在体(Chile/Tanzania/Burkina Faso 三篇实题) | PASS | | ①b | 编码内容页 `/how-to-choose-pe-pipe%ef%bc%9f/` | 同上 | 200;标题"Before choosing a PE pipe, be sure to confirm the purpose"在体 | PASS | | ①c | 产品分类列表 `/product-category/hdpe-pipe-fittings/` | 同上 | 200;term 名在体;term 含 14 产品,断言抽前 3 标题在体 | PASS | | ①d | 产品详情 `/product/ecological-wooden-door/` | 同上 | 200;标题"ECOLOGICAL WOOD DOORS"在体 + 12 个本地化媒体引用 | PASS | | ①e | 新闻列表 `/news/` | 同上 | 200;最新 5 条标题在体 | PASS | | ①f | 新闻详情 `/2023-pipe-market-analysis/` | 同上 | 200;标题 + 日期"2023-11-19 13:32:01"在体 | PASS | | ② | 图片本地化抽检 10 张 | 根相对引用 + dist 文件存在 + preview 200 + assets.json 在册 | 10/10:全部 `/wp-content/uploads/…` 根相对、dist 文件在、HTTP 200(11KB-1MB 实尺寸)、assets.json 全在册 | PASS | | ③ | SEO 三件套 ×5 页 | title/description 非空 + canonical === origin+路径 | 5/5 逐页精确(含编码路径 canonical `…/how-to-choose-pe-pipe%ef%bc%9f/` 原样保持) | PASS | | ④a | 未命中 404 | 404 + noindex | status=404 + `X-Robots-Tag: noindex, nofollow`(自定义 404.html) | PASS | | ④b | 301 抽检 | `?attachment_id=` 原串 3 + 路径形态 2,逐条 301 + Location 与 manifest 原串一致 | 5/5:全 301、locationMatch 全真(ASCII 位置串逐字节等;非 ASCII 原串的头域派生形见 §5.2) | PASS | | ④c | 非 ASCII redirect → 资产终端(7 条全查) | 301 → 跟随 Location 编码形 → 终端 200 + 图片 MIME | 7/7(如 `/?attachment_id=2835` → `…School-%EF%BC%881%EF%BC%89.jpg` → 200 image/jpeg);§5.5 修补回归 | PASS | | ⑤a | sitemap.xml(盘读) | 条目 = manifest indexable html 数 | loc=190 = indexable=190 | PASS | | ⑤b | robots.txt(盘读) | 存在 + Sitemap 行指向 origin | 存在,`Sitemap: https://hesupipe.com/sitemap.xml` | PASS | | ⑥ | 浏览器时刻(375/1440 ×3 页) | 无横向溢出 + 视觉无破版 | scrollWidth 360≤375 / 1425≤1440 ×6 组合全过;6 截图样式生效无破版(§3) | PASS | | ⑦a | html 全量 | 190 行全 200 | ok=190/190(keep-alive 并发 8;content-type=text/html; charset=utf-8) | PASS | | ⑦b | redirect 全量 | 1746 行逐条 301 + Location 一致 | ok=1746/1746(含 24 条 `?attachment_id=` exact 行按原串发请求;非 ASCII Location 修复后全过,§5.2) | PASS | | ⑦c | manifest 键线形 | 全 2772 键纯 ASCII(编码线形) | nonAsciiKeys=0(§5.5 修补不变量——裸非 ASCII 键永不匹配浏览器请求形) | PASS | | ⑧ | 计时对账 | 分钟级预算内 + 与阶段 3 基线同量级 | 首轮 ~64s(import 51.1s + build 11.9s);spec 复审复跑 ~70s(import 57.2s + build 9.5s);幂等复跑 ~15s(§4) | PASS | | ⑨ | 门禁 | validation_runs PASS | job=succeeded · run PASS · min-gates-v1 · p0=0 p2=0 · issues=[] | PASS | | 计数 | content_entries | 192(129+48+15) | SQL=192 {news_article:129, product:48, basic_page:15} | PASS | | 计数 | published 指针 | 192 | SQL=192 | PASS | | 计数 | routes | 2772(190/1746/836) | SQL 分组逐桶吻合 | PASS | | 计数 | media ready | 836 | SQL=836 | PASS | | 计数 | audit content.publish | ≥1 | =1(confirm 写入) | PASS | | 计数 | 构建产物面 | dist≈1029 | distFileCount=1029 · mediaCopied=836 · sitemapEntries=190 | PASS | ## 3. 浏览器时刻(Playwright MCP × 常驻预览 127.0.0.1:4571) 三页取样(与 ① 断言页一致):首页 `/`、产品详情 `/product/ecological-wooden-door/`(12 媒体引用最多)、新闻详情 `/2023-pipe-market-analysis/`。每页双视口截图(整页)。 | 截图 | 视口 | scrollWidth(≤视口) | 视觉判定 | |---|---|---|---| | `screenshots/phase4-mobile-375-home.png` | 375×812 | 360 | 样式生效(标题层级/列表/图片自适应),无破版 | | `screenshots/phase4-mobile-375-product-detail.png` | 375×812 | 360 | 产品标题 + 木门样图横排自适应缩放,无溢出无破图 | | `screenshots/phase4-mobile-375-news-detail.png` | 375×812 | 360 | 文章标题/日期/正文排版正常,头图自适应 | | `screenshots/phase4-desktop-1440-home.png` | 1440×900 | 1425 | 桌面版式正常,新闻列表/产品分节无错位 | | `screenshots/phase4-desktop-1440-product-detail.png` | 1440×900 | 1425 | 图廊 + 描述正文排版正常 | | `screenshots/phase4-desktop-1440-news-detail.png` | 1440×900 | 1425 | 标题/日期/正文/头图排版正常 | - 溢出判据:`document.body.scrollWidth <= window.innerWidth`(六组合全 false 无横向溢出;360/1425 与视口的 15px 差是滚动条槽位)。 - 截图取自首轮 build(`619c440c`)产物;spec 复审复跑(`e059bf41`,§5.5)后浏览器复验过同一会话流程(两页 3 张原破图加载成功,见 §5.5)。 - 控制台仅 1 条噪声:`/favicon.ico` 404(源数据无 favicon,路由表无此行——如期 404,装饰性,不修,§5.4)。 ## 4. 耗时分解(vs 基线) | 阶段 | spec 复审复跑(权威) | 首轮 | 对账 | |---|---|---|---| | register(快照副本复用) | 服务内 2,874ms | 1,750ms | 首登 19.3s(阶段 3)的重哈希路径,符合内容寻址预期 | | import | **57,198.6ms** | **51,123.8ms** | 阶段 3 基线 51,768.0ms——同量级(-1.2% / +10.5%,机噪区间) | | publish(dry-run + confirm) | 6ms + 17ms(upToDate=192 幂等) | 首跑 192 发布;复跑 87ms + 77ms | — | | build 总 | **9,497.9ms** | **11,885.4ms** | 分相 marks:snapshot=431 / building=8,880 / validating+report=174(首轮 450 / 11,240 / 183) | | └ astro 纯构建探针 | 4,865ms | 4,567 / 5,650 / 7,486ms(冻结输入三跑) | building 相与探针差 ≈ 媒体复制 836 文件(~164.5MiB)+ 主题运行器编排,量级自洽 | | 全量冒烟(190 html + 1746 redirect) | **285ms** | 1,225ms | keep-alive 并发 8,quiet 服务器 | | 幂等复跑链(同库重提) | — | register 1.8s + import 复用 94ms + publish 164ms + build 复用(不重跑,读原报告) | 断言二次全过(首轮 21/21) | 分钟级预算内:首轮全链 ~64s、spec 复审复跑(新库全量,含 §5.5 修补验证)~70s、幂等复跑 ~15s。 ## 5. 观察项兑现(验收内修补与判定) ### 5.1 主题分发观察项:**未触发** 计划预告的三类分发观察项在真实 190 路由集上均未出现,无需补主题规则: - `/news/` 兄弟特例:无冲突(`/news/` 列表行唯一); - 嵌套分类前缀:数据集 product_cat 无嵌套(10 个分类全一级); - 裸 `/product-category/` 前缀:10 个归档页均有 term 且有产物(`hdpe-pipe-fittings` 14 产品为最大者); - `/products/` 走 basic_page 内容页(阶段 3 既有 exact 冲突事实的延续,generic 模板正常渲染)。 ### 5.2 预览服务器修补一:Location 头 ASCII 化(真实缺陷,HTTP 级全量校验显影) ⑦b 首轮 1739/1746(7 例失败):manifest location 含非 ASCII 码位(全角括号`(1)`、间隔号`·`)时,`res.setHeader('Location', 原串)` 触发 Node `ERR_INVALID_CHAR` 崩 500(5×500)或被按 latin1 写出 mojibake(2×301,`·` → `·`)。修补:`apps/preview-server/server.mjs` 新增 `asciiLocation()`——非 ASCII 码位按 UTF-8 百分号编码(与 WP/nginx 形态一致,大写 hex),控制字符同路编码(头注入面封死);manifest 原串保持权威,HTML 迁移页内嵌转义原文。断言侧比较改用同规则镜像(`asciiLocationMirror`),预期口径更新为"manifest 原串的 RFC 9110 头域 ASCII 派生形"。回归测试:`tests/integration/preview-server.test.ts` 测试 2b(非 ASCII redirect 行 → 301 + 编码 Location + 迁移页原文)。修后 1746/1746。 ### 5.3 预览服务器修补二:CSP 放行内联样式(浏览器时刻显影,HTTP 级不可见) 截图会话首开时页面在浏览器里裸奔(无样式)——Astro 5 把 scoped 样式内联为 `