From eec26c7cfedf8b308c523a3f839b4029fe455f4a Mon Sep 17 00:00:00 2001 From: shunli Date: Sat, 19 Sep 2026 19:52:17 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DR101=E6=A8=A1=E6=8B=9F?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E9=9A=94=E7=A6=BB=E5=B9=B6=E6=8E=A5=E6=94=B6?= =?UTF-8?q?=E5=AE=9A=E5=90=91=E9=AA=8C=E6=94=B6=EF=BC=8809-06-release-gate?= =?UTF-8?q?-integrity=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../09-06-release-gate-integrity/design.md | 18 +++++ .../09-06-release-gate-integrity/implement.md | 23 +++++++ .../tasks/09-06-release-gate-integrity/prd.md | 12 ++++ .../09-06-release-gate-integrity/task.json | 65 +++++++++++++++++-- .../e2e/r101-mobile-wip-closeout.spec.ts | 15 ++++- 5 files changed, 126 insertions(+), 7 deletions(-) diff --git a/.trellis/tasks/09-06-release-gate-integrity/design.md b/.trellis/tasks/09-06-release-gate-integrity/design.md index 6d8b6e5a..dbe54c50 100644 --- a/.trellis/tasks/09-06-release-gate-integrity/design.md +++ b/.trellis/tasks/09-06-release-gate-integrity/design.md @@ -1,3 +1,21 @@ +## 2026-09-19 R101通过后的独立完整候选请求 + +仅四份任务接收/决策记录相对定向输入改变,R101测试保持独立复验时原字节;本地提交5路径后冻结整树(任务记录也不再编辑)。新交接为r101-session-isolation-20260919/final-candidate/claude-request.md,旧r101根目录claude-request.md与focused-started.json保持已消耗状态。 + +新run-once.py直接逐字节复制已经审查并运行的cache-copy-20260919/final-candidate/run-once.py,目录深度相同;原ROOT/R1/SOURCE计算不变,BASE自然指向新目录,因此输入文件、marker、输出为本次独占。只读核摘要与新candidate-inputs,复用既有安全审核,避免再造执行逻辑。新固定clean SHA、正式内部链接复制器、C组冻结源、双引擎先验、原完整门禁、离线verify、短私有运行环境及owner清理全沿用。 + +不再单独运行R101 3项、复制工具12项或探针30/7项;完整门禁内部必需阶段不能删。现行R2要求同一clean候选完整通过,故旧371/372失败不能由定向3/3抵扣;这次是修正后独立一次完整尝试,不是复用旧marker或循环到绿。失败即保留并报告,不自动再次启动;无推送部署授权。 + +## 2026-09-19 R101定向修复与验证 + +证据:final-linked-u450kf2s的error-context和失败PNG均为登录页,非标题改名。MobileMePage仍使用PageHeader title=我的;生产PWA的NetworkFirst接管/api/me,page.route不能接管Worker请求。R101缺失既有规范要求的test.use({serviceWorkers: block}),而同类r92/user-onboarding已有此隔离。旧运行无trace/请求归属观测,故仅确认契约漏项和退登录现象,不把机制推断写成唯一根因。 + +最小实现只改R101 spec:文件级block;/mobile/me原page.goto之前监听refresh与/me真实响应,要求200、非Worker响应、R101专属requestId,并保留准确到达/原标题和后续三条设计断言。只检查安全路径/状态/来源标记,不记录令牌、完整响应或cookie值;不调用额外refresh、不重新登录、不增加等待预算或重试。沿用现有mock会话提示和认证链。 + +定向入口复用原run-web-gates.sh retained-local,QA_RETAINED_SPECS仅r101-mobile-wip-closeout.spec.ts,不设grep;真实生产构建、独占合成库/账号、native/guest与owner清理照旧,零重试。仅该文件3项,因此实际文件级并发自然为1;不改全局workers配置。新ignored封装器固定HEAD+5份预期dirty及摘要,调用正式复制器建立新缓存,短私有runtime/白名单环境/只查真实配置存在性,独立一次marker;不重复外部先验/复制器单测/后端全量或其他浏览器文件。 + +本轮是既有mock隔离合同补漏及全文件见证,不新增旧版单worker红/绿因果矩阵,不宣称历史唯一根因已验证。0f9e7049已通过的login-portal-ux bootstrap及后端认证相关原日志按未改输入保留,不重复测试;如修后仍退登录,再基于具体失败调查真实会话。真实PWA A/B用例与其allow策略保持,本次定向不抵扣完整发布资格。 + ## 2026-09-19 修正后最终候选单次启动 新ignored启动器固定最终commit+clean和运行输入摘要;唯一源为三组对照C保留cache,源结构/字节/模式/131条内部链接指纹冻结。调用已独立测试的copy-browser-cache.py生成本轮新副本与独立复制回执,不从历史缓存执行。用已审comparison execute_case函数做一次双引擎先验,只导入函数、不运行三组main/旧marker;新副本及原无外网namespace拓扑,失败即停。 diff --git a/.trellis/tasks/09-06-release-gate-integrity/implement.md b/.trellis/tasks/09-06-release-gate-integrity/implement.md index 103a951c..c081ca24 100644 --- a/.trellis/tasks/09-06-release-gate-integrity/implement.md +++ b/.trellis/tasks/09-06-release-gate-integrity/implement.md @@ -1,3 +1,26 @@ +## 2026-09-19 R101补验已接收,保存新候选并准备独立完整请求(当前) + +Claude报告claude-focused-5m9p8_o7:copy/focused/wrapper均0,351.918秒,3/3 expected、零失败/跳过/重试;实际1worker(全局4未改)。原失败用例两张截图及全部响应/页面断言走完,native/guest、owner清理通过。主会话实算112输入、5份after/before一致;原报告“稳定通过”仅按单次通过接收,不扩大因果结论。 + +- [x] 接收定向PASS与只读补审PASS;旧focused请求/marker已消耗,绝不重复派发。 +- [x] 按既有授权决定本地提交5路径;测试原字节不变,仅4份任务记录接收更新。实际SHA/clean以本轮final-candidate/local-commit.json为准。 +- [ ] Claude核新独立请求与输入后,一次复制→双引擎先验→原完整门禁→离线verify;不额外重跑旧定向。 +- [ ] 接收新完整结果,另行处理推送部署与12公网义务;本地资格与交付资格分开。 + +唯一新入口:artifacts/qa-governance/r1-enforcement-20260919/r101-session-isolation-20260919/final-candidate/claude-request.md。该目录run-once与已审上一版逐字节相同,只由所在目录定位新的输入/marker/输出;不得使用父目录旧run-focused-once。主会话不启动测试,后续结果只写ignored目录以保持新候选clean。旧完整FAIL、37项FAIL、registry FAIL、CSV NOT_RUN、docs22提示及12公网义务原样保留,当前deploymentEligible=false。 + +## 2026-09-19 接回371/372:R101模拟会话隔离定向续修(当前) + +79输入及clean已实核;复制、先验、native/guest均通过,12阶段完成且11通过;后端4449/1条件跳过,浏览器371通过/1失败,原完整FAIL/exit1保留。截图确认/mobile/me最终是登录页;不是“我的”标题被改。现有R101 page.route测试遗漏Worker隔离,与既有frontend spec合同冲突。未获得网络归属trace,不声称唯一根因已确定。 + +- [x] 接收原回执、完整阶段及截图/error-context;记录接收前79摘要零漂移。 +- [x] 更新限定方案:只改R101夹具隔离及响应来源断言,保持产品/原3用例/视觉断言/预算。 +- [x] 最小实施与交接脚本落盘;主会话仅做语法/治理/格式自查,实测回执见本包static-checks.json。 +- [ ] Claude执行一次全文件3项受管复验与只读补审,输入按本包manifest/run-inputs冻结。 +- [ ] 接回结果再决定提交与最终候选;本次不启动或自动授权下一次完整门禁。 + +证据及交接:artifacts/qa-governance/r1-enforcement-20260919/r101-session-isolation-20260919/。旧整轮FAIL、37项FAIL、registry FAIL、CSV NOT_RUN、docs22提示及12公网义务保持。当前deploymentEligible=false,无推送部署/生产ARM/真实配置密钥备份/旧恢复/编号启用/差旅解冻;主会话不运行测试。 + ## 2026-09-19 缓存修正12/12及独立复核通过:提交最终候选并交Claude单次门禁(当前) 已核原报告/单测日志及7份after、14输入摘要,12passed/0failed/0skipped、exit0;只读复核无问题。复制工具、测试、spec保持已审字节,仅本任务四份接收/执行记录更新。真实缓存复制尚未验证,不因合成通过宣称发布资格。 diff --git a/.trellis/tasks/09-06-release-gate-integrity/prd.md b/.trellis/tasks/09-06-release-gate-integrity/prd.md index 69b6064d..8e986028 100644 --- a/.trellis/tasks/09-06-release-gate-integrity/prd.md +++ b/.trellis/tasks/09-06-release-gate-integrity/prd.md @@ -1,3 +1,15 @@ +## 2026-09-19 R101定向3/3接收,固定新候选进入最终验收 + +R101一次生产构建定向复验3/3通过,零失败/跳过/重试;只读补审通过,接收时112输入及5份before/after均一致。新增响应来源检查、准确到达、原标题/入口/设计断言与两张截图全部走完。只证明该次通过,不宣称统计稳定性或历史唯一根因已确定。 + +按既有本地提交授权保存5路径补丁;随后冻结新clean SHA,单独新请求/新marker安排一次原完整verify-candidate。旧focused请求已消耗,禁止再次派发或删除其marker;旧371/372 FAIL与3/3定向PASS分别保留,不能拼接完整资格。推送、部署、公网与真实业务边界保持。 + +## 2026-09-19 最终候选仅R101失败:补齐模拟接口隔离合同 + +0f9e7049完整尝试已结束:复制/双引擎先验及门禁native/guest通过,static通过、full-local 11/12通过;后端4449通过/1原条件跳过,浏览器371/372、零跳过零重试。唯一R101失败截图实际为登录页,产品“我的”标题仍在;该spec使用page.route却未按现有frontend spec阻止Service Worker。不能据此宣称历史唯一根因已证明。 + +同一QA修复范围内补齐R101的测试上下文隔离,并在原完整跳转中验证refresh与/me由模拟接口返回;保留3个用例、原标题/卡片/文案/设计断言和截图。产品认证/PWA、配置、预算、全局workers不变。先Claude一次受管retained-local全文件3项,结果独立保存,不抵扣旧完整FAIL,也不自动重跑完整门禁。 + ## 2026-09-19 缓存修正定向验收通过,冻结最终本地候选 复制工具12/12合成测试及独立只读复核通过,7份增量和14条输入实算一致。按既有授权本地提交该补丁;随后Claude执行一次“独占保留内部链接复制→双引擎空白页先验→原verify-candidate完整门禁”。先验失败不得进入昂贵阶段;先验不能抵扣原门禁内native/guest或业务阶段。原失败、所有旧marker和12公网等外部义务不变。无推送部署或真实业务授权。 diff --git a/.trellis/tasks/09-06-release-gate-integrity/task.json b/.trellis/tasks/09-06-release-gate-integrity/task.json index 3fa60c88..fee7d826 100644 --- a/.trellis/tasks/09-06-release-gate-integrity/task.json +++ b/.trellis/tasks/09-06-release-gate-integrity/task.json @@ -2,7 +2,7 @@ "id": "release-gate-integrity", "name": "release-gate-integrity", "title": "部署门禁完整性与候选验收回执", - "description": "浏览器缓存复制12/12及独立复核通过,按授权本地提交最终候选;待一次正确复制、双引擎先验及原完整门禁。无推送部署授权。", + "description": "R101单文件3/3与独立补审通过;按授权保存clean新候选,准备独立一次最终完整门禁,旧请求已消耗不得重发。", "status": "in_progress", "dev_type": null, "scope": "release-gate", @@ -278,10 +278,10 @@ "historyUniqueRootCauseProven": false }, "browserCacheCopy": { - "status": "SCOPED_VERIFIED_LOCAL_COMMIT_AND_FINAL_GATE_HANDOFF", + "status": "REAL_CACHE_COPY_VERIFIED_FULL_GATE_RECEIVED", "evidence": "artifacts/qa-governance/r1-enforcement-20260919/cache-copy-20260919", "testExecutor": "Claude", - "fullGateStarted": false, + "fullGateStarted": true, "deploymentEligible": false, "executionFile": "scripts/qa/copy-browser-cache.py", "testFile": "scripts/qa/tests/browser-cache-copy.test.mjs", @@ -296,16 +296,69 @@ "independentReview": "PASS", "reception": "artifacts/qa-governance/r1-enforcement-20260919/cache-copy-20260919/final-candidate/review-reception.json", "nextHandoff": "artifacts/qa-governance/r1-enforcement-20260919/cache-copy-20260919/final-candidate/claude-request.md", - "localCommitReceipt": "artifacts/qa-governance/r1-enforcement-20260919/cache-copy-20260919/final-candidate/local-commit.json" + "localCommitReceipt": "artifacts/qa-governance/r1-enforcement-20260919/cache-copy-20260919/final-candidate/local-commit.json", + "realCopyResult": "PASS_1179_REGULAR_131_INTERNAL_LINKS" }, "finalLinkedCacheGate": { - "status": "PLANNED_NOT_RUN", + "status": "FAIL_BROWSER_R101_371_OF_372", "copyAndNativePreflight": "REQUIRED_BEFORE_ORIGINAL_FULL_GATE", "maxCompleteAttempts": 1, "maxNativePreflightAttempts": 1, "pushAuthorized": false, "deploymentAuthorized": false, - "deploymentEligible": false + "deploymentEligible": false, + "candidate": "0f9e70499417fabc188a0748ab1ed27e3538dd70", + "copyExit": 0, + "preflightExit": 0, + "fullGateExit": 1, + "offlineVerifyExit": 1, + "wrapperExit": 1, + "completedStages": 12, + "passedStages": 11, + "backendPassed": 4449, + "backendConditionalSkipped": 1, + "reception": "artifacts/qa-governance/r1-enforcement-20260919/r101-session-isolation-20260919/reception.json" } + }, + "r101SessionIsolation20260919": { + "status": "SCOPED_VERIFIED_LOCAL_COMMIT_AND_FINAL_GATE_HANDOFF", + "evidence": "artifacts/qa-governance/r1-enforcement-20260919/r101-session-isolation-20260919", + "priorCandidate": "0f9e70499417fabc188a0748ab1ed27e3538dd70", + "priorFullGate": "FAIL", + "browserPassed": 371, + "browserTotal": 372, + "uniqueHistoricalRootCauseProven": false, + "plannedTargetedSpecs": [ + "r101-mobile-wip-closeout.spec.ts" + ], + "plannedCases": 3, + "testExecutor": "Claude", + "fullGateRerunAuthorizedByThisHandoff": false, + "deploymentEligible": false, + "testCommand": "bash scripts/qa/run-web-gates.sh retained-local", + "handoff": "artifacts/qa-governance/r1-enforcement-20260919/r101-session-isolation-20260919/claude-request.md", + "testsRunByCodex": false, + "testResult": { + "passed": 3, + "failed": 0, + "skipped": 0, + "retried": 0, + "exitCode": 0 + }, + "independentReview": "PASS", + "reception": "artifacts/qa-governance/r1-enforcement-20260919/r101-session-isolation-20260919/final-candidate/review-reception.json", + "oldFocusedRequestConsumed": true, + "nextHandoff": "artifacts/qa-governance/r1-enforcement-20260919/r101-session-isolation-20260919/final-candidate/claude-request.md", + "localCommitReceipt": "artifacts/qa-governance/r1-enforcement-20260919/r101-session-isolation-20260919/final-candidate/local-commit.json" + }, + "r101FinalCandidateGate20260919": { + "status": "PLANNED_NOT_RUN", + "handoff": "artifacts/qa-governance/r1-enforcement-20260919/r101-session-isolation-20260919/final-candidate/claude-request.md", + "maxFullGateAttempts": 1, + "wrapperReuse": "BYTE_IDENTICAL_TO_PREVIOUS_REVIEWED_COMPLETE_WRAPPER", + "fullGateStarted": false, + "pushAuthorized": false, + "deploymentAuthorized": false, + "deploymentEligible": false } } diff --git a/client/tests/e2e/r101-mobile-wip-closeout.spec.ts b/client/tests/e2e/r101-mobile-wip-closeout.spec.ts index 9395608f..82fe964b 100644 --- a/client/tests/e2e/r101-mobile-wip-closeout.spec.ts +++ b/client/tests/e2e/r101-mobile-wip-closeout.spec.ts @@ -1,5 +1,8 @@ import { expect, test, type Page, type Route, type TestInfo } from "@playwright/test"; +// Keep API fixtures authoritative across full navigation in the production PWA. +test.use({ serviceWorkers: "block" }); + function envelope(data: unknown) { return { requestId: "mock-r101-mobile", data }; } @@ -376,7 +379,17 @@ test.describe("R101 mobile WIP closeout", () => { await assertNoMobileDesignRegression(page); await attachScreenshot(page, testInfo, "r101-mobile-home.png"); - await page.goto("/mobile/me"); + const [refreshResponse, meResponse] = await Promise.all([ + page.waitForResponse((response) => new URL(response.url()).pathname === "/api/auth/refresh"), + page.waitForResponse((response) => new URL(response.url()).pathname === "/api/me"), + page.goto("/mobile/me"), + ]); + for (const response of [refreshResponse, meResponse]) { + expect(response.status()).toBe(200); + expect(response.fromServiceWorker()).toBe(false); + expect((await response.json()).requestId).toBe("mock-r101-mobile"); + } + await expect(page).toHaveURL(/\/mobile\/me$/); await expect(page.getByRole("heading", { name: "我的" })).toBeVisible(); await expect(page.getByTestId("mobile-more-links")).toBeVisible(); await expect(page.getByText("低频模块放在这里")).toHaveCount(0);