修复R101模拟接口隔离并接收定向验收(09-06-release-gate-integrity)

这个提交包含在:
2026-09-19 19:52:17 +08:00
父节点 0f9e704994
当前提交 eec26c7cfe
共修改 5 个文件,包含 126 行新增和 7 行删除
@@ -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 修正后最终候选单次启动 ## 2026-09-19 修正后最终候选单次启动
新ignored启动器固定最终commit+clean和运行输入摘要;唯一源为三组对照C保留cache,源结构/字节/模式/131条内部链接指纹冻结。调用已独立测试的copy-browser-cache.py生成本轮新副本与独立复制回执,不从历史缓存执行。用已审comparison execute_case函数做一次双引擎先验,只导入函数、不运行三组main/旧marker;新副本及原无外网namespace拓扑,失败即停。 新ignored启动器固定最终commit+clean和运行输入摘要;唯一源为三组对照C保留cache,源结构/字节/模式/131条内部链接指纹冻结。调用已独立测试的copy-browser-cache.py生成本轮新副本与独立复制回执,不从历史缓存执行。用已审comparison execute_case函数做一次双引擎先验,只导入函数、不运行三组main/旧marker;新副本及原无外网namespace拓扑,失败即停。
@@ -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单次门禁(当前) ## 2026-09-19 缓存修正12/12及独立复核通过:提交最终候选并交Claude单次门禁(当前)
已核原报告/单测日志及7份after、14输入摘要,12passed/0failed/0skipped、exit0;只读复核无问题。复制工具、测试、spec保持已审字节,仅本任务四份接收/执行记录更新。真实缓存复制尚未验证,不因合成通过宣称发布资格。 已核原报告/单测日志及7份after、14输入摘要,12passed/0failed/0skipped、exit0;只读复核无问题。复制工具、测试、spec保持已审字节,仅本任务四份接收/执行记录更新。真实缓存复制尚未验证,不因合成通过宣称发布资格。
@@ -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 缓存修正定向验收通过,冻结最终本地候选 ## 2026-09-19 缓存修正定向验收通过,冻结最终本地候选
复制工具12/12合成测试及独立只读复核通过,7份增量和14条输入实算一致。按既有授权本地提交该补丁;随后Claude执行一次“独占保留内部链接复制→双引擎空白页先验→原verify-candidate完整门禁”。先验失败不得进入昂贵阶段;先验不能抵扣原门禁内native/guest或业务阶段。原失败、所有旧marker和12公网等外部义务不变。无推送部署或真实业务授权。 复制工具12/12合成测试及独立只读复核通过,7份增量和14条输入实算一致。按既有授权本地提交该补丁;随后Claude执行一次“独占保留内部链接复制→双引擎空白页先验→原verify-candidate完整门禁”。先验失败不得进入昂贵阶段;先验不能抵扣原门禁内native/guest或业务阶段。原失败、所有旧marker和12公网等外部义务不变。无推送部署或真实业务授权。
@@ -2,7 +2,7 @@
"id": "release-gate-integrity", "id": "release-gate-integrity",
"name": "release-gate-integrity", "name": "release-gate-integrity",
"title": "部署门禁完整性与候选验收回执", "title": "部署门禁完整性与候选验收回执",
"description": "浏览器缓存复制12/12及独立复核通过,按授权本地提交最终候选;待一次正确复制、双引擎先验及原完整门禁。无推送部署授权。", "description": "R101单文件3/3与独立补审通过;按授权保存clean新候选,准备独立一次最终完整门禁,旧请求已消耗不得重发。",
"status": "in_progress", "status": "in_progress",
"dev_type": null, "dev_type": null,
"scope": "release-gate", "scope": "release-gate",
@@ -278,10 +278,10 @@
"historyUniqueRootCauseProven": false "historyUniqueRootCauseProven": false
}, },
"browserCacheCopy": { "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", "evidence": "artifacts/qa-governance/r1-enforcement-20260919/cache-copy-20260919",
"testExecutor": "Claude", "testExecutor": "Claude",
"fullGateStarted": false, "fullGateStarted": true,
"deploymentEligible": false, "deploymentEligible": false,
"executionFile": "scripts/qa/copy-browser-cache.py", "executionFile": "scripts/qa/copy-browser-cache.py",
"testFile": "scripts/qa/tests/browser-cache-copy.test.mjs", "testFile": "scripts/qa/tests/browser-cache-copy.test.mjs",
@@ -296,16 +296,69 @@
"independentReview": "PASS", "independentReview": "PASS",
"reception": "artifacts/qa-governance/r1-enforcement-20260919/cache-copy-20260919/final-candidate/review-reception.json", "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", "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": { "finalLinkedCacheGate": {
"status": "PLANNED_NOT_RUN", "status": "FAIL_BROWSER_R101_371_OF_372",
"copyAndNativePreflight": "REQUIRED_BEFORE_ORIGINAL_FULL_GATE", "copyAndNativePreflight": "REQUIRED_BEFORE_ORIGINAL_FULL_GATE",
"maxCompleteAttempts": 1, "maxCompleteAttempts": 1,
"maxNativePreflightAttempts": 1, "maxNativePreflightAttempts": 1,
"pushAuthorized": false, "pushAuthorized": false,
"deploymentAuthorized": 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
} }
} }
@@ -1,5 +1,8 @@
import { expect, test, type Page, type Route, type TestInfo } from "@playwright/test"; 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) { function envelope(data: unknown) {
return { requestId: "mock-r101-mobile", data }; return { requestId: "mock-r101-mobile", data };
} }
@@ -376,7 +379,17 @@ test.describe("R101 mobile WIP closeout", () => {
await assertNoMobileDesignRegression(page); await assertNoMobileDesignRegression(page);
await attachScreenshot(page, testInfo, "r101-mobile-home.png"); 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.getByRole("heading", { name: "我的" })).toBeVisible();
await expect(page.getByTestId("mobile-more-links")).toBeVisible(); await expect(page.getByTestId("mobile-more-links")).toBeVisible();
await expect(page.getByText("低频模块放在这里")).toHaveCount(0); await expect(page.getByText("低频模块放在这里")).toHaveCount(0);