Compare commits

...
17 changed files with 182 additions and 34 deletions
+7 -7
View File
@@ -254,7 +254,7 @@
"code_hotspots": [ "code_hotspots": [
{ {
"path": "frontend/styles/styles.css", "path": "frontend/styles/styles.css",
"bytes": 361780, "bytes": 361776,
"lines": 15465 "lines": 15465
}, },
{ {
@@ -279,12 +279,12 @@
}, },
{ {
"path": "frontend/app.js", "path": "frontend/app.js",
"bytes": 91151, "bytes": 89213,
"lines": 1939 "lines": 1939
}, },
{ {
"path": "frontend/pages/heaven/page.js", "path": "frontend/pages/heaven/page.js",
"bytes": 88322, "bytes": 86493,
"lines": 1830 "lines": 1830
}, },
{ {
@@ -299,7 +299,7 @@
}, },
{ {
"path": "backend/features/heaven/service.py", "path": "backend/features/heaven/service.py",
"bytes": 64421, "bytes": 63123,
"lines": 1303 "lines": 1303
}, },
{ {
@@ -309,7 +309,7 @@
}, },
{ {
"path": "frontend/pages/market/runtime.js", "path": "frontend/pages/market/runtime.js",
"bytes": 57053, "bytes": 55720,
"lines": 1333 "lines": 1333
}, },
{ {
@@ -319,7 +319,7 @@
}, },
{ {
"path": "backend/application.py", "path": "backend/application.py",
"bytes": 50934, "bytes": 49784,
"lines": 1165 "lines": 1165
}, },
{ {
@@ -329,7 +329,7 @@
}, },
{ {
"path": "database.py", "path": "database.py",
"bytes": 34013, "bytes": 33284,
"lines": 746 "lines": 746
} }
] ]
+4 -5
View File
@@ -26,6 +26,7 @@ RETIRED_FRONTEND_SOURCE_RANGES = (
(9022, 9027), (9022, 9027),
(9052, 9055), (9052, 9055),
) )
AUDITED_FRONTEND_SOURCE_LINE_COUNT = 9283
def sha256(path: Path) -> str: def sha256(path: Path) -> str:
@@ -51,12 +52,10 @@ def reassembled_frontend_runtime() -> str:
assembled.append(content) assembled.append(content)
next_line = end + 1 next_line = end + 1
original_line_count = len( if next_line != AUDITED_FRONTEND_SOURCE_LINE_COUNT + 1:
(ORIGINAL_STATIC / "app.js").read_text(encoding="utf-8").splitlines()
)
if next_line != original_line_count + 1:
raise AssertionError( raise AssertionError(
f"app.js source coverage ended at {next_line - 1}, expected {original_line_count}" "app.js source coverage ended at "
f"{next_line - 1}, expected {AUDITED_FRONTEND_SOURCE_LINE_COUNT}"
) )
return "".join(assembled) return "".join(assembled)
+9
View File
@@ -4,6 +4,7 @@ import json
import re import re
import unittest import unittest
from pathlib import Path from pathlib import Path
from unittest.mock import patch
from tests.preservation_helpers import reassembled_frontend_runtime from tests.preservation_helpers import reassembled_frontend_runtime
@@ -46,6 +47,14 @@ class FrontendBoundaryTests(unittest.TestCase):
self.assertIn("const state = window.XiaobaiState.create({", app) self.assertIn("const state = window.XiaobaiState.create({", app)
self.assertNotIn("const state = {", app) self.assertNotIn("const state = {", app)
def test_candidate_runtime_reassembly_does_not_require_original_static(self) -> None:
with patch(
"tests.preservation_helpers.ORIGINAL_STATIC",
ROOT / "missing-original-static",
):
app = reassembled_frontend_runtime()
self.assertIn("function openView(", app)
def test_runtime_page_registry_matches_governance_registry(self) -> None: def test_runtime_page_registry_matches_governance_registry(self) -> None:
expected = json.loads( expected = json.loads(
(ROOT / "config" / "pages.config.json").read_text(encoding="utf-8") (ROOT / "config" / "pages.config.json").read_text(encoding="utf-8")
+22 -1
View File
@@ -3,11 +3,16 @@ from __future__ import annotations
import json import json
import subprocess import subprocess
import sys import sys
import tempfile
import unittest import unittest
from pathlib import Path from pathlib import Path
from tools.build_api_registry import build as build_api_registry from tools.build_api_registry import build as build_api_registry
from tools.build_architecture_inventory import build as build_architecture_inventory from tools.build_architecture_inventory import (
build as build_architecture_inventory,
source_metrics,
)
from tools.verify_baseline import python_test_command
ROOT = Path(__file__).resolve().parents[1] ROOT = Path(__file__).resolve().parents[1]
@@ -34,6 +39,22 @@ class MaintenanceToolTests(unittest.TestCase):
self.assertIn("stop_e2e_server(server)", source) self.assertIn("stop_e2e_server(server)", source)
self.assertNotIn('"static/app.js"', source) self.assertNotIn('"static/app.js"', source)
def test_standalone_verifier_excludes_only_migration_comparison_modules(self) -> None:
repository_command = python_test_command(preservation_baseline=True)
standalone_command = python_test_command(preservation_baseline=False)
self.assertIn("discover", repository_command)
self.assertTrue(any(item == "tests.test_frontend_contract" for item in standalone_command))
self.assertFalse(any("test_preservation_" in item for item in standalone_command))
def test_architecture_metrics_are_independent_of_checkout_line_endings(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
lf = root / "lf.js"
crlf = root / "crlf.js"
lf.write_bytes(b"const a = 1;\nconst b = 2;\n")
crlf.write_bytes(b"const a = 1;\r\nconst b = 2;\r\n")
self.assertEqual(source_metrics(lf), source_metrics(crlf))
def test_every_tool_has_a_non_mutating_help_path(self) -> None: def test_every_tool_has_a_non_mutating_help_path(self) -> None:
for path in sorted((ROOT / "tools").glob("*.py")): for path in sorted((ROOT / "tools").glob("*.py")):
if path.name.startswith("_"): if path.name.startswith("_"):
+2 -2
View File
@@ -46,7 +46,7 @@ class RealtimeClientStub:
class FixedMarketDatetime(datetime): class FixedMarketDatetime(datetime):
fixed_now = datetime.now().astimezone().replace(hour=10, minute=30, second=0, microsecond=0) fixed_now = datetime(2026, 7, 31, 10, 30).astimezone()
@classmethod @classmethod
def now(cls, tz=None): def now(cls, tz=None):
@@ -54,7 +54,7 @@ class FixedMarketDatetime(datetime):
class FixedPreopenDatetime(datetime): class FixedPreopenDatetime(datetime):
fixed_now = datetime.now().astimezone().replace(hour=8, minute=45, second=0, microsecond=0) fixed_now = datetime(2026, 7, 31, 8, 45).astimezone()
@classmethod @classmethod
def now(cls, tz=None): def now(cls, tz=None):
+7
View File
@@ -14,6 +14,13 @@ maintenance command cannot be mistaken for a historical migration rewrite.
- `python tools/build_architecture_inventory.py [--check]`: generate or verify - `python tools/build_architecture_inventory.py [--check]`: generate or verify
`config/architecture-inventory.json` from the candidate source tree. `config/architecture-inventory.json` from the candidate source tree.
Inside the canonical `webapp/app/` checkout, `verify_baseline.py` runs the full preservation
suite against the retained original baseline and enforces `git diff --check`. In a standalone
`app/` export where that baseline and Git checkout do not exist, the same command runs all
candidate-owned tests, skips only `test_preservation_*` comparison modules, and reports the Git
check as skipped. Product, registry, JavaScript, database, and optional Playwright checks remain
active in both modes.
## Acceptance and differential checks ## Acceptance and differential checks
- `run_preservation_runtime.py`: start an isolated original or candidate runtime with an - `run_preservation_runtime.py`: start an isolated original or candidate runtime with an
+9 -7
View File
@@ -75,6 +75,14 @@ def css_layers(html: str) -> list[str]:
return re.findall(r'<link[^>]+rel="stylesheet"[^>]+href="([^"]+)"', html) return re.findall(r'<link[^>]+rel="stylesheet"[^>]+href="([^"]+)"', html)
def source_metrics(path: Path) -> dict[str, int]:
text = path.read_text(encoding="utf-8")
return {
"bytes": len(text.encode("utf-8")),
"lines": len(text.splitlines()),
}
def code_hotspots() -> list[dict[str, Any]]: def code_hotspots() -> list[dict[str, Any]]:
candidates = [ candidates = [
"backend/application.py", "backend/application.py",
@@ -99,13 +107,7 @@ def code_hotspots() -> list[dict[str, Any]]:
path = ROOT / name path = ROOT / name
if not path.is_file(): if not path.is_file():
continue continue
rows.append( rows.append({"path": name, **source_metrics(path)})
{
"path": name,
"bytes": path.stat().st_size,
"lines": len(path.read_text(encoding="utf-8").splitlines()),
}
)
return sorted(rows, key=lambda item: item["bytes"], reverse=True) return sorted(rows, key=lambda item: item["bytes"], reverse=True)
+35 -2
View File
@@ -21,6 +21,39 @@ def run(label: str, command: list[str]) -> None:
subprocess.run(command, cwd=ROOT, check=True) subprocess.run(command, cwd=ROOT, check=True)
def python_test_command(preservation_baseline: bool | None = None) -> list[str]:
if preservation_baseline is None:
preservation_baseline = (ROOT.parent / "static" / "app.js").is_file()
if preservation_baseline:
return [sys.executable, "-m", "unittest", "discover", "-s", "tests"]
modules = [
f"tests.{path.stem}"
for path in sorted((ROOT / "tests").glob("test_*.py"))
if not path.stem.startswith("test_preservation_")
]
if not modules:
raise RuntimeError("no standalone candidate tests found")
return [sys.executable, "-m", "unittest", *modules]
def verify_git_diff() -> None:
result = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
cwd=ROOT,
capture_output=True,
text=True,
check=False,
)
if result.returncode != 0:
print("\n[patch] skipped: standalone export is not a Git checkout")
return
repository_root = Path(result.stdout.strip()).resolve()
if ROOT != repository_root / "app":
print("\n[patch] skipped: standalone export is outside the canonical app path")
return
run("patch", ["git", "diff", "--check"])
def verify_database() -> None: def verify_database() -> None:
database = ROOT / "data" / "review.db" database = ROOT / "data" / "review.db"
if not database.exists(): if not database.exists():
@@ -97,7 +130,7 @@ def main() -> int:
) )
args = parser.parse_args() args = parser.parse_args()
run("python", [sys.executable, "-m", "unittest", "discover", "-s", "tests"]) run("python", python_test_command())
run( run(
"api-registry", "api-registry",
[sys.executable, "tools/build_api_registry.py", "--check"], [sys.executable, "tools/build_api_registry.py", "--check"],
@@ -117,7 +150,7 @@ def main() -> int:
"javascript", "javascript",
[node, "--check", script.relative_to(ROOT).as_posix()], [node, "--check", script.relative_to(ROOT).as_posix()],
) )
run("patch", ["git", "diff", "--check"]) verify_git_diff()
verify_database() verify_database()
if args.e2e: if args.e2e:
+5 -1
View File
@@ -39,7 +39,7 @@
## 4. 真实浏览器差分 ## 4. 真实浏览器差分
- 1920×1080日间模式检查情绪周期、集合竞价、智能选股、观势、观气、观心介绍及观心呼吸。 - 1920×1080日间模式检查情绪周期、智能选股、观势、观气、观心介绍及观心呼吸。
- 1920×1080夜间模式检查情绪周期的背景、字体、表格、几何和横向溢出。 - 1920×1080夜间模式检查情绪周期的背景、字体、表格、几何和横向溢出。
- 390×844检查情绪周期与观势:移动Shell、底部五入口、页面纵向滚动、无横向溢出及问天特效均一致。 - 390×844检查情绪周期与观势:移动Shell、底部五入口、页面纵向滚动、无横向溢出及问天特效均一致。
- 观势星空节点90个、观气100个、观心110个;三页八卦节点均为2个。 - 观势星空节点90个、观气100个、观心110个;三页八卦节点均为2个。
@@ -48,6 +48,10 @@
- 原版和迁移版检查流程均未产生新增控制台error或warn。 - 原版和迁移版检查流程均未产生新增控制台error或warn。
- 动画帧、焦点框和动态状态文字属于采样瞬时状态,因此截图文件哈希不要求相同;可见布局几何、计算样式、节点、动画名称和交互结果必须一致,本次均通过。 - 动画帧、焦点框和动态状态文字属于采样瞬时状态,因此截图文件哈希不要求相同;可见布局几何、计算样式、节点、动画名称和交互结果必须一致,本次均通过。
- 两个服务使用相同主机名、不同端口时会共享并覆盖登录Cookie;曾导致迁移版切页被误判为失效。逐服务重新登录后行为一致,该问题属于并行验收环境限制,不是产品回归。 - 两个服务使用相同主机名、不同端口时会共享并覆盖登录Cookie;曾导致迁移版切页被误判为失效。逐服务重新登录后行为一致,该问题属于并行验收环境限制,不是产品回归。
- 2026-08-01像素复核发现`frontend-migrated-auction-light-1920x1080`实际截取了登录状态失效页,
不能证明集合竞价视觉等价,文件已重命名为`INVALID-login-session`。集合竞价仍有切片05真实页面、
本切片源码/样式保真、API及Playwright证据,但最终视觉明确留待人工验收,不以替代证据冒充
本切片截图差分。
- 机器可读记录见`browser-acceptance.json`,截图均保存在本目录。 - 机器可读记录见`browser-acceptance.json`,截图均保存在本目录。
## 5. 自动验证与保留边界 ## 5. 自动验证与保留边界
@@ -1,7 +1,7 @@
{ {
"schema_version": 1, "schema_version": 1,
"tested_at": "2026-07-31T12:00:00+08:00", "tested_at": "2026-07-31T12:00:00+08:00",
"result": "passed", "result": "passed_with_documented_evidence_gap",
"environments": { "environments": {
"original": "temporary_original_runtime", "original": "temporary_original_runtime",
"migrated": "temporary_app_runtime", "migrated": "temporary_app_runtime",
@@ -15,7 +15,6 @@
"theme": "light", "theme": "light",
"views": [ "views": [
"sentiment", "sentiment",
"auction",
"screener", "screener",
"heaven_trend", "heaven_trend",
"heaven_fortune", "heaven_fortune",
@@ -44,6 +43,14 @@
"equal": true "equal": true
} }
], ],
"invalid_captures": [
{
"view": "auction",
"file": "frontend-migrated-auction-light-1920x1080.INVALID-login-session.png",
"reason": "The migrated capture shows an expired login session and is not visual-equivalence evidence.",
"replacement_claim": "No replacement screenshot claim; final visual acceptance remains manual."
}
],
"heaven_animation_contract": { "heaven_animation_contract": {
"trend_star_nodes": 90, "trend_star_nodes": 90,
"fortune_star_nodes": 100, "fortune_star_nodes": 100,
@@ -89,5 +96,6 @@
}, },
"screenshot_policy": "Dynamic animation frames, focus outlines, and live status text may change pixel hashes. Acceptance compares visible geometry, computed styles, DOM state, animation names, interaction results, and overflow behavior.", "screenshot_policy": "Dynamic animation frames, focus outlines, and live status text may change pixel hashes. Acceptance compares visible geometry, computed styles, DOM state, animation names, interaction results, and overflow behavior.",
"known_test_environment_constraint": "Original and migrated services on the same hostname share cookies across ports. Each service must be logged in separately immediately before comparison.", "known_test_environment_constraint": "Original and migrated services on the same hostname share cookies across ports. Each service must be logged in separately immediately before comparison.",
"all_equal": true "all_equal": true,
"manual_acceptance_required": true
} }
+17 -1
View File
@@ -3,6 +3,8 @@
> 基线:`dec3cd12365df5e7d4c391369f14c457cf56d7d9` > 基线:`dec3cd12365df5e7d4c391369f14c457cf56d7d9`
> 候选回档标签:`xiaobai-preservation-slice-11-candidate-20260731` > 候选回档标签:`xiaobai-preservation-slice-11-candidate-20260731`
> 严格审计候选标签:`xiaobai-preservation-slice-11-audit-candidate-20260731` > 严格审计候选标签:`xiaobai-preservation-slice-11-audit-candidate-20260731`
> 跨日复验候选标签:`xiaobai-preservation-slice-11-audit-candidate-20260801`
> 独立维护候选标签:`xiaobai-preservation-slice-11-audit-candidate-standalone-20260801`
> 当前结论:自动验收完成,等待用户人工验收;尚未执行正式切换、Docker或NAS部署 > 当前结论:自动验收完成,等待用户人工验收;尚未执行正式切换、Docker或NAS部署
## 1. 本切片做了什么 ## 1. 本切片做了什么
@@ -31,7 +33,7 @@
| 验证 | 结果 | | 验证 | 结果 |
|---|---:| |---|---:|
| 原版`python -m unittest discover -s tests -q` | 231项通过 | | 原版`python -m unittest discover -s tests -q` | 231项通过 |
| 迁移版`python -m unittest discover -s tests -q` | 302项通过 | | 迁移版`python -m unittest discover -s tests -q` | 305项通过 |
| 历史切片与前端/清理专项复核 | 通过 | | 历史切片与前端/清理专项复核 | 通过 |
| 迁移版JavaScript语法检查 | 24个文件通过 | | 迁移版JavaScript语法检查 | 24个文件通过 |
| `npx.cmd playwright test --reporter=dot` | 45项通过(1.6分钟) | | `npx.cmd playwright test --reporter=dot` | 45项通过(1.6分钟) |
@@ -39,6 +41,12 @@
| 数据库差分 | 62个schema对象、21张关键表全部一致 | | 数据库差分 | 62个schema对象、21张关键表全部一致 |
| `git diff --check` | 通过 | | `git diff --check` | 通过 |
2026-08-01 01:19Asia/Shanghai)跨日复验时,原版与迁移版共用的实时详情测试暴露出
测试夹具依赖运行日的问题:夹具在周六动态生成“今日”,而业务正确拒绝在非交易日合并实时
行情。两版测试夹具同步固定到明确的工作日盘中/盘前时点,产品代码与行情日期规则未改。
修复后统一验收命令再次通过302项测试、24个JavaScript文件、SQLite完整性和45项Playwright
2.0分钟)。
早期切片的五个前端源码测试原本要求与未试删的`static/app.js`逐字符一致。本切片把它们统一到 早期切片的五个前端源码测试原本要求与未试删的`static/app.js`逐字符一致。本切片把它们统一到
`preservation_helpers.assert_frontend_runtime_matches_audited_baseline`:只允许审计登记的五段原始 `preservation_helpers.assert_frontend_runtime_matches_audited_baseline`:只允许审计登记的五段原始
行号被删除,任何其他新增、删除、重排或内容变化仍会使全部历史测试失败。 行号被删除,任何其他新增、删除、重排或内容变化仍会使全部历史测试失败。
@@ -51,6 +59,12 @@ Windows下Playwright托管Python静态服务器会在45项执行后不退出,
探活和停止8876;最终同一命令正常退出并明确报告`45 passed`。四项维护工具契约测试使迁移版 探活和停止8876;最终同一命令正常退出并明确报告`45 passed`。四项维护工具契约测试使迁移版
总数从298增加到302。逐项目标矩阵见`completion-audit.md` 总数从298增加到302。逐项目标矩阵见`completion-audit.md`
后续独立性审计把仅含`app/`的Git导出放到系统临时目录:运行模块全部解析到导出内部,统一
维护命令通过242项候选自有测试、两个注册表、24个JavaScript文件和SQLite完整性检查,并明确
跳过不存在的Git工作区。正式仓库继续执行全部保真差分,最终为305项Python测试和45项
Playwright通过。架构热点字节数已改为换行归一化后的UTF-8大小,Windows CRLF与Git/Linux LF
不会再制造假差异。
## 3. 真实浏览器验收 ## 3. 真实浏览器验收
- 桌面端复核情绪周期、智能选股三个工作区、问天日间/夜间和加载资源;控制台无新增 - 桌面端复核情绪周期、智能选股三个工作区、问天日间/夜间和加载资源;控制台无新增
@@ -63,6 +77,8 @@ Windows下Playwright托管Python静态服务器会在45项执行后不退出,
- 同账号、同数据库快照、同主题和同视口下,原版与迁移版问天布局和计算样式一致。 - 同账号、同数据库快照、同主题和同视口下,原版与迁移版问天布局和计算样式一致。
机器可读记录见`browser-acceptance.json`。截图和更广的桌面/移动基线继续沿用切片10证据目录。 机器可读记录见`browser-acceptance.json`。截图和更广的桌面/移动基线继续沿用切片10证据目录。
切片10截图的事后像素复核见`screenshot-pixel-audit.json`;九组有效截图平均色差低于0.3/255,
集合竞价候选图因登录状态失效被明确排除,仍等待用户人工视觉验收。
## 4. 数据与部署边界 ## 4. 数据与部署边界
@@ -24,7 +24,7 @@
| 前端唯一请求出口、Shell和页面职责 | 只有`frontend/shared/api.js`调用`fetch`;Shell、状态、弹窗、页面生命周期及页面模块已归位 | frontend boundary tests;切片10源码重组哈希 | 自动闭环 | | 前端唯一请求出口、Shell和页面职责 | 只有`frontend/shared/api.js`调用`fetch`;Shell、状态、弹窗、页面生命周期及页面模块已归位 | frontend boundary tests;切片10源码重组哈希 | 自动闭环 |
| CSS、主题、动画和移动行为不改写 | 原七层CSS和问天动画按字节/源码移动;令牌层与加载顺序受测试保护 | CSS governance;切片10/11浏览器证据 | 自动闭环 | | CSS、主题、动画和移动行为不改写 | 原七层CSS和问天动画按字节/源码移动;令牌层与加载顺序受测试保护 | CSS governance;切片10/11浏览器证据 | 自动闭环 |
| 不确定代码先记录再试删 | 2个文件和5个无消费者函数仅为候选试删;历史表兼容责任继续保留 | `uncertain-code-audit.md`cleanup contract | 人工待验 | | 不确定代码先记录再试删 | 2个文件和5个无消费者函数仅为候选试删;历史表兼容责任继续保留 | `uncertain-code-audit.md`cleanup contract | 人工待验 |
| 可由人工维护者复验 | 候选README/架构说明、工具分类、API/架构生成器、统一验证命令均可从`app/`独立运行 | `app/tools/README.md`maintenance tool tests | 自动闭环 | | 可由人工维护者复验 | 候选README/架构说明、工具分类、API/架构生成器、统一验证命令均可从`app/`独立运行;系统临时目录导出实测242项候选测试通过 | `app/tools/README.md`maintenance tool tests;独立导出复验 | 自动闭环 |
| 正式数据库与部署不受影响 | 验收只使用隔离SQLite副本和非8765端口 | 各切片README;运行记录 | 自动闭环 | | 正式数据库与部署不受影响 | 验收只使用隔离SQLite副本和非8765端口 | 各切片README;运行记录 | 自动闭环 |
| Docker/NAS和正式入口切换 | 用户已明确本轮不做NAS Docker测试;人工验收前禁止切换 | 迁移状态和切换指南 | 未执行 | | Docker/NAS和正式入口切换 | 用户已明确本轮不做NAS Docker测试;人工验收前禁止切换 | 迁移状态和切换指南 | 未执行 |
@@ -58,6 +58,17 @@
3. 原样资产清单、方法搬运和前端拆分明确标为迁移期工具;可能写文件的操作必须显式传路径或 3. 原样资产清单、方法搬运和前端拆分明确标为迁移期工具;可能写文件的操作必须显式传路径或
`--apply`,默认帮助路径不改文件。 `--apply`,默认帮助路径不改文件。
4. 新增维护工具契约测试,保证生成文件新鲜、所有工具`--help`可用,旧`static/`路径不会回归。 4. 新增维护工具契约测试,保证生成文件新鲜、所有工具`--help`可用,旧`static/`路径不会回归。
5. 跨到2026-08-01周六后,实时详情测试中用运行日构造的“固定时间”不再代表交易日;原版和
候选测试夹具同步固定到明确工作日,消除跨午夜/周末不确定性。产品实现与交易日规则未改,
随后统一验收再次通过302项Python测试和45项Playwright。
6. 独立导出`app/`后成功启动健康接口,236项非迁移业务/前端测试通过;同时发现六项日常前端
契约经辅助函数隐式读取旧`static/app.js`。候选运行时重组现使用已审计的9,283行覆盖边界,
只有明确的保真差分断言继续读取原版,避免未来清理旧目录后日常契约失效。
7. 对切片10十组截图重新做像素审计,九组平均色差均低于0.3/255;集合竞价候选图实际为登录
失效页,已明确标为无效并撤销其截图证明力。集合竞价最终视觉继续列为人工验收项。
8. 统一验证器现在按环境选择完整保真套件或候选自有套件;系统临时目录中的纯`app/`导出通过
242项测试、注册表、24个JavaScript文件和SQLite检查。架构热点大小按归一化UTF-8计算,
CRLF/LF不再导致清单失效;正式仓库最终通过305项测试和45项Playwright。
## 4. 仍需用户完成的最终裁决 ## 4. 仍需用户完成的最终裁决
@@ -0,0 +1,27 @@
{
"schema_version": 1,
"audited_at": "2026-08-01T01:55:00+08:00",
"source_directory": "docs/migration/evidence/slice-10",
"difference_threshold_per_channel": 8,
"valid_pairs": [
{"view": "dark-sentiment-1920x1080", "size": "1683x1080", "mean_absolute_difference": 0.065, "changed_pixel_percent": 0.123},
{"view": "dark-sentiment-390x844", "size": "375x812", "mean_absolute_difference": 0.009, "changed_pixel_percent": 0.012},
{"view": "heaven-fortune-light-1920x1080", "size": "1683x1080", "mean_absolute_difference": 0.043, "changed_pixel_percent": 0.046},
{"view": "heaven-heart-breath-light-1920x1080", "size": "1683x1080", "mean_absolute_difference": 0.256, "changed_pixel_percent": 0.545},
{"view": "heaven-heart-intro-light-1920x1080", "size": "1683x1080", "mean_absolute_difference": 0.047, "changed_pixel_percent": 0.068},
{"view": "heaven-trend-390x844", "size": "390x844", "mean_absolute_difference": 0.284, "changed_pixel_percent": 0.712},
{"view": "heaven-trend-light-1920x1080", "size": "1683x1080", "mean_absolute_difference": 0.040, "changed_pixel_percent": 0.063},
{"view": "light-1920x1080", "size": "1683x1080", "mean_absolute_difference": 0.000, "changed_pixel_percent": 0.000},
{"view": "screener-light-1920x1080", "size": "1683x1080", "mean_absolute_difference": 0.064, "changed_pixel_percent": 0.157}
],
"invalid_pairs": [
{
"view": "auction-light-1920x1080",
"mean_absolute_difference": 15.820,
"changed_pixel_percent": 77.932,
"reason": "The migrated image is an expired-login page, not the auction workspace.",
"disposition": "Renamed INVALID-login-session and excluded from visual-equivalence evidence."
}
],
"conclusion": "Nine valid pairs are geometrically and visually consistent within dynamic rendering noise. Auction remains a manual visual acceptance item."
}
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"schema_version": 1, "schema_version": 1,
"updated_at": "2026-07-31T20:57:42+08:00", "updated_at": "2026-08-01T03:14:46+08:00",
"status": "awaiting_manual_acceptance", "status": "awaiting_manual_acceptance",
"migration_mode": "behavior_preserving_source_migration", "migration_mode": "behavior_preserving_source_migration",
"source_of_truth": "current_original_webapp_runtime_and_source", "source_of_truth": "current_original_webapp_runtime_and_source",
@@ -12,7 +12,7 @@
"current_slice": "slice-11-strict-completion-audit-maintenance-handoff", "current_slice": "slice-11-strict-completion-audit-maintenance-handoff",
"last_completed_slice": "slice-10-frontend-shell-pages-components-css-mobile", "last_completed_slice": "slice-10-frontend-shell-pages-components-css-mobile",
"last_automated_slice": "slice-11-strict-completion-audit-maintenance-handoff", "last_automated_slice": "slice-11-strict-completion-audit-maintenance-handoff",
"last_checkpoint": "xiaobai-preservation-slice-11-audit-candidate-20260731", "last_checkpoint": "xiaobai-preservation-slice-11-audit-candidate-standalone-20260801",
"next_action": "user_acceptance_on_local_port_8797_then_confirm_or_restore_each_trial_retirement; do_not_switch_docker_or_nas_before_approval", "next_action": "user_acceptance_on_local_port_8797_then_confirm_or_restore_each_trial_retirement; do_not_switch_docker_or_nas_before_approval",
"authoritative_documents": [ "authoritative_documents": [
"AGENTS.md", "AGENTS.md",
+11
View File
@@ -32,6 +32,7 @@
| 2026-07-31 | `xiaobai-preservation-slice-10-20260731` | 前端Shell、页面、CSS、动画与移动端职责归位 | 源码重组、自动、API、数据库、桌面、夜间、移动端与动画差分通过,进入切片11 | | 2026-07-31 | `xiaobai-preservation-slice-10-20260731` | 前端Shell、页面、CSS、动画与移动端职责归位 | 源码重组、自动、API、数据库、桌面、夜间、移动端与动画差分通过,进入切片11 |
| 2026-07-31 | `xiaobai-preservation-slice-11-candidate-20260731` | 不确定代码审计、全量验收与人工交接准备 | 自动、API、数据库和浏览器验收通过;等待人工确认,未切换部署 | | 2026-07-31 | `xiaobai-preservation-slice-11-candidate-20260731` | 不确定代码审计、全量验收与人工交接准备 | 自动、API、数据库和浏览器验收通过;等待人工确认,未切换部署 |
| 2026-07-31 | `xiaobai-preservation-slice-11-audit-candidate-20260731` | 逐项目标审计并修复候选维护工具旧路径 | 302项测试、24个脚本、45项Playwright、21项API与62项schema差分通过;仍等待人工确认 | | 2026-07-31 | `xiaobai-preservation-slice-11-audit-candidate-20260731` | 逐项目标审计并修复候选维护工具旧路径 | 302项测试、24个脚本、45项Playwright、21项API与62项schema差分通过;仍等待人工确认 |
| 2026-08-01 | `xiaobai-preservation-slice-11-audit-candidate-standalone-20260801` | 独立维护与截图证据复核 | 正式仓库305项、独立导出242项测试通过;撤销无效竞价截图;仍等待人工确认 |
## 资产处置登记 ## 资产处置登记
@@ -216,6 +217,16 @@
`app/config/architecture-inventory.json`;逐项结论见`completion-audit.md` `app/config/architecture-inventory.json`;逐项结论见`completion-audit.md`
- 严格审计回档标签为`xiaobai-preservation-slice-11-audit-candidate-20260731`。该标签仍不是最终 - 严格审计回档标签为`xiaobai-preservation-slice-11-audit-candidate-20260731`。该标签仍不是最终
完成标签,未改变试删候选、人工验收和部署切换边界。 完成标签,未改变试删候选、人工验收和部署切换边界。
- 2026-08-01跨日复验发现原版与候选共用的一项实时详情测试夹具依赖运行日,进入周六后会把
非交易日误作预期合并日;两版测试夹具同步固定到明确工作日,产品代码和日期规则未改。
- 跨日修复后统一验收再次通过302项Python测试、24个JavaScript文件、SQLite完整性和45项
Playwright;新增候选回档标签`xiaobai-preservation-slice-11-audit-candidate-20260801`
-`app/`导出在系统临时目录成功运行统一维护命令:242项候选测试、注册表、24个JavaScript
文件和SQLite检查通过;正式仓库保真套件为305项Python测试及45项Playwright通过。
- 日常前端契约不再隐式读取旧`static/app.js`,只有迁移差分断言保留原版依赖;架构热点大小
按归一化UTF-8统计,CRLF/LF跨环境结果一致。
- 切片10集合竞价候选截图实际为登录失效页,已重命名并撤销证明力;九组有效截图像素差异
低于动态渲染噪声阈值,集合竞价最终视觉继续等待人工验收。
## 决策记录 ## 决策记录
+2 -2
View File
@@ -46,7 +46,7 @@ class RealtimeClientStub:
class FixedMarketDatetime(datetime): class FixedMarketDatetime(datetime):
fixed_now = datetime.now().astimezone().replace(hour=10, minute=30, second=0, microsecond=0) fixed_now = datetime(2026, 7, 31, 10, 30).astimezone()
@classmethod @classmethod
def now(cls, tz=None): def now(cls, tz=None):
@@ -54,7 +54,7 @@ class FixedMarketDatetime(datetime):
class FixedPreopenDatetime(datetime): class FixedPreopenDatetime(datetime):
fixed_now = datetime.now().astimezone().replace(hour=8, minute=45, second=0, microsecond=0) fixed_now = datetime(2026, 7, 31, 8, 45).astimezone()
@classmethod @classmethod
def now(cls, tz=None): def now(cls, tz=None):