chore: baseline V8.10.0-dev.1 with v8_workflows type-mapping fix and trade-pro spec
- SiteForge Enterprise V8.10.0-dev.1 upstream source (210 tests) - Fix: v8_workflows._mapped_input now reads both 类型/分类 columns so WXR imports keep post/product types instead of collapsing to page - Docs: trade-pro theme port spec (R3) and implementation plan Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,368 @@
|
||||
# trade-pro 第 5 主题移植实施计划
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 把 cms Astro 主题移植为 SiteForge V8_10 第 5 套内置主题 trade-pro(英文外贸 B2B),平台适配 + 骨架跑通 + 完整模板 + 验收。
|
||||
|
||||
**Architecture:** 三步走——先做平台适配(主题发现单一源、宏覆盖机制、主题资产优先、6 处前端入口),再以骨架主题包在真实 hesupipe 站点(`483295c29ea0`,193 条内容)跑通构建,最后按 cms 设计源(`D:\project\cms\runtimes\astro-site-runtime`)逐模板移植视觉。设计依据:[spec R3](../specs/2026-10-22-trade-pro-theme-design.md)。
|
||||
|
||||
**Tech Stack:** Python 3.12 / FastAPI / Jinja2(FileSystemLoader 多目录)/ pytest / 原生 JS。
|
||||
|
||||
**环境事实(执行者必读):**
|
||||
- 工作目录:`C:\Users\30997\Downloads\SiteForge_Factory_V5_Web_Docker\SiteForge_Enterprise_V8_10`(下称 `$ROOT`,bash 里用正斜杠)
|
||||
- Docker 实例已运行:`http://127.0.0.1:8787`(容器 siteforge-v8-10-siteforge-1/-worker-1),admin 密码在 `.env`
|
||||
- 测试命令:`python -m pytest -q`(当前 210 项全绿,任何改动后必须保持);JS 检查 `node --check app/static/<file>.js`
|
||||
- cms 设计源:`D:/project/cms/runtimes/astro-site-runtime/src`(tokens.css 250 行、base.css 445 行、components/*.astro、pages/[locale]/*.astro);像素基准 `dist/hesupipe/en/`
|
||||
- 改动 Python 后需 `docker compose up -d --build` 重建容器再验证 HTTP 层(构建约 1 分钟,有缓存)
|
||||
- 契约清单已固化于 spec §2(F1-F13),本计划直接引用
|
||||
|
||||
---
|
||||
|
||||
## Phase 1+2:平台适配与骨架跑通(Task 1-8)
|
||||
|
||||
### Task 1: Git 基线
|
||||
|
||||
**Files:** 无新文件(初始化仓库)
|
||||
|
||||
- [ ] **Step 1:** `cd $ROOT && git init && git add -A && git status --short | head -20`
|
||||
- [ ] **Step 2:** 确认 `.env`、`storage/`、`hesupipe_clean2.xml` 未被跟踪(.gitignore 已含 `.env`、`storage/`;`git check-ignore .env` 应有输出;`hesupipe_clean2.xml` 加入 .gitignore 一行)
|
||||
- [ ] **Step 3:** `git commit -m "chore: baseline V8.10.0-dev.1 with v8_workflows type-mapping fix and trade-pro spec"`(含 Co-Authored-By 行,下同)
|
||||
|
||||
### Task 2: 主题发现单一源(F1)
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/core.py:12`
|
||||
- Modify: `app/theme_pro.py:16-18`
|
||||
- Modify: `app/theme_store.py:14`
|
||||
- Test: `tests/test_theme_registry.py`(新建)
|
||||
|
||||
- [ ] **Step 1: 写失败测试**
|
||||
|
||||
```python
|
||||
"""Theme discovery must be a single source of truth."""
|
||||
from app import core, theme_pro, theme_store
|
||||
|
||||
def test_discover_returns_builtin_themes_with_manifests():
|
||||
ids = core.discover_themes()
|
||||
for tid in ('automotive', 'industrial', 'corporate', 'minimal'):
|
||||
assert tid in ids
|
||||
import json
|
||||
for tid in ids:
|
||||
manifest = json.loads((core.ROOT / 'themes' / tid / 'manifest.json').read_text(encoding='utf-8'))
|
||||
assert manifest.get('id') == tid
|
||||
|
||||
def test_three_whitelists_agree():
|
||||
assert set(core.THEMES) == set(theme_pro.THEMES) == set(theme_store.BUILTIN)
|
||||
assert isinstance(core.THEMES, tuple)
|
||||
```
|
||||
|
||||
注:`core.ROOT` 若不存在,测试里用 `pathlib.Path(app.__file__).parent.parent`。
|
||||
|
||||
- [ ] **Step 2:** `python -m pytest tests/test_theme_registry.py -q` → 预期 FAIL(`discover_themes` 不存在)
|
||||
- [ ] **Step 3: 实现**——`app/core.py:12` 替换为:
|
||||
|
||||
```python
|
||||
ROOT_PATH = __import__('pathlib').Path(__file__).resolve().parent.parent
|
||||
|
||||
def discover_themes():
|
||||
"""Single source of truth: scan themes/*/manifest.json (id must match folder)."""
|
||||
import json
|
||||
themes_dir = ROOT_PATH / 'themes'
|
||||
found = []
|
||||
for folder in sorted(themes_dir.iterdir()):
|
||||
if not folder.is_dir() or folder.name.startswith('_'):
|
||||
continue
|
||||
manifest = folder / 'manifest.json'
|
||||
if not manifest.is_file():
|
||||
continue
|
||||
try:
|
||||
if json.loads(manifest.read_text(encoding='utf-8')).get('id') == folder.name:
|
||||
found.append(folder.name)
|
||||
except (ValueError, UnicodeError):
|
||||
continue
|
||||
return tuple(found)
|
||||
|
||||
THEMES = discover_themes()
|
||||
```
|
||||
|
||||
(core.py 顶部已有 `import json`,可复用;实现时去掉重复 import,风格与文件一致。)
|
||||
|
||||
`app/theme_pro.py:16-18` 改为:
|
||||
|
||||
```python
|
||||
from .core import sanitize, route_path, THEMES
|
||||
```
|
||||
|
||||
(删除原第 18 行的元组定义。)
|
||||
|
||||
`app/theme_store.py:14` 改为:
|
||||
|
||||
```python
|
||||
from .core import THEMES as BUILTIN
|
||||
```
|
||||
|
||||
- [ ] **Step 4:** `python -m pytest tests/test_theme_registry.py -q` → PASS;`python -m pytest -q` 全绿
|
||||
- [ ] **Step 5:** `git add -A && git commit -m "feat: single-source theme discovery replaces three hardcoded whitelists"`
|
||||
|
||||
### Task 3: theme_pro 每主题环境(宏覆盖机制,F4)
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/theme_pro.py:18-20`(LOADER 旁新增 env_for)
|
||||
- Modify: `app/theme_pro.py:190`(render 用 env_for)
|
||||
- Test: `tests/test_theme_registry.py`(追加)
|
||||
|
||||
- [ ] **Step 1: 追加失败测试**
|
||||
|
||||
```python
|
||||
def test_env_for_theme_prefers_theme_dir_then_shared_root():
|
||||
from app import theme_pro
|
||||
env = theme_pro.env_for('industrial')
|
||||
paths = [str(p) for p in env.loader.searchpaths]
|
||||
assert paths[0].replace('\\', '/').endswith('themes/industrial')
|
||||
assert paths[1].replace('\\', '/').endswith('themes')
|
||||
# 共享宏库必须仍可解析(回落路径)
|
||||
assert env.get_template('_professional_components.html') is not None
|
||||
|
||||
def test_env_for_is_cached_per_theme():
|
||||
from app import theme_pro
|
||||
assert theme_pro.env_for('industrial') is theme_pro.env_for('industrial')
|
||||
```
|
||||
|
||||
- [ ] **Step 2:** 跑测试 → FAIL(env_for 不存在)
|
||||
- [ ] **Step 3: 实现**——`app/theme_pro.py` 在 LOADER 定义后追加:
|
||||
|
||||
```python
|
||||
_ENVS = {}
|
||||
|
||||
def env_for(theme):
|
||||
"""Per-theme Jinja env: theme dir first, shared themes/ root as fallback.
|
||||
|
||||
Lets a theme override shared macros (_professional_components.html) while
|
||||
inheriting everything else. Cached: one env per theme across 5000-page builds.
|
||||
"""
|
||||
env = _ENVS.get(theme)
|
||||
if env is None:
|
||||
theme_dir = ROOT / 'themes' / theme
|
||||
search = [str(theme_dir), str(ROOT / 'themes')] if theme_dir.is_dir() else [str(ROOT / 'themes')]
|
||||
env = Environment(loader=FileSystemLoader(search), autoescape=select_autoescape(['html', 'xml']))
|
||||
_ENVS[theme] = env
|
||||
return env
|
||||
```
|
||||
|
||||
`render()` 末行(:190)改为:
|
||||
|
||||
```python
|
||||
return env_for(theme).get_template('pro.html').render(**ctx)
|
||||
```
|
||||
|
||||
- [ ] **Step 4:** `python -m pytest -q` 全绿(现有 test_theme_pro_v3.py 即回归)
|
||||
- [ ] **Step 5:** commit `feat: per-theme Jinja env enables theme-level macro override`
|
||||
|
||||
### Task 4: builder 主题资产优先(F5)
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/builder.py:191`
|
||||
- Test: `tests/test_theme_registry.py`(追加)
|
||||
|
||||
- [ ] **Step 1: 追加失败测试**
|
||||
|
||||
```python
|
||||
def test_theme_blocks_css_overrides_platform(tmp_path):
|
||||
from app import builder
|
||||
from pathlib import Path
|
||||
dist = tmp_path / 'assets'; dist.mkdir()
|
||||
theme_dir = tmp_path / 'theme'; theme_dir.mkdir()
|
||||
(theme_dir / 'blocks.css').write_text('/* theme version */', encoding='utf-8')
|
||||
builder.copy_blocks_css(theme_dir, dist)
|
||||
assert (dist / 'blocks.css').read_text(encoding='utf-8') == '/* theme version */'
|
||||
# 无主题版时回落平台版
|
||||
dist2 = tmp_path / 'assets2'; dist2.mkdir()
|
||||
builder.copy_blocks_css(tmp_path / 'nonexistent', dist2)
|
||||
assert (dist2 / 'blocks.css').is_file()
|
||||
```
|
||||
|
||||
- [ ] **Step 2:** 跑 → FAIL
|
||||
- [ ] **Step 3: 实现**——`app/builder.py:191` 的 `shutil.copy2(ROOT/'app'/'static'/'blocks.css',dist/'assets'/'blocks.css')` 替换为调用新函数,并在文件合适位置(output_path 附近)定义:
|
||||
|
||||
```python
|
||||
def copy_blocks_css(theme_dir, dist_assets):
|
||||
"""Theme-provided blocks.css wins; platform default is the fallback."""
|
||||
src = theme_dir / 'blocks.css' if (theme_dir / 'blocks.css').is_file() else ROOT / 'app' / 'static' / 'blocks.css'
|
||||
shutil.copy2(src, dist_assets / 'blocks.css')
|
||||
```
|
||||
|
||||
- [ ] **Step 4:** `python -m pytest -q` 全绿;commit `feat: theme-level blocks.css takes priority over platform asset`
|
||||
|
||||
### Task 5: trade-pro 骨架主题包(Phase 2 核心)
|
||||
|
||||
**Files:**
|
||||
- Create: `themes/trade-pro/manifest.json`、`site.html`、`pro.html`、`_professional_components.html`、`theme.css`、`blocks.css`、`pro.css`、`page-presets.json`、`pages/`(14 个 html)、`visual/.gitkeep`、`versions/.gitkeep`
|
||||
- Test: `tests/test_theme_registry.py`(追加)
|
||||
|
||||
- [ ] **Step 1: 追加失败测试**
|
||||
|
||||
```python
|
||||
def test_trade_pro_registered_and_compatible():
|
||||
assert 'trade-pro' in core.THEMES
|
||||
result = theme_store.compatibility('trade-pro')
|
||||
assert result['valid'], result['errors']
|
||||
assert not result['errors']
|
||||
|
||||
def test_trade_pro_builds_real_site():
|
||||
# 集成冒烟:真实构建函数对演示站点渲染 home 不抛异常(复用现有测试夹具方式,
|
||||
# 参考 tests/test_theme_pro_v3.py 中的最小站点夹具)
|
||||
...
|
||||
```
|
||||
|
||||
(第二个测试复用 test_theme_pro_v3.py 已有的最小站点构建夹具写法,theme 传 'trade-pro'。)
|
||||
|
||||
- [ ] **Step 2:** 跑 → FAIL
|
||||
- [ ] **Step 3: 创建文件**
|
||||
|
||||
`manifest.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "trade-pro",
|
||||
"name": "Trade Pro — International B2B",
|
||||
"description": "English-first international B2B theme ported from the hesupipe Astro design: SVG icon set, responsive trade catalogue, product specs and inquiry CTA.",
|
||||
"version": "1.0.0",
|
||||
"style": "international-b2b",
|
||||
"entry": "pro.html",
|
||||
"styles": ["theme.css", "blocks.css"],
|
||||
"page_templates": ["home","about","contact","listing","product","article","case","standard","category"],
|
||||
"blocks": ["hero","richtext","features","stats","collection","image_text","gallery","faq","cta","attributes","spacer","layout","carousel","video","tabs","pricing","map","form_link"],
|
||||
"responsive": true,
|
||||
"auto_binding": true,
|
||||
"professional_layout": true,
|
||||
"commercial_ui": true,
|
||||
"accent": "#0b3d91"
|
||||
}
|
||||
```
|
||||
|
||||
`site.html`:以 `themes/industrial/site.html` 为底,改 `lang="zh-CN"` → `lang="en"`,其余变量占位不动(它只包 {{content}})。
|
||||
|
||||
`_professional_components.html`:先做**透传骨架**(从共享库复制 card/cover 等宏,Task 10 起逐个换成 cms 设计 + SVG 图标)。
|
||||
|
||||
`pro.html`:以 industrial 的 pro.html 为底最小化:`lang="en"`;页眉/页脚消费 site_design chrome 但按 spec 4.2.5 值比对输出英文兜底(中文默认值视为未配置);正文区渲染 page content 与 custom_blocks;按钮类名一律 `sf-tp-*`。
|
||||
|
||||
`theme.css`:先放 tokens 骨架(Task 9 全量移植):
|
||||
|
||||
```css
|
||||
:root{--sf-tp-primary:#0b3d91;--sf-tp-accent:#f5a623;--sf-tp-bg:#ffffff;--sf-tp-text:#1a2332;--sf-tp-muted:#5a6a7e;--sf-tp-radius:8px;--sf-tp-font:'Inter',system-ui,-apple-system,sans-serif}
|
||||
```
|
||||
|
||||
`blocks.css`:空文件 + 注释 `/* trade-pro component styles: filled in Phase 3 */`。
|
||||
`pro.css`:空文件(兼容性硬性要求,spec F8)。
|
||||
`page-presets.json`:**复制 industrial 的同名文件原样**(Phase 4 精修;13 种 key 齐全即可)。
|
||||
`pages/`:`home.html` 写最小可用骨架(hero + showcase 列表 + cta 引用共享宏);其余 13 个(about/contact/listing/product/brand/article/case/service/faq/category/search/standard/notfound)先各写薄变体(复用 home 的结构、引用同样宏),文件必须存在(spec F7 硬性)。
|
||||
|
||||
- [ ] **Step 4:** `python -m pytest -q` 全绿(含新测试)
|
||||
- [ ] **Step 5:** `docker compose up -d --build` 后:`curl -s http://127.0.0.1:8787/api/health` 版本不变、`curl -s "http://127.0.0.1:8787/api/factory/themes/trade-pro/compatibility"`(带登录 cookie)返回 valid=true
|
||||
- [ ] **Step 6:** commit `feat: trade-pro skeleton theme package (Phase 2)`
|
||||
|
||||
### Task 6: theme_color 映射 + 6 处前端入口(F3/F11)
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/theme_pro.py`(theme_color 字典,make_context 内,约 :171)
|
||||
- Modify: `app/templates/admin.html:13`(siteTheme select)
|
||||
- Modify: `app/templates/workspace.html:41`(建站模态框 theme select)
|
||||
- Modify: `app/templates/theme-gallery.html:4`(theme select)
|
||||
- Modify: `app/static/workspace.js:5`(themeName)
|
||||
- Modify: `app/static/admin.js:5`(cfg)
|
||||
- Modify: `app/static/factory-v2.js:30`(schemes)
|
||||
- Test: `tests/test_theme_registry.py`(追加)
|
||||
|
||||
- [ ] **Step 1: 追加失败测试**
|
||||
|
||||
```python
|
||||
def test_frontend_entries_include_trade_pro():
|
||||
import pathlib
|
||||
base = pathlib.Path(__file__).resolve().parent.parent
|
||||
for rel, needle in [
|
||||
('app/templates/admin.html', 'value="trade-pro"'),
|
||||
('app/templates/workspace.html', 'value="trade-pro"'),
|
||||
('app/templates/theme-gallery.html', 'value="trade-pro"'),
|
||||
('app/static/workspace.js', "'trade-pro'"),
|
||||
('app/static/admin.js', "'trade-pro'"),
|
||||
('app/static/factory-v2.js', "'trade-pro'"),
|
||||
]:
|
||||
assert needle in (base / rel).read_text(encoding='utf-8'), rel
|
||||
|
||||
def test_theme_color_map_covers_trade_pro():
|
||||
# theme_pro.make_context 的 theme_color 字典含 trade-pro(渲染 smoke 断言 meta theme-color)
|
||||
...
|
||||
```
|
||||
|
||||
- [ ] **Step 2:** 跑 → FAIL
|
||||
- [ ] **Step 3: 实现**——逐处追加(保持原文风格):
|
||||
- theme_color 字典追加 `'trade-pro':'#0b3d91'`
|
||||
- 三个 HTML select 各追加:`<option value="trade-pro">Trade Pro · 外贸 B2B(英文)</option>`(theme-gallery 用 `Trade Pro · International B2B`)
|
||||
- `workspace.js` themeName 追加 `'trade-pro':'Trade Pro'`
|
||||
- `admin.js` cfg 追加 `trade-pro:{label:'Trade Pro',desc:'英文外贸 · B2B 目录与询盘',prev:'corp',title:'Go global.'}`
|
||||
- `factory-v2.js` schemes 追加 `'trade-pro':['#0b3d91','#e8f1fb','GO GLOBAL']`
|
||||
- [ ] **Step 4:** `python -m pytest -q` 全绿 + `for f in app/static/*.js; do node --check "$f"; done` 通过
|
||||
- [ ] **Step 5:** commit `feat: register trade-pro in theme color map and all six frontend entry points`
|
||||
|
||||
### Task 7: E2E 验收——真实站点切主题构建(Phase 2 门槛)
|
||||
|
||||
**Files:** 无代码(验证 + 记录)
|
||||
|
||||
- [ ] **Step 1:** 登录拿 cookie/CSRF(参照已验证流程:POST /api/auth/login → GET /api/auth/me)
|
||||
- [ ] **Step 2:** `PUT /api/factory/sites/483295c29ea0/theme` body `{"theme":"trade-pro"}`(确认返回成功)
|
||||
- [ ] **Step 3:** `POST /api/sites/483295c29ea0/builds`(Idempotency-Key + CSRF)→ 轮询 job 至 done
|
||||
- [ ] **Step 4:** 断言:pages ≥203、errors 空;预览首页 HTTP 200、含 `lang="en"`、不含"首页/联系我们"(平台中文默认,spec F9 验收);`/products/`、`/product/alloy-resin-system-window/` 200
|
||||
- [ ] **Step 5:** 断言 6 入口:新工作台/`/classic` 建站表单、theme-gallery、factory-studio 主题卡里均出现 trade-pro(浏览器人工点验,截图存 `docs/superpowers/` 或记录于验收文件)
|
||||
- [ ] **Step 6:** 把结果写入 `docs/superpowers/specs/2026-10-22-trade-pro-theme-design.md` 附录"Phase 2 验收记录";commit
|
||||
|
||||
---
|
||||
|
||||
## Phase 3:完整视觉移植(Task 8-12)
|
||||
|
||||
> 以下任务的代码无法在计划里全文预写(从 cms 源逐段移植),但每步给出**精确源文件、提取规则与验收断言**。共用纪律:每完成一个 Task 跑全量 pytest + commit。
|
||||
|
||||
### Task 8: 设计令牌与基础样式全量移植
|
||||
|
||||
- 源:`D:/project/cms/runtimes/astro-site-runtime/src/styles/tokens.css`(250 行)+ `base.css`(445 行)
|
||||
- [ ] 变量重命名加 `--sf-tp-` 前缀(spec 4.2.6);按钮/交互元素全部 `sf-tp-` 类名(避开平台 `.btn/.primary/.request/.button/.nav-btn`)
|
||||
- [ ] 验收断言:`themes/trade-pro/theme.css` 覆盖 tokens 全集(颜色/字体/圆角/间距);构建产物 `assets/theme.css` 含 `--sf-tp-` 变量;平台 custom.css 的 `.btn{...}` 与主题按钮类无交集(grep 断言)
|
||||
- [ ] pytest(现有快照不受影响)+ commit
|
||||
|
||||
### Task 9: 图标宏库(替代箭头)
|
||||
|
||||
- 源:cms 组件与 dist HTML 中的内联 SVG(`grep -o '<svg[^>]*>' dist/hesupipe/en/index.html` 起逐个提取;参考 `src/components/*.astro` 里的图标用法)
|
||||
- [ ] `themes/trade-pro/_professional_components.html` 定义 `icon(name)` 宏:内联 SVG 集合(≥8 个:arrow-right/check/phone/mail/location/whatsapp/download/quote 等,从 cms 实际用到的集合提取)
|
||||
- [ ] 验收:home/product 页渲染输出含 `<svg` 且无裸文本箭头 `→` 作为唯一图标(对照 dist)
|
||||
- [ ] commit
|
||||
|
||||
### Task 10: 九种独立布局逐个移植(每布局一个 commit)
|
||||
|
||||
顺序与源对照(cms `src/pages/[locale]/` ↔ dist `en/` ↔ trade-pro `pages/`):
|
||||
|
||||
1. `home`:index.astro ↔ dist 首页(Hero/FeatureGrid/CardGrid/CTA 结构)
|
||||
2. `product`:products/[slug].astro ↔ dist/products/alloy-resin-system-window/(参数表 specs、多图 gallery、询盘 CTA)
|
||||
3. `listing`:news/[...page].astro + products 列表 ↔ 按 kind 分支(Phase 0 已确认模板可拿到 page/kind 上下文;若不可分支由 category 承担产品列表,spec §7 兜底)
|
||||
4. `category`:product-category ↔ 分类页
|
||||
5. `article`:news/[slug] ↔ 文章详情
|
||||
6. `about` / 7. `contact`(LeadForm 样式 + form_endpoint)/ 8. `case`(projects)/ 9. `standard`(applications、support)
|
||||
|
||||
每个布局的统一验收:构建对应真实页面 200;关键结构选择器与 dist 对照(header/hero/卡片/参数表/页脚);英文文案断言(无中文默认);手机宽度可读(人工抽查一次,全量视觉验收在 Task 12)。
|
||||
|
||||
### Task 11: 五个变体布局 + search
|
||||
|
||||
- brand/service/faq/notfound/search 以 standard/listing 变体补齐为"结构合理"版本(后续迭代精修,spec 4.3)
|
||||
|
||||
### Task 12: 预设、编辑器与全量验收(Phase 4)
|
||||
|
||||
- [ ] `page-presets.json` 13 种按 cms 首页/产品页实际区块结构重写(编辑器初始布局与真实设计一致)
|
||||
- [ ] 编辑器验证:hero/cta/features/collection/attributes 在 trade-pro 下可编辑、预览、保存草稿、发布(spec §6)
|
||||
- [ ] 全站设计草稿验证:页眉/页脚/导航配置生效(显式值渲染、中文默认被值比对替换为英文兜底)
|
||||
- [ ] 全量回归:`python -m pytest -q`(≥210+新增全绿)、`node --check` 全部 JS
|
||||
- [ ] 实战验收:站点 483295c29ea0 最终构建 ≥203 页零错误;视觉对照两层标准(spec §6)逐页记录
|
||||
- [ ] 更新 spec 附录验收记录;commit
|
||||
|
||||
## 风险与回退
|
||||
|
||||
- 每个 Task 独立 commit,出问题 `git revert` 单点回退
|
||||
- 平台适配层(Task 2-4)有 4 主题回归保护:任何现有测试变红立即停止
|
||||
- trade-pro 目录独立,删目录 + revert 白名单改动即完全移除
|
||||
Reference in New Issue
Block a user