视觉审核完成30%

This commit is contained in:
rain
2026-07-16 18:12:59 +08:00
parent cb25317412
commit 23cfd365a1
89 changed files with 3740 additions and 403 deletions
@@ -0,0 +1,284 @@
# Design Asset Pipeline v2 Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Subagents and worktrees are prohibited for this project.
**Goal:** Upgrade the existing Node/Sharp A01 pipeline with a strict manifest v2, a portable Python/Pillow pixel-quality gate, and two clean, exact-ratio A01 button v2 assets verified in the real uni-app page.
**Architecture:** Keep `design-pipeline/` as the single orchestration root. Node validates the manifest, checks consumers and runtime slots, and invokes Python; Pillow deterministically rebuilds versioned assets from committed masters and analyzes Alpha/edge contamination. A new pilot manifest is added beside schema v1 so the current reproducible pipeline is not broken before the pilot passes.
**Tech Stack:** Node.js 24 / native `node:test`, Sharp 0.34.5, Python 3.14, Pillow 12.3.0, PowerShell contracts, Chrome CDP 9222, uni-app/HBuilderX.
**Execution record (2026-07-16):** All six implementation tasks were executed in the current worktree. The importable Python module is `rebuild_a01_buttons.py` and the Node entry is `rebuild-a01-buttons.mjs`. The focused automated gates and four H5 viewports passed; Android/HBuilderX, a second-computer rebuild, and user acceptance remain outstanding. Existing handoff evidence was not overwritten; the latest internal 412×915 check stays under ignored `tmp/`.
## Global Constraints
- Do not use a worktree, subagent, `git add`, `git commit`, `git push`, `git reset`, `git checkout`, upload, or destructive cleanup.
- Preserve the current A01 and documentation changes already in the dirty worktree.
- Do not overwrite or delete `a01-primary-button.png` or `a01-secondary-button.png`; v2 outputs use new paths.
- Do not promote the pipeline beyond A01 in this implementation.
- A01 stays `[~]`; H5 evidence is internal only and Android/user verification remains outstanding.
- Fixed assets must match the actual logical slots: primary `622×92rpx`, secondary `622×100rpx`.
- Produce 3× files: primary `1866×276px`, secondary `1866×300px`.
- Button text and the WeChat icon remain code/UI nodes and must not be baked into the bitmaps.
- Every production-code task follows RED → GREEN and ends with a focused diff/status checkpoint instead of a commit.
---
### Task 1: Strict Manifest v2 Validator
**Files:**
- Create: `design-pipeline/manifests/a01-buttons-v2.json`
- Create: `design-pipeline/scripts/manifest-v2.mjs`
- Create: `design-pipeline/scripts/validate-manifest-v2.mjs`
- Create: `design-pipeline/tests/manifest-v2.test.mjs`
- Modify: `design-pipeline/package.json`
**Interfaces:**
- Consumes: a JSON file path supplied on the command line.
- Produces: `loadAndValidateManifest(filePath): Promise<ManifestV2>` and CLI output `MANIFEST-V2 PASS <path>`.
- [ ] **Step 1: Write failing Node tests**
Use `node:test` to assert that a valid fixture returns schema version 2 and that validators reject duplicate IDs, path escape, ratio mismatch over 0.5%, `uniform-only` with `scaleToFill`, unknown asset classes, and missing consumers.
```js
import test from 'node:test'
import assert from 'node:assert/strict'
import { validateManifest } from '../scripts/manifest-v2.mjs'
test('rejects a fixed bitmap whose output ratio differs from its slot', () => {
const manifest = validManifest()
manifest.assets[0].outputPixels.height = 300
assert.throws(() => validateManifest(manifest, workspace), /ratio/i)
})
```
- [ ] **Step 2: Run RED**
Run: `node --test design-pipeline/tests/manifest-v2.test.mjs`
Expected: FAIL because `manifest-v2.mjs` does not exist.
- [ ] **Step 3: Implement the minimal strict validator**
Validate these exact properties: `schemaVersion`, `page`, unique `assets[].id`, `assetClass`, workspace-safe `source/output`, `logicalSlot`, `outputPixels`, `render.scalePolicy`, `render.uniMode`, `alpha`, `edge`, `quality.maxBytes`, and non-empty `consumers`. Reject unknown top-level asset classes and calculate ratio drift as:
```js
const slotRatio = widthRpx / heightRpx
const outputRatio = width / height
const ratioDrift = Math.abs(outputRatio / slotRatio - 1)
if (ratioDrift > 0.005) throw new Error('asset ratio drift exceeds 0.5%')
```
- [ ] **Step 4: Add pilot manifest and package scripts**
Add scripts:
```json
{
"test:v2": "node --test tests/manifest-v2.test.mjs",
"validate:a01-buttons": "node scripts/validate-manifest-v2.mjs manifests/a01-buttons-v2.json"
}
```
The manifest declares the two old committed masters as `source`, versioned v2 outputs, exact slot/output dimensions, `uniform-only`, transparent outer padding, no chroma residue, sRGB, and the A01 consumer selectors `.login-submit` / `.wechat-login`.
- [ ] **Step 5: Run GREEN and checkpoint**
Run:
```powershell
npm.cmd ci --prefix design-pipeline
npm.cmd --prefix design-pipeline run test:v2
npm.cmd --prefix design-pipeline run validate:a01-buttons
git diff --check
git status --short
```
Expected: Node tests and manifest validation pass; only scoped files plus pre-existing changes appear.
### Task 2: Portable Python Pixel Quality Gate
**Files:**
- Create: `design-pipeline/requirements.txt`
- Create: `design-pipeline/scripts/asset_quality.py`
- Create: `design-pipeline/tests/test_asset_quality.py`
- Create: `design-pipeline/scripts/verify-assets.py`
**Interfaces:**
- Consumes: manifest v2 and its output PNG files.
- Produces: `analyze_asset(path: Path, spec: dict) -> dict`, non-zero CLI exit on violations, and `ASSET-QUALITY PASS <id>` per asset.
- [ ] **Step 1: Pin the supported dependency**
Create:
```text
Pillow==12.3.0
```
Pillow 12.3.0 supports Python 3.10+ including 3.14; do not add OpenCV unless a failing test proves Pillow is insufficient.
- [ ] **Step 2: Write failing Python unit tests with synthetic PNGs**
Use `unittest` and temporary images to cover exact dimensions, RGBA requirement, transparent corners, transparent outer padding, green residue, light fringe on partially transparent edge pixels, sRGB metadata warning/reporting, and maximum bytes.
```python
def test_rejects_visible_green_residue(self):
image = Image.new("RGBA", (32, 16), (0, 0, 0, 0))
image.putpixel((16, 8), (0, 255, 0, 255))
report = analyze_asset(self.save(image), self.spec())
self.assertIn("chroma residue", " ".join(report["errors"]))
```
- [ ] **Step 3: Run RED**
Run outside the sandbox if required:
```powershell
python -m venv design-pipeline/.venv
design-pipeline/.venv/Scripts/python.exe -m pip install -r design-pipeline/requirements.txt
design-pipeline/.venv/Scripts/python.exe -m unittest discover -s design-pipeline/tests -p 'test_*.py' -v
```
Expected: FAIL because `asset_quality.py` does not exist.
- [ ] **Step 4: Implement the minimal analyzer**
Implement row-wise Pillow analysis without OpenCV. Count chroma residue only where Alpha is visible. Evaluate light fringe only on pixels with `0 < alpha < 255`, so the intentionally warm-white secondary-button interior is not rejected. Require the declared transparent outer padding and exact dimensions.
- [ ] **Step 5: Implement manifest CLI and run GREEN**
`verify-assets.py` imports the analyzer, reads the manifest, validates each existing output, writes an ignored JSON report under `design-pipeline/generated/a01-buttons-v2/quality-report.json`, and exits non-zero on any error.
Run unit tests and `git diff --check`; expected unit-test PASS. Output verification remains RED until Task 3 creates v2 assets.
### Task 3: Deterministic A01 Button Rebuilder
**Files:**
- Create: `design-pipeline/scripts/rebuild-a01-buttons.py`
- Create: `design-pipeline/scripts/rebuild-a01-buttons.mjs`
- Create: `design-pipeline/tests/test_rebuild_a01_buttons.py`
- Modify: `design-pipeline/package.json`
**Interfaces:**
- Consumes: the two committed v1 master PNGs plus manifest v2.
- Produces: versioned v2 PNGs at exactly `1866×276` and `1866×300`, with transparent outer padding and no baked text/icons.
- [ ] **Step 1: Write failing reconstruction tests**
Generate synthetic framed masters with deliberately contaminated outer rows. Assert that `rebuild_button(source, output_size, trim, padding, cap_width)` removes the contaminated border, keeps transparent corners/padding, preserves left/right caps, and returns exact output size.
- [ ] **Step 2: Run RED**
Run the Python unittest command. Expected: FAIL because the rebuilder module does not exist.
- [ ] **Step 3: Implement nine-slice-like offline rebuilding**
Use Pillow only:
1. Crop the manifest-declared contaminated outer trim.
2. Split into left cap, stretchable center, and right cap.
3. Resize caps uniformly to the inner target height.
4. Resize only the center horizontally to fill the remaining width.
5. Paste onto an RGBA target with the declared transparent padding.
6. Save optimized sRGB PNG.
This is offline raster production, not runtime nine-slice; the page receives a complete exact-ratio bitmap.
- [ ] **Step 4: Add a Node orchestration script**
Add `rebuild:a01-buttons` to `package.json`, calling a small Node launcher that selects `PYTHON` or `python`, validates the manifest, runs the Python rebuilder, then runs `verify-assets.py`. A non-zero Python exit must propagate.
- [ ] **Step 5: Build actual v2 assets and run GREEN**
Run the rebuild command with Python available outside the sandbox if required. Inspect both output images with `view_image`, then run Python unit tests and output verification. Expected: exact sizes, transparent outer padding, no detected fringe/chroma residue.
### Task 4: A01 Asset Contract and Page Integration
**Files:**
- Modify: `tests/a01-a02-ui-contract.ps1`
- Modify: `tests/a01-asset-alpha-audit.ps1`
- Modify: `pages/auth/a01-entry.vue`
**Interfaces:**
- Consumes: v2 button paths and manifest render policy.
- Produces: A01 references v2 skins with no text/icon baked in and no ratio distortion.
- [ ] **Step 1: Extend the PowerShell contracts first**
Require v2 asset references, forbid the two v1 paths in A01, require `mode="aspectFit"` on both button skins, and verify exact v2 pixel sizes/Alpha corners. Keep v1 files present because other pages may still consume them.
- [ ] **Step 2: Run RED**
Run both A01 contracts. Expected: FAIL because A01 still references v1 assets with `scaleToFill`.
- [ ] **Step 3: Make the minimal page change**
Change only the two `button-skin` sources and their `mode`; do not change button copy, WeChat icon, click behavior, layout heights, or unrelated A01 styles.
- [ ] **Step 4: Run GREEN**
Run the A01 contracts and `git diff --check`. Expected: PASS.
### Task 5: Runtime Slot and Visual Verification
**Files:**
- Create: `design-pipeline/scripts/verify-runtime-slots.mjs`
- Create: `design-pipeline/tests/runtime-slot-contract.test.mjs`
- Modify: `design-pipeline/package.json`
**Interfaces:**
- Consumes: manifest v2 runtime URL, selectors, Chrome port 9222.
- Produces: `RUNTIME-ASSET-SLOTS PASS` after checking loaded sources, slot dimensions, ratio, overflow, and four viewports.
- [ ] **Step 1: Write failing Node tests for pure slot comparison**
Extract `compareSlot(actual, expected, tolerance)` and test exact match, tolerance, and mismatch. Run RED before implementation.
- [ ] **Step 2: Implement the CDP verifier**
Use the same native WebSocket request pattern as `scripts/capture-chrome-page.js`. Navigate without the existing reload race, wait for both images to load, and inspect 320×568, 360×640, 360×800, and 412×915. Assert no horizontal overflow and natural scroll at 320×568.
- [ ] **Step 3: Run Node tests and real runtime verification**
Keep HBuilderX H5 and the dedicated Chrome 9222 page running. Expected: pure tests PASS and `RUNTIME-ASSET-SLOTS PASS`.
- [ ] **Step 4: Capture one representative screenshot**
Overwrite the existing ignored A01 412×915 runtime representative instead of generating multiple similar images. Open it with `view_image` and inspect both button edges, corners, line sharpness, text/icon separation, and page layout.
### Task 6: Portable Handoff and Final Verification
**Files:**
- Modify: `docs/design/设计资产生产流水线规范.md`
- Modify: `docs/交接记录.md`
- Modify: `docs/design/P00_页面结构与资产清单.md`
**Interfaces:**
- Consumes: actual commands, paths, reports, and observed verification results.
- Produces: another Windows computer can restore Node/Python dependencies and repeat the verified static pipeline.
- [ ] **Step 1: Update documentation with facts only**
Replace “target contract” wording only for components actually implemented. Record Pillow 12.3.0, Python 3.10+ support, environment commands, manifest path, script names, v2 asset dimensions, and remaining Android/user-validation gap.
- [ ] **Step 2: Run the complete focused verification set**
Run:
```powershell
npm.cmd --prefix design-pipeline run test:v2
npm.cmd --prefix design-pipeline run validate:a01-buttons
npm.cmd --prefix design-pipeline run rebuild:a01-buttons
design-pipeline/.venv/Scripts/python.exe -m unittest discover -s design-pipeline/tests -p 'test_*.py' -v
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/a01-a02-ui-contract.ps1
powershell.exe -NoProfile -ExecutionPolicy Bypass -File tests/a01-asset-alpha-audit.ps1
npm.cmd --prefix design-pipeline run verify:a01-buttons:runtime
git diff --check
git status --short
```
- [ ] **Step 3: Report exact verification layers**
Report static pipeline, Python tests, H5 runtime and screenshot evidence separately. Do not claim Android or user acceptance. Do not commit, stage, push, upload, delete v1 assets, or promote the pipeline to other pages.