From 4a37a2fe7b2d28adbf1ceae2ca9e05cc8d28cd33 Mon Sep 17 00:00:00 2001 From: "songtianyi.theo" Date: Sun, 21 Jun 2026 21:37:31 +0800 Subject: [PATCH] test(svglide): close theme system p0 plan evidence --- .../svglide-theme-system-p0-evidence.md | 308 ++++++++++++++++++ .../theme-system-invalid/README.md | 9 + .../low-contrast/02-plan/slide_plan.json | 16 + .../low-contrast/04-svg/prepared/page-001.svg | 1 + .../06-check/theme-adherence.json | 63 ++++ .../low-contrast/06-check/theme-validate.json | 39 +++ .../receipts/theme-adherence.json | 63 ++++ .../low-contrast/receipts/theme-validate.json | 39 +++ .../02-plan/slide_plan.json | 16 + .../04-svg/prepared/page-001.svg | 1 + .../06-check/theme-adherence.json | 47 +++ .../06-check/theme-validate.json | 34 ++ .../receipts/theme-adherence.json | 47 +++ .../receipts/theme-validate.json | 30 ++ .../unknown-color/02-plan/slide_plan.json | 16 + .../04-svg/prepared/page-001.svg | 1 + .../06-check/theme-adherence.json | 61 ++++ .../06-check/theme-validate.json | 39 +++ .../receipts/theme-adherence.json | 61 ++++ .../receipts/theme-validate.json | 39 +++ .../theme-system-p0/README.md | 19 ++ .../artboard-satori/02-plan/slide_plan.json | 55 ++++ .../04-svg/prepared/page-001.svg | 1 + .../04-svg/prepared/page-002.svg | 1 + .../04-svg/prepared/page-003.svg | 1 + .../06-check/theme-adherence.json | 65 ++++ .../06-check/theme-validate.json | 55 ++++ .../receipts/theme-adherence.json | 65 ++++ .../receipts/theme-validate.json | 55 ++++ .../direct-svg/02-plan/slide_plan.json | 12 + .../direct-svg/04-svg/prepared/page-001.svg | 1 + .../direct-svg/06-check/theme-adherence.json | 41 +++ .../direct-svg/06-check/theme-validate.json | 39 +++ .../direct-svg/receipts/theme-adherence.json | 41 +++ .../direct-svg/receipts/theme-validate.json | 39 +++ .../scripts/svglide_pre_submit_review.py | 31 +- .../scripts/svglide_pre_submit_review_test.py | 29 ++ .../scripts/svglide_project_runner.py | 4 +- .../scripts/svglide_project_runner_test.py | 47 +++ .../svglide_theme_system_p0_fixture_test.py | 93 ++++++ 40 files changed, 1620 insertions(+), 4 deletions(-) create mode 100644 skills/lark-slides/references/svglide-theme-system-p0-evidence.md create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/README.md create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/02-plan/slide_plan.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/04-svg/prepared/page-001.svg create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/06-check/theme-adherence.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/06-check/theme-validate.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/receipts/theme-adherence.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/receipts/theme-validate.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/02-plan/slide_plan.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/04-svg/prepared/page-001.svg create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/06-check/theme-adherence.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/06-check/theme-validate.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/receipts/theme-adherence.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/receipts/theme-validate.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/02-plan/slide_plan.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/04-svg/prepared/page-001.svg create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/06-check/theme-adherence.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/06-check/theme-validate.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/receipts/theme-adherence.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/receipts/theme-validate.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/README.md create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/02-plan/slide_plan.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/04-svg/prepared/page-001.svg create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/04-svg/prepared/page-002.svg create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/04-svg/prepared/page-003.svg create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/06-check/theme-adherence.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/06-check/theme-validate.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/receipts/theme-adherence.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/receipts/theme-validate.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/02-plan/slide_plan.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/04-svg/prepared/page-001.svg create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/06-check/theme-adherence.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/06-check/theme-validate.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/receipts/theme-adherence.json create mode 100644 skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/receipts/theme-validate.json create mode 100644 skills/lark-slides/scripts/svglide_theme_system_p0_fixture_test.py diff --git a/skills/lark-slides/references/svglide-theme-system-p0-evidence.md b/skills/lark-slides/references/svglide-theme-system-p0-evidence.md new file mode 100644 index 000000000..6d911b458 --- /dev/null +++ b/skills/lark-slides/references/svglide-theme-system-p0-evidence.md @@ -0,0 +1,308 @@ +# SVGlide Theme System P0 Evidence + +This file is the reviewer-facing evidence for the Theme System P0 plan in +`/Users/bytedance/Desktop/SVGlide-Theme-System-立项文档.md`. + +## Scope + +P0 proves a minimal executable loop: + +- ThemeSpec exists and rejects invalid themes. +- `theme_validate` validates plan/canvas `theme_id` against the registry. +- `theme_adherence` checks final `04-svg/prepared/*.svg` colors and direct + text contrast. +- `quality_gate` consumes theme receipts and freshness. +- `production_live` requires a current human `pre_submit_review` before + `live_create`. +- `direct_svg` is not blocked by artboard package checks. + +P0 does not claim automatic aesthetics, image/gradient sampling, full CSS +cascade, editor UI, slide server changes, PPTX export, animation, narrated +output, or a complete theme/template product surface. + +P1/P2 are not targets for this evidence slice. In particular, this file does +not claim model-driven theme extraction, productized theme authoring UI, +cross-deck theme migration, export packaging, or automated aesthetic approval. + +## Branch And Baseline + +Observed during this evidence update: + +```text +worktree: /Users/bytedance/bd-projects/workspaces/SVGlide/.worktrees/cli-svglide-svg-private +branch: feat/svglide-artboard-satori +implementation base HEAD before evidence patch: 842e2d44dec7a16f1b432687e2ae4e49fb9b2333 +``` + +The original document's section 0 described an older target HEAD where many +files were still dirty overlay. Current branch HEAD already contains the Theme +System implementation files from commit `842e2d44`. + +## P0 Checklist + +| Item | Status | Evidence | +| --- | --- | --- | +| P0-0 baseline normalization | PASS | artboard renderer, package check, canvas schema, and template registry are tracked in HEAD | +| P0-1 ThemeSpec schema | PASS | `references/svglide-theme-spec.schema.json`; `svglide_theme_test.py` | +| P0-2 theme utilities | PASS | `svglide_theme.py` covers registry/theme load, stable hashes, static SVG colors, contrast | +| P0-3 theme_validate | PASS | `svglide_theme_validate.py` writes `06-check/theme-validate.json` and `receipts/theme-validate.json` | +| P0-4 theme_adherence | PASS | `svglide_theme_adherence.py` checks final prepared SVG colors, contrast, and stale theme validation | +| P0-5 pre_submit_review | PASS | `svglide_pre_submit_review.py` requires structured human approval and freshness | +| P0-6a runner theme/package stages | PASS | `svglide_project_runner.py` supports `theme_validate`, `package_check`, `theme_adherence` | +| P0-6b production_live submit gate | PASS | `production_live` path requires current `pre_submit_review` before `live_create` | +| P0-7 quality_gate integration | PASS | `svglide_quality_gate.py` consumes theme receipts; `artboard_satori` requires package check; `direct_svg` does not | +| P0-8 fixtures/evidence/regression | PASS | positive and negative fixtures added under `fixtures/svglide_artboard/theme-system-*`; this file records commands | + +Contract details closed during this update: + +- `pre_submit_review` accepts both the keyed object format and the document + example format `[{kind, path, sha256}]` for `reviewed_artifacts`. +- runner `pre_submit_review` uses the documented default human input file: + `06-check/pre-submit-human-review.json`. + +## Fixture Coverage + +Positive fixtures: + +- `skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori` + - three pages: cover, content, closing + - `generation_mode=artboard_satori` + - expected: `theme_validate=passed`, `theme_adherence=passed` +- `skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg` + - one direct SVG page + - expected: `theme_validate=passed`, `theme_adherence=passed` + - used to document that `direct_svg` does not require artboard package check + +Negative fixtures: + +- `skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color` + - expected issue: `theme_unknown_color` +- `skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast` + - expected issue: `contrast_too_low` +- `skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate` + - expected issue: `theme_validate_plan_stale` + +The fixture regression is executable through: + +```bash +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 -m unittest skills/lark-slides/scripts/svglide_theme_system_p0_fixture_test.py +``` + +## P0-8 Receipt Paths + +Positive receipt paths written by the fixture commands: + +```text +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/06-check/theme-validate.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/receipts/theme-validate.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/06-check/theme-adherence.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/receipts/theme-adherence.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/06-check/theme-validate.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/receipts/theme-validate.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/06-check/theme-adherence.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/receipts/theme-adherence.json +``` + +Negative receipt paths written by the fixture commands: + +```text +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/06-check/theme-validate.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/receipts/theme-validate.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/06-check/theme-adherence.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/receipts/theme-adherence.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/06-check/theme-validate.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/receipts/theme-validate.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/06-check/theme-adherence.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/receipts/theme-adherence.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/06-check/theme-validate.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/receipts/theme-validate.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/06-check/theme-adherence.json +skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/receipts/theme-adherence.json +``` + +## Commands Run + +Command templates used for reviewer reproduction: + +```bash +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 skills/lark-slides/scripts/svglide_theme_validate.py --pretty + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 skills/lark-slides/scripts/svglide_theme_adherence.py --pretty + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 -m unittest +``` + +Real fixture commands run for this P0-8 evidence pass: + +```bash +git -C /Users/bytedance/bd-projects/workspaces/SVGlide/.worktrees/cli-svglide-svg-private status --short --branch +git -C /Users/bytedance/bd-projects/workspaces/SVGlide/.worktrees/cli-svglide-svg-private rev-parse HEAD + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 skills/lark-slides/scripts/svglide_theme_validate.py \ + skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori \ + --pretty + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 skills/lark-slides/scripts/svglide_theme_validate.py \ + skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg \ + --pretty + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 skills/lark-slides/scripts/svglide_theme_validate.py \ + skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color \ + --pretty + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 skills/lark-slides/scripts/svglide_theme_validate.py \ + skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast \ + --pretty + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 skills/lark-slides/scripts/svglide_theme_adherence.py \ + skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori \ + --pretty + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 skills/lark-slides/scripts/svglide_theme_adherence.py \ + skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg \ + --pretty + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 skills/lark-slides/scripts/svglide_theme_adherence.py \ + skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color \ + --pretty + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 skills/lark-slides/scripts/svglide_theme_adherence.py \ + skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast \ + --pretty + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 skills/lark-slides/scripts/svglide_theme_adherence.py \ + skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate \ + --pretty + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 -m unittest \ + skills/lark-slides/scripts/svglide_theme_validate_test.py \ + skills/lark-slides/scripts/svglide_theme_adherence_test.py + +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 -m unittest skills/lark-slides/scripts/svglide_theme_system_p0_fixture_test.py +``` + +Observed P0-8 command results: + +```text +branch: feat/svglide-artboard-satori +HEAD: 842e2d44dec7a16f1b432687e2ae4e49fb9b2333 +artboard-satori theme_validate: passed, page_count=3, theme_count=1 +artboard-satori theme_adherence: passed, prepared_svg_count=3 +direct-svg theme_validate: passed, page_count=1, theme_count=1 +direct-svg theme_adherence: passed, prepared_svg_count=1 +unknown-color theme_validate: passed +unknown-color theme_adherence: failed as expected with theme_unknown_color +low-contrast theme_validate: passed +low-contrast theme_adherence: failed as expected with contrast_too_low +stale-theme-validate theme_adherence: failed as expected with theme_validate_plan_stale +svglide_theme_validate_test.py + svglide_theme_adherence_test.py: Ran 11 tests, OK +theme_system_p0_fixture_test.py: Ran 4 tests, OK +``` + +Targeted P0 tests: + +```bash +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 -m unittest \ + skills/lark-slides/scripts/svglide_theme_test.py \ + skills/lark-slides/scripts/svglide_theme_validate_test.py \ + skills/lark-slides/scripts/svglide_theme_adherence_test.py \ + skills/lark-slides/scripts/svglide_pre_submit_review_test.py \ + skills/lark-slides/scripts/svglide_project_runner_test.py \ + skills/lark-slides/scripts/svglide_quality_gate_test.py \ + skills/lark-slides/scripts/svglide_theme_system_p0_fixture_test.py +``` + +Result: + +```text +Ran 103 tests in 8.395s +OK +``` + +Full scripts unittest discovery: + +```bash +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 -m unittest discover skills/lark-slides/scripts -p '*_test.py' +``` + +Result: + +```text +Ran 335 tests in 16.636s +OK +``` + +Package check tests: + +```bash +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 -m unittest skills/lark-slides/scripts/svglide_artboard_package_check_test.py +``` + +Result: + +```text +Ran 4 tests in 0.127s +OK +``` + +Go root package test: + +```bash +env GOCACHE=/private/tmp/svglide-gocache go test . +``` + +Result: + +```text +ok github.com/larksuite/cli 0.739s +``` + +Package runtime check: + +```bash +env PYTHONPYCACHEPREFIX=/private/tmp/svglide-pycache \ + python3 skills/lark-slides/scripts/svglide_artboard_package_check.py \ + --output-dir /private/tmp/svglide-theme-system-p0-package-check-final \ + --pretty +``` + +Result: + +```text +status: passed +runtime_check_count: 2 +output receipts: + /private/tmp/svglide-theme-system-p0-package-check-final/06-check/artboard-package-check.json + /private/tmp/svglide-theme-system-p0-package-check-final/receipts/artboard-package-check.json +``` + +## Key Runtime Boundaries + +- `pre_submit_review` is a human receipt validator. It is not an automatic + aesthetics model. +- `theme_adherence` only checks static SVG colors and direct text/background + contrast in final prepared SVG files. +- Satori SVG remains an artboard preview/intermediate artifact; final theme + adherence is checked on `04-svg/prepared/*.svg`. +- `direct_svg` still runs theme checks, but it must not require + `artboard-package-check`. +- `export` is outside Theme System P0. The P0 plan explicitly does not claim + PPTX or narrated deck output. diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/README.md b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/README.md new file mode 100644 index 000000000..7cc2c0f42 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/README.md @@ -0,0 +1,9 @@ +Theme System P0 negative fixtures. + +Each child directory is a project root for a specific failing condition: + +- `unknown-color`: prepared SVG contains a color outside the active ThemeSpec. +- `low-contrast`: prepared SVG uses allowed colors but fails text contrast. +- `stale-theme-validate`: `theme-validate.json` is intentionally stale. + +These fixtures are exercised by `svglide_theme_system_p0_fixture_test.py`. diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/02-plan/slide_plan.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/02-plan/slide_plan.json new file mode 100644 index 000000000..b0787e853 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/02-plan/slide_plan.json @@ -0,0 +1,16 @@ +{ + "generation_mode": "artboard_satori", + "language": "zh-CN", + "theme_id": "dark-clarity", + "slides": [ + { + "page": 1, + "title": "低对比度负例", + "canvas_spec": { + "version": "svglide-canvas-spec/v1", + "template_id": "cover-hero", + "theme_id": "dark-clarity" + } + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/04-svg/prepared/page-001.svg b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/04-svg/prepared/page-001.svg new file mode 100644 index 000000000..b3712da85 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/04-svg/prepared/page-001.svg @@ -0,0 +1 @@ +低对比度负例 diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/06-check/theme-adherence.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/06-check/theme-adherence.json new file mode 100644 index 000000000..9ceb2ab56 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/06-check/theme-adherence.json @@ -0,0 +1,63 @@ +{ + "action": "repair_and_rerun", + "checked_at": "2026-06-21T21:34:08+08:00", + "contrast_failures": [ + { + "min": 4.5, + "page": 1, + "path": "04-svg/prepared/page-001.svg", + "ratio": 1.0064, + "text_index": 0 + } + ], + "contrast_unresolved": [], + "inputs": { + "plan_sha256": "9d54b389938c4a1d52a2ff9ef4cffacf7fc90d4282aee5154ba85d1b78198fc3", + "slide_plan": "02-plan/slide_plan.json", + "theme_validate": "06-check/theme-validate.json", + "theme_validate_sha256": "0d72cbf9f836201ee3e7f8589a2b2fbdd00907e5e89a69640d5060bc98d7c65b" + }, + "issues": [ + { + "code": "contrast_too_low", + "message": "text contrast 1.01 is below 4.5", + "page": 1, + "path": "04-svg/prepared/page-001.svg" + } + ], + "output_path": "06-check/theme-adherence.json", + "pages": [ + { + "issues": [ + { + "code": "contrast_too_low", + "message": "text contrast 1.01 is below 4.5", + "page": 1, + "path": "04-svg/prepared/page-001.svg" + } + ], + "page": 1, + "path": "04-svg/prepared/page-001.svg", + "sha256": "b18e154042df52110f187d53683dbb3faff57944db65a5c218e313b57615d7f6", + "status": "failed", + "theme_id": "dark-clarity" + } + ], + "prepared_files": [ + { + "path": "04-svg/prepared/page-001.svg", + "sha256": "b18e154042df52110f187d53683dbb3faff57944db65a5c218e313b57615d7f6" + } + ], + "schema_version": "svglide-theme-adherence/v1", + "stage": "theme_adherence", + "status": "failed", + "summary": { + "contrast_unresolved_count": 0, + "error_count": 1, + "prepared_svg_count": 1, + "unknown_color_count": 0, + "warning_count": 0 + }, + "unknown_colors": [] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/06-check/theme-validate.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/06-check/theme-validate.json new file mode 100644 index 000000000..6f8940074 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/06-check/theme-validate.json @@ -0,0 +1,39 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T21:34:00+08:00", + "inputs": { + "plan_sha256": "9d54b389938c4a1d52a2ff9ef4cffacf7fc90d4282aee5154ba85d1b78198fc3", + "slide_plan": "02-plan/slide_plan.json", + "template_registry": "skills/lark-slides/references/svglide-template-registry.json", + "template_registry_sha256": "78c6639ee435ff8b8818f64f7b536967a7486281eacab5abce8e20626e20f6b3", + "theme_registry": "skills/lark-slides/scripts/artboard_renderer/themes/registry.json", + "theme_registry_sha256": "62ea0e69b9f5380a11622a6a47f6947943601f4cc3f1bdba1c7f4e45d77db959" + }, + "issues": [], + "output_path": "06-check/theme-validate.json", + "pages": [ + { + "issues": [], + "page": 1, + "status": "passed", + "template_id": "cover-hero", + "theme_id": "dark-clarity", + "theme_sha256": "707e671d244fb10d975a10da1c78692b215e8544bc44eb1bb4c0a55dfaec01c1" + } + ], + "schema_version": "svglide-theme-validate/v1", + "stage": "theme_validate", + "status": "passed", + "summary": { + "error_count": 0, + "page_count": 1, + "theme_count": 1, + "warning_count": 0 + }, + "theme_files": [ + { + "path": "skills/lark-slides/scripts/artboard_renderer/themes/dark-clarity.json", + "sha256": "f47cfdcc5c29d4b694820c097a8cccedc964d072211b579c0a91e3702189ab1b" + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/receipts/theme-adherence.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/receipts/theme-adherence.json new file mode 100644 index 000000000..9ceb2ab56 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/receipts/theme-adherence.json @@ -0,0 +1,63 @@ +{ + "action": "repair_and_rerun", + "checked_at": "2026-06-21T21:34:08+08:00", + "contrast_failures": [ + { + "min": 4.5, + "page": 1, + "path": "04-svg/prepared/page-001.svg", + "ratio": 1.0064, + "text_index": 0 + } + ], + "contrast_unresolved": [], + "inputs": { + "plan_sha256": "9d54b389938c4a1d52a2ff9ef4cffacf7fc90d4282aee5154ba85d1b78198fc3", + "slide_plan": "02-plan/slide_plan.json", + "theme_validate": "06-check/theme-validate.json", + "theme_validate_sha256": "0d72cbf9f836201ee3e7f8589a2b2fbdd00907e5e89a69640d5060bc98d7c65b" + }, + "issues": [ + { + "code": "contrast_too_low", + "message": "text contrast 1.01 is below 4.5", + "page": 1, + "path": "04-svg/prepared/page-001.svg" + } + ], + "output_path": "06-check/theme-adherence.json", + "pages": [ + { + "issues": [ + { + "code": "contrast_too_low", + "message": "text contrast 1.01 is below 4.5", + "page": 1, + "path": "04-svg/prepared/page-001.svg" + } + ], + "page": 1, + "path": "04-svg/prepared/page-001.svg", + "sha256": "b18e154042df52110f187d53683dbb3faff57944db65a5c218e313b57615d7f6", + "status": "failed", + "theme_id": "dark-clarity" + } + ], + "prepared_files": [ + { + "path": "04-svg/prepared/page-001.svg", + "sha256": "b18e154042df52110f187d53683dbb3faff57944db65a5c218e313b57615d7f6" + } + ], + "schema_version": "svglide-theme-adherence/v1", + "stage": "theme_adherence", + "status": "failed", + "summary": { + "contrast_unresolved_count": 0, + "error_count": 1, + "prepared_svg_count": 1, + "unknown_color_count": 0, + "warning_count": 0 + }, + "unknown_colors": [] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/receipts/theme-validate.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/receipts/theme-validate.json new file mode 100644 index 000000000..6f8940074 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/low-contrast/receipts/theme-validate.json @@ -0,0 +1,39 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T21:34:00+08:00", + "inputs": { + "plan_sha256": "9d54b389938c4a1d52a2ff9ef4cffacf7fc90d4282aee5154ba85d1b78198fc3", + "slide_plan": "02-plan/slide_plan.json", + "template_registry": "skills/lark-slides/references/svglide-template-registry.json", + "template_registry_sha256": "78c6639ee435ff8b8818f64f7b536967a7486281eacab5abce8e20626e20f6b3", + "theme_registry": "skills/lark-slides/scripts/artboard_renderer/themes/registry.json", + "theme_registry_sha256": "62ea0e69b9f5380a11622a6a47f6947943601f4cc3f1bdba1c7f4e45d77db959" + }, + "issues": [], + "output_path": "06-check/theme-validate.json", + "pages": [ + { + "issues": [], + "page": 1, + "status": "passed", + "template_id": "cover-hero", + "theme_id": "dark-clarity", + "theme_sha256": "707e671d244fb10d975a10da1c78692b215e8544bc44eb1bb4c0a55dfaec01c1" + } + ], + "schema_version": "svglide-theme-validate/v1", + "stage": "theme_validate", + "status": "passed", + "summary": { + "error_count": 0, + "page_count": 1, + "theme_count": 1, + "warning_count": 0 + }, + "theme_files": [ + { + "path": "skills/lark-slides/scripts/artboard_renderer/themes/dark-clarity.json", + "sha256": "f47cfdcc5c29d4b694820c097a8cccedc964d072211b579c0a91e3702189ab1b" + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/02-plan/slide_plan.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/02-plan/slide_plan.json new file mode 100644 index 000000000..8b17ec71e --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/02-plan/slide_plan.json @@ -0,0 +1,16 @@ +{ + "generation_mode": "artboard_satori", + "language": "zh-CN", + "theme_id": "dark-clarity", + "slides": [ + { + "page": 1, + "title": "stale receipt 负例", + "canvas_spec": { + "version": "svglide-canvas-spec/v1", + "template_id": "cover-hero", + "theme_id": "dark-clarity" + } + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/04-svg/prepared/page-001.svg b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/04-svg/prepared/page-001.svg new file mode 100644 index 000000000..b1604d9cd --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/04-svg/prepared/page-001.svg @@ -0,0 +1 @@ +stale receipt 负例 diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/06-check/theme-adherence.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/06-check/theme-adherence.json new file mode 100644 index 000000000..71dedabc5 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/06-check/theme-adherence.json @@ -0,0 +1,47 @@ +{ + "action": "repair_and_rerun", + "checked_at": "2026-06-21T21:34:11+08:00", + "contrast_failures": [], + "contrast_unresolved": [], + "inputs": { + "plan_sha256": "776295d1edea25ac19696a5b5114ada5cce7a99b2a02ecc0a8a3ac65d697f121", + "slide_plan": "02-plan/slide_plan.json", + "theme_validate": "06-check/theme-validate.json", + "theme_validate_sha256": "bc51b146bf85d911dda444f8b43b955347fdeee6c69b9bb467d5fceff4b1ebe3" + }, + "issues": [ + { + "code": "theme_validate_plan_stale", + "message": "theme_validate plan_sha256 does not match current slide_plan.json", + "path": "06-check/theme-validate.json" + } + ], + "output_path": "06-check/theme-adherence.json", + "pages": [ + { + "issues": [], + "page": 1, + "path": "04-svg/prepared/page-001.svg", + "sha256": "6287b6edeed2bf3219eaa1035a5a922406f47d7d905b2e34ab6f1899901aab92", + "status": "passed", + "theme_id": "dark-clarity" + } + ], + "prepared_files": [ + { + "path": "04-svg/prepared/page-001.svg", + "sha256": "6287b6edeed2bf3219eaa1035a5a922406f47d7d905b2e34ab6f1899901aab92" + } + ], + "schema_version": "svglide-theme-adherence/v1", + "stage": "theme_adherence", + "status": "failed", + "summary": { + "contrast_unresolved_count": 0, + "error_count": 1, + "prepared_svg_count": 1, + "unknown_color_count": 0, + "warning_count": 0 + }, + "unknown_colors": [] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/06-check/theme-validate.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/06-check/theme-validate.json new file mode 100644 index 000000000..a3d31a490 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/06-check/theme-validate.json @@ -0,0 +1,34 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T00:00:00+08:00", + "inputs": { + "plan_sha256": "stale", + "slide_plan": "02-plan/slide_plan.json", + "template_registry": "skills/lark-slides/references/svglide-template-registry.json", + "template_registry_sha256": "stale", + "theme_registry": "skills/lark-slides/scripts/artboard_renderer/themes/registry.json", + "theme_registry_sha256": "stale" + }, + "issues": [], + "output_path": "06-check/theme-validate.json", + "pages": [ + { + "issues": [], + "page": 1, + "status": "passed", + "template_id": "cover-hero", + "theme_id": "dark-clarity", + "theme_sha256": "stale" + } + ], + "schema_version": "svglide-theme-validate/v1", + "stage": "theme_validate", + "status": "passed", + "summary": { + "error_count": 0, + "page_count": 1, + "theme_count": 1, + "warning_count": 0 + }, + "theme_files": [] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/receipts/theme-adherence.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/receipts/theme-adherence.json new file mode 100644 index 000000000..71dedabc5 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/receipts/theme-adherence.json @@ -0,0 +1,47 @@ +{ + "action": "repair_and_rerun", + "checked_at": "2026-06-21T21:34:11+08:00", + "contrast_failures": [], + "contrast_unresolved": [], + "inputs": { + "plan_sha256": "776295d1edea25ac19696a5b5114ada5cce7a99b2a02ecc0a8a3ac65d697f121", + "slide_plan": "02-plan/slide_plan.json", + "theme_validate": "06-check/theme-validate.json", + "theme_validate_sha256": "bc51b146bf85d911dda444f8b43b955347fdeee6c69b9bb467d5fceff4b1ebe3" + }, + "issues": [ + { + "code": "theme_validate_plan_stale", + "message": "theme_validate plan_sha256 does not match current slide_plan.json", + "path": "06-check/theme-validate.json" + } + ], + "output_path": "06-check/theme-adherence.json", + "pages": [ + { + "issues": [], + "page": 1, + "path": "04-svg/prepared/page-001.svg", + "sha256": "6287b6edeed2bf3219eaa1035a5a922406f47d7d905b2e34ab6f1899901aab92", + "status": "passed", + "theme_id": "dark-clarity" + } + ], + "prepared_files": [ + { + "path": "04-svg/prepared/page-001.svg", + "sha256": "6287b6edeed2bf3219eaa1035a5a922406f47d7d905b2e34ab6f1899901aab92" + } + ], + "schema_version": "svglide-theme-adherence/v1", + "stage": "theme_adherence", + "status": "failed", + "summary": { + "contrast_unresolved_count": 0, + "error_count": 1, + "prepared_svg_count": 1, + "unknown_color_count": 0, + "warning_count": 0 + }, + "unknown_colors": [] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/receipts/theme-validate.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/receipts/theme-validate.json new file mode 100644 index 000000000..b8859e7a7 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/stale-theme-validate/receipts/theme-validate.json @@ -0,0 +1,30 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T00:00:00+08:00", + "inputs": { + "plan_sha256": "stale", + "slide_plan": "02-plan/slide_plan.json" + }, + "issues": [], + "output_path": "06-check/theme-validate.json", + "pages": [ + { + "issues": [], + "page": 1, + "status": "passed", + "template_id": "cover-hero", + "theme_id": "dark-clarity", + "theme_sha256": "stale" + } + ], + "schema_version": "svglide-theme-validate/v1", + "stage": "theme_validate", + "status": "passed", + "summary": { + "error_count": 0, + "page_count": 1, + "theme_count": 1, + "warning_count": 0 + }, + "theme_files": [] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/02-plan/slide_plan.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/02-plan/slide_plan.json new file mode 100644 index 000000000..38730b0e0 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/02-plan/slide_plan.json @@ -0,0 +1,16 @@ +{ + "generation_mode": "artboard_satori", + "language": "zh-CN", + "theme_id": "dark-clarity", + "slides": [ + { + "page": 1, + "title": "未知颜色负例", + "canvas_spec": { + "version": "svglide-canvas-spec/v1", + "template_id": "cover-hero", + "theme_id": "dark-clarity" + } + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/04-svg/prepared/page-001.svg b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/04-svg/prepared/page-001.svg new file mode 100644 index 000000000..2a81aef30 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/04-svg/prepared/page-001.svg @@ -0,0 +1 @@ +未知颜色负例 diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/06-check/theme-adherence.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/06-check/theme-adherence.json new file mode 100644 index 000000000..5bb5f42f7 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/06-check/theme-adherence.json @@ -0,0 +1,61 @@ +{ + "action": "repair_and_rerun", + "checked_at": "2026-06-21T21:34:08+08:00", + "contrast_failures": [], + "contrast_unresolved": [], + "inputs": { + "plan_sha256": "37ec82d0f615e7daf9ab498684597ebc93748372a1f8c56daec548ba01a3e425", + "slide_plan": "02-plan/slide_plan.json", + "theme_validate": "06-check/theme-validate.json", + "theme_validate_sha256": "bec8dd0adf58083b70607bfa7d1ddaabd67b53826bf1c15e43c92348bb999d12" + }, + "issues": [ + { + "code": "theme_unknown_color", + "message": "color #123456 is not allowed by theme dark-clarity", + "page": 1, + "path": "04-svg/prepared/page-001.svg" + } + ], + "output_path": "06-check/theme-adherence.json", + "pages": [ + { + "issues": [ + { + "code": "theme_unknown_color", + "message": "color #123456 is not allowed by theme dark-clarity", + "page": 1, + "path": "04-svg/prepared/page-001.svg" + } + ], + "page": 1, + "path": "04-svg/prepared/page-001.svg", + "sha256": "9a86436dad4a18fcb65e7a41a37ed7c6aa6f59bd79ef15f041feaa16675e62b7", + "status": "failed", + "theme_id": "dark-clarity" + } + ], + "prepared_files": [ + { + "path": "04-svg/prepared/page-001.svg", + "sha256": "9a86436dad4a18fcb65e7a41a37ed7c6aa6f59bd79ef15f041feaa16675e62b7" + } + ], + "schema_version": "svglide-theme-adherence/v1", + "stage": "theme_adherence", + "status": "failed", + "summary": { + "contrast_unresolved_count": 0, + "error_count": 1, + "prepared_svg_count": 1, + "unknown_color_count": 1, + "warning_count": 0 + }, + "unknown_colors": [ + { + "color": "#123456", + "page": 1, + "path": "04-svg/prepared/page-001.svg" + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/06-check/theme-validate.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/06-check/theme-validate.json new file mode 100644 index 000000000..079e557b1 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/06-check/theme-validate.json @@ -0,0 +1,39 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T21:34:00+08:00", + "inputs": { + "plan_sha256": "37ec82d0f615e7daf9ab498684597ebc93748372a1f8c56daec548ba01a3e425", + "slide_plan": "02-plan/slide_plan.json", + "template_registry": "skills/lark-slides/references/svglide-template-registry.json", + "template_registry_sha256": "78c6639ee435ff8b8818f64f7b536967a7486281eacab5abce8e20626e20f6b3", + "theme_registry": "skills/lark-slides/scripts/artboard_renderer/themes/registry.json", + "theme_registry_sha256": "62ea0e69b9f5380a11622a6a47f6947943601f4cc3f1bdba1c7f4e45d77db959" + }, + "issues": [], + "output_path": "06-check/theme-validate.json", + "pages": [ + { + "issues": [], + "page": 1, + "status": "passed", + "template_id": "cover-hero", + "theme_id": "dark-clarity", + "theme_sha256": "707e671d244fb10d975a10da1c78692b215e8544bc44eb1bb4c0a55dfaec01c1" + } + ], + "schema_version": "svglide-theme-validate/v1", + "stage": "theme_validate", + "status": "passed", + "summary": { + "error_count": 0, + "page_count": 1, + "theme_count": 1, + "warning_count": 0 + }, + "theme_files": [ + { + "path": "skills/lark-slides/scripts/artboard_renderer/themes/dark-clarity.json", + "sha256": "f47cfdcc5c29d4b694820c097a8cccedc964d072211b579c0a91e3702189ab1b" + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/receipts/theme-adherence.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/receipts/theme-adherence.json new file mode 100644 index 000000000..5bb5f42f7 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/receipts/theme-adherence.json @@ -0,0 +1,61 @@ +{ + "action": "repair_and_rerun", + "checked_at": "2026-06-21T21:34:08+08:00", + "contrast_failures": [], + "contrast_unresolved": [], + "inputs": { + "plan_sha256": "37ec82d0f615e7daf9ab498684597ebc93748372a1f8c56daec548ba01a3e425", + "slide_plan": "02-plan/slide_plan.json", + "theme_validate": "06-check/theme-validate.json", + "theme_validate_sha256": "bec8dd0adf58083b70607bfa7d1ddaabd67b53826bf1c15e43c92348bb999d12" + }, + "issues": [ + { + "code": "theme_unknown_color", + "message": "color #123456 is not allowed by theme dark-clarity", + "page": 1, + "path": "04-svg/prepared/page-001.svg" + } + ], + "output_path": "06-check/theme-adherence.json", + "pages": [ + { + "issues": [ + { + "code": "theme_unknown_color", + "message": "color #123456 is not allowed by theme dark-clarity", + "page": 1, + "path": "04-svg/prepared/page-001.svg" + } + ], + "page": 1, + "path": "04-svg/prepared/page-001.svg", + "sha256": "9a86436dad4a18fcb65e7a41a37ed7c6aa6f59bd79ef15f041feaa16675e62b7", + "status": "failed", + "theme_id": "dark-clarity" + } + ], + "prepared_files": [ + { + "path": "04-svg/prepared/page-001.svg", + "sha256": "9a86436dad4a18fcb65e7a41a37ed7c6aa6f59bd79ef15f041feaa16675e62b7" + } + ], + "schema_version": "svglide-theme-adherence/v1", + "stage": "theme_adherence", + "status": "failed", + "summary": { + "contrast_unresolved_count": 0, + "error_count": 1, + "prepared_svg_count": 1, + "unknown_color_count": 1, + "warning_count": 0 + }, + "unknown_colors": [ + { + "color": "#123456", + "page": 1, + "path": "04-svg/prepared/page-001.svg" + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/receipts/theme-validate.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/receipts/theme-validate.json new file mode 100644 index 000000000..079e557b1 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-invalid/unknown-color/receipts/theme-validate.json @@ -0,0 +1,39 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T21:34:00+08:00", + "inputs": { + "plan_sha256": "37ec82d0f615e7daf9ab498684597ebc93748372a1f8c56daec548ba01a3e425", + "slide_plan": "02-plan/slide_plan.json", + "template_registry": "skills/lark-slides/references/svglide-template-registry.json", + "template_registry_sha256": "78c6639ee435ff8b8818f64f7b536967a7486281eacab5abce8e20626e20f6b3", + "theme_registry": "skills/lark-slides/scripts/artboard_renderer/themes/registry.json", + "theme_registry_sha256": "62ea0e69b9f5380a11622a6a47f6947943601f4cc3f1bdba1c7f4e45d77db959" + }, + "issues": [], + "output_path": "06-check/theme-validate.json", + "pages": [ + { + "issues": [], + "page": 1, + "status": "passed", + "template_id": "cover-hero", + "theme_id": "dark-clarity", + "theme_sha256": "707e671d244fb10d975a10da1c78692b215e8544bc44eb1bb4c0a55dfaec01c1" + } + ], + "schema_version": "svglide-theme-validate/v1", + "stage": "theme_validate", + "status": "passed", + "summary": { + "error_count": 0, + "page_count": 1, + "theme_count": 1, + "warning_count": 0 + }, + "theme_files": [ + { + "path": "skills/lark-slides/scripts/artboard_renderer/themes/dark-clarity.json", + "sha256": "f47cfdcc5c29d4b694820c097a8cccedc964d072211b579c0a91e3702189ab1b" + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/README.md b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/README.md new file mode 100644 index 000000000..f4ce9b3d6 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/README.md @@ -0,0 +1,19 @@ +Theme System P0 positive fixtures. + +Use these project roots to prove that ThemeSpec validation and final SVG +theme adherence can pass without relying on plan-only visual distinctness. + +- `artboard-satori`: three-page cover/content/closing artboard project. +- `direct-svg`: one-page direct SVG project; it validates theme usage without + requiring the artboard package check. + +Recommended smoke commands: + +```bash +python3 skills/lark-slides/scripts/svglide_theme_validate.py \ + skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori \ + --pretty +python3 skills/lark-slides/scripts/svglide_theme_adherence.py \ + skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori \ + --pretty +``` diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/02-plan/slide_plan.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/02-plan/slide_plan.json new file mode 100644 index 000000000..03f6ac63b --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/02-plan/slide_plan.json @@ -0,0 +1,55 @@ +{ + "generation_mode": "artboard_satori", + "language": "zh-CN", + "theme_id": "dark-clarity", + "slides": [ + { + "page": 1, + "page_type": "cover", + "title": "主题系统封面", + "canvas_spec": { + "version": "svglide-canvas-spec/v1", + "template_id": "cover-hero", + "theme_id": "dark-clarity", + "theme": { + "colors": { + "background": "#0F172A", + "text": "#F8FAFC" + } + } + } + }, + { + "page": 2, + "page_type": "content", + "title": "主题系统正文", + "canvas_spec": { + "version": "svglide-canvas-spec/v1", + "template_id": "comparison-cards", + "theme_id": "dark-clarity", + "theme": { + "colors": { + "background": "#0F172A", + "text": "#F8FAFC" + } + } + } + }, + { + "page": 3, + "page_type": "closing", + "title": "主题系统结尾", + "canvas_spec": { + "version": "svglide-canvas-spec/v1", + "template_id": "summary-final", + "theme_id": "dark-clarity", + "theme": { + "colors": { + "background": "#0F172A", + "text": "#F8FAFC" + } + } + } + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/04-svg/prepared/page-001.svg b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/04-svg/prepared/page-001.svg new file mode 100644 index 000000000..af8e9a657 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/04-svg/prepared/page-001.svg @@ -0,0 +1 @@ +主题系统封面final prepared SVG 使用 ThemeSpec 颜色 diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/04-svg/prepared/page-002.svg b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/04-svg/prepared/page-002.svg new file mode 100644 index 000000000..83904b873 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/04-svg/prepared/page-002.svg @@ -0,0 +1 @@ +主题系统正文左侧观点右侧观点 diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/04-svg/prepared/page-003.svg b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/04-svg/prepared/page-003.svg new file mode 100644 index 000000000..043f777f6 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/04-svg/prepared/page-003.svg @@ -0,0 +1 @@ +主题系统结尾ThemeSpec、adherence 与 quality gate 闭环 diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/06-check/theme-adherence.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/06-check/theme-adherence.json new file mode 100644 index 000000000..b48fb4e67 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/06-check/theme-adherence.json @@ -0,0 +1,65 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T21:34:08+08:00", + "contrast_failures": [], + "contrast_unresolved": [], + "inputs": { + "plan_sha256": "4831ac4c6c82d461a671a67561e50fe6111af4c6baba957a2640512c39517afd", + "slide_plan": "02-plan/slide_plan.json", + "theme_validate": "06-check/theme-validate.json", + "theme_validate_sha256": "3a16056b5947ff22a2145592ef6a774c92c7b292272913527a837ec998ab295d" + }, + "issues": [], + "output_path": "06-check/theme-adherence.json", + "pages": [ + { + "issues": [], + "page": 1, + "path": "04-svg/prepared/page-001.svg", + "sha256": "f6f43d6b72f7872fcdc80f7a6130d16a2247f9b0ec2bdbd40fb6e0d2e9a336ef", + "status": "passed", + "theme_id": "dark-clarity" + }, + { + "issues": [], + "page": 2, + "path": "04-svg/prepared/page-002.svg", + "sha256": "fe7e6b41731a60b3cc48a9431640104f55374cf4f4a69548072546e1b51f7301", + "status": "passed", + "theme_id": "dark-clarity" + }, + { + "issues": [], + "page": 3, + "path": "04-svg/prepared/page-003.svg", + "sha256": "a4a09d108e7bfb33ca8104e4261983794e48a8a4b947fcdf5f06c820e6936bf0", + "status": "passed", + "theme_id": "dark-clarity" + } + ], + "prepared_files": [ + { + "path": "04-svg/prepared/page-001.svg", + "sha256": "f6f43d6b72f7872fcdc80f7a6130d16a2247f9b0ec2bdbd40fb6e0d2e9a336ef" + }, + { + "path": "04-svg/prepared/page-002.svg", + "sha256": "fe7e6b41731a60b3cc48a9431640104f55374cf4f4a69548072546e1b51f7301" + }, + { + "path": "04-svg/prepared/page-003.svg", + "sha256": "a4a09d108e7bfb33ca8104e4261983794e48a8a4b947fcdf5f06c820e6936bf0" + } + ], + "schema_version": "svglide-theme-adherence/v1", + "stage": "theme_adherence", + "status": "passed", + "summary": { + "contrast_unresolved_count": 0, + "error_count": 0, + "prepared_svg_count": 3, + "unknown_color_count": 0, + "warning_count": 0 + }, + "unknown_colors": [] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/06-check/theme-validate.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/06-check/theme-validate.json new file mode 100644 index 000000000..a306b538a --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/06-check/theme-validate.json @@ -0,0 +1,55 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T21:34:00+08:00", + "inputs": { + "plan_sha256": "4831ac4c6c82d461a671a67561e50fe6111af4c6baba957a2640512c39517afd", + "slide_plan": "02-plan/slide_plan.json", + "template_registry": "skills/lark-slides/references/svglide-template-registry.json", + "template_registry_sha256": "78c6639ee435ff8b8818f64f7b536967a7486281eacab5abce8e20626e20f6b3", + "theme_registry": "skills/lark-slides/scripts/artboard_renderer/themes/registry.json", + "theme_registry_sha256": "62ea0e69b9f5380a11622a6a47f6947943601f4cc3f1bdba1c7f4e45d77db959" + }, + "issues": [], + "output_path": "06-check/theme-validate.json", + "pages": [ + { + "issues": [], + "page": 1, + "status": "passed", + "template_id": "cover-hero", + "theme_id": "dark-clarity", + "theme_sha256": "707e671d244fb10d975a10da1c78692b215e8544bc44eb1bb4c0a55dfaec01c1" + }, + { + "issues": [], + "page": 2, + "status": "passed", + "template_id": "comparison-cards", + "theme_id": "dark-clarity", + "theme_sha256": "707e671d244fb10d975a10da1c78692b215e8544bc44eb1bb4c0a55dfaec01c1" + }, + { + "issues": [], + "page": 3, + "status": "passed", + "template_id": "summary-final", + "theme_id": "dark-clarity", + "theme_sha256": "707e671d244fb10d975a10da1c78692b215e8544bc44eb1bb4c0a55dfaec01c1" + } + ], + "schema_version": "svglide-theme-validate/v1", + "stage": "theme_validate", + "status": "passed", + "summary": { + "error_count": 0, + "page_count": 3, + "theme_count": 1, + "warning_count": 0 + }, + "theme_files": [ + { + "path": "skills/lark-slides/scripts/artboard_renderer/themes/dark-clarity.json", + "sha256": "f47cfdcc5c29d4b694820c097a8cccedc964d072211b579c0a91e3702189ab1b" + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/receipts/theme-adherence.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/receipts/theme-adherence.json new file mode 100644 index 000000000..b48fb4e67 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/receipts/theme-adherence.json @@ -0,0 +1,65 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T21:34:08+08:00", + "contrast_failures": [], + "contrast_unresolved": [], + "inputs": { + "plan_sha256": "4831ac4c6c82d461a671a67561e50fe6111af4c6baba957a2640512c39517afd", + "slide_plan": "02-plan/slide_plan.json", + "theme_validate": "06-check/theme-validate.json", + "theme_validate_sha256": "3a16056b5947ff22a2145592ef6a774c92c7b292272913527a837ec998ab295d" + }, + "issues": [], + "output_path": "06-check/theme-adherence.json", + "pages": [ + { + "issues": [], + "page": 1, + "path": "04-svg/prepared/page-001.svg", + "sha256": "f6f43d6b72f7872fcdc80f7a6130d16a2247f9b0ec2bdbd40fb6e0d2e9a336ef", + "status": "passed", + "theme_id": "dark-clarity" + }, + { + "issues": [], + "page": 2, + "path": "04-svg/prepared/page-002.svg", + "sha256": "fe7e6b41731a60b3cc48a9431640104f55374cf4f4a69548072546e1b51f7301", + "status": "passed", + "theme_id": "dark-clarity" + }, + { + "issues": [], + "page": 3, + "path": "04-svg/prepared/page-003.svg", + "sha256": "a4a09d108e7bfb33ca8104e4261983794e48a8a4b947fcdf5f06c820e6936bf0", + "status": "passed", + "theme_id": "dark-clarity" + } + ], + "prepared_files": [ + { + "path": "04-svg/prepared/page-001.svg", + "sha256": "f6f43d6b72f7872fcdc80f7a6130d16a2247f9b0ec2bdbd40fb6e0d2e9a336ef" + }, + { + "path": "04-svg/prepared/page-002.svg", + "sha256": "fe7e6b41731a60b3cc48a9431640104f55374cf4f4a69548072546e1b51f7301" + }, + { + "path": "04-svg/prepared/page-003.svg", + "sha256": "a4a09d108e7bfb33ca8104e4261983794e48a8a4b947fcdf5f06c820e6936bf0" + } + ], + "schema_version": "svglide-theme-adherence/v1", + "stage": "theme_adherence", + "status": "passed", + "summary": { + "contrast_unresolved_count": 0, + "error_count": 0, + "prepared_svg_count": 3, + "unknown_color_count": 0, + "warning_count": 0 + }, + "unknown_colors": [] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/receipts/theme-validate.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/receipts/theme-validate.json new file mode 100644 index 000000000..a306b538a --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/artboard-satori/receipts/theme-validate.json @@ -0,0 +1,55 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T21:34:00+08:00", + "inputs": { + "plan_sha256": "4831ac4c6c82d461a671a67561e50fe6111af4c6baba957a2640512c39517afd", + "slide_plan": "02-plan/slide_plan.json", + "template_registry": "skills/lark-slides/references/svglide-template-registry.json", + "template_registry_sha256": "78c6639ee435ff8b8818f64f7b536967a7486281eacab5abce8e20626e20f6b3", + "theme_registry": "skills/lark-slides/scripts/artboard_renderer/themes/registry.json", + "theme_registry_sha256": "62ea0e69b9f5380a11622a6a47f6947943601f4cc3f1bdba1c7f4e45d77db959" + }, + "issues": [], + "output_path": "06-check/theme-validate.json", + "pages": [ + { + "issues": [], + "page": 1, + "status": "passed", + "template_id": "cover-hero", + "theme_id": "dark-clarity", + "theme_sha256": "707e671d244fb10d975a10da1c78692b215e8544bc44eb1bb4c0a55dfaec01c1" + }, + { + "issues": [], + "page": 2, + "status": "passed", + "template_id": "comparison-cards", + "theme_id": "dark-clarity", + "theme_sha256": "707e671d244fb10d975a10da1c78692b215e8544bc44eb1bb4c0a55dfaec01c1" + }, + { + "issues": [], + "page": 3, + "status": "passed", + "template_id": "summary-final", + "theme_id": "dark-clarity", + "theme_sha256": "707e671d244fb10d975a10da1c78692b215e8544bc44eb1bb4c0a55dfaec01c1" + } + ], + "schema_version": "svglide-theme-validate/v1", + "stage": "theme_validate", + "status": "passed", + "summary": { + "error_count": 0, + "page_count": 3, + "theme_count": 1, + "warning_count": 0 + }, + "theme_files": [ + { + "path": "skills/lark-slides/scripts/artboard_renderer/themes/dark-clarity.json", + "sha256": "f47cfdcc5c29d4b694820c097a8cccedc964d072211b579c0a91e3702189ab1b" + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/02-plan/slide_plan.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/02-plan/slide_plan.json new file mode 100644 index 000000000..e79a7d779 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/02-plan/slide_plan.json @@ -0,0 +1,12 @@ +{ + "generation_mode": "direct_svg", + "language": "zh-CN", + "theme_id": "dark-clarity", + "slides": [ + { + "page": 1, + "page_type": "content", + "title": "Direct SVG 主题检查" + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/04-svg/prepared/page-001.svg b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/04-svg/prepared/page-001.svg new file mode 100644 index 000000000..b170cef4f --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/04-svg/prepared/page-001.svg @@ -0,0 +1 @@ +Direct SVG 主题检查direct_svg 不依赖 artboard package check diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/06-check/theme-adherence.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/06-check/theme-adherence.json new file mode 100644 index 000000000..f8f5aadbb --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/06-check/theme-adherence.json @@ -0,0 +1,41 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T21:34:08+08:00", + "contrast_failures": [], + "contrast_unresolved": [], + "inputs": { + "plan_sha256": "b17941fe2a32a4fb4b5dce9404baac08d14183ffe2ae2a9b2ff07d6b3bb0c10a", + "slide_plan": "02-plan/slide_plan.json", + "theme_validate": "06-check/theme-validate.json", + "theme_validate_sha256": "dc0b34dfe8f2e2d82f2c3b9053e559b67225736e6a7806fef603b58086242eb9" + }, + "issues": [], + "output_path": "06-check/theme-adherence.json", + "pages": [ + { + "issues": [], + "page": 1, + "path": "04-svg/prepared/page-001.svg", + "sha256": "e1447907a30468942c2605b6de2a0571dd6503102e3f49fe9a53958bbabd11c8", + "status": "passed", + "theme_id": "dark-clarity" + } + ], + "prepared_files": [ + { + "path": "04-svg/prepared/page-001.svg", + "sha256": "e1447907a30468942c2605b6de2a0571dd6503102e3f49fe9a53958bbabd11c8" + } + ], + "schema_version": "svglide-theme-adherence/v1", + "stage": "theme_adherence", + "status": "passed", + "summary": { + "contrast_unresolved_count": 0, + "error_count": 0, + "prepared_svg_count": 1, + "unknown_color_count": 0, + "warning_count": 0 + }, + "unknown_colors": [] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/06-check/theme-validate.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/06-check/theme-validate.json new file mode 100644 index 000000000..4cbc84359 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/06-check/theme-validate.json @@ -0,0 +1,39 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T21:34:00+08:00", + "inputs": { + "plan_sha256": "b17941fe2a32a4fb4b5dce9404baac08d14183ffe2ae2a9b2ff07d6b3bb0c10a", + "slide_plan": "02-plan/slide_plan.json", + "template_registry": "skills/lark-slides/references/svglide-template-registry.json", + "template_registry_sha256": "78c6639ee435ff8b8818f64f7b536967a7486281eacab5abce8e20626e20f6b3", + "theme_registry": "skills/lark-slides/scripts/artboard_renderer/themes/registry.json", + "theme_registry_sha256": "62ea0e69b9f5380a11622a6a47f6947943601f4cc3f1bdba1c7f4e45d77db959" + }, + "issues": [], + "output_path": "06-check/theme-validate.json", + "pages": [ + { + "issues": [], + "page": 1, + "status": "passed", + "template_id": null, + "theme_id": "dark-clarity", + "theme_sha256": "707e671d244fb10d975a10da1c78692b215e8544bc44eb1bb4c0a55dfaec01c1" + } + ], + "schema_version": "svglide-theme-validate/v1", + "stage": "theme_validate", + "status": "passed", + "summary": { + "error_count": 0, + "page_count": 1, + "theme_count": 1, + "warning_count": 0 + }, + "theme_files": [ + { + "path": "skills/lark-slides/scripts/artboard_renderer/themes/dark-clarity.json", + "sha256": "f47cfdcc5c29d4b694820c097a8cccedc964d072211b579c0a91e3702189ab1b" + } + ] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/receipts/theme-adherence.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/receipts/theme-adherence.json new file mode 100644 index 000000000..f8f5aadbb --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/receipts/theme-adherence.json @@ -0,0 +1,41 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T21:34:08+08:00", + "contrast_failures": [], + "contrast_unresolved": [], + "inputs": { + "plan_sha256": "b17941fe2a32a4fb4b5dce9404baac08d14183ffe2ae2a9b2ff07d6b3bb0c10a", + "slide_plan": "02-plan/slide_plan.json", + "theme_validate": "06-check/theme-validate.json", + "theme_validate_sha256": "dc0b34dfe8f2e2d82f2c3b9053e559b67225736e6a7806fef603b58086242eb9" + }, + "issues": [], + "output_path": "06-check/theme-adherence.json", + "pages": [ + { + "issues": [], + "page": 1, + "path": "04-svg/prepared/page-001.svg", + "sha256": "e1447907a30468942c2605b6de2a0571dd6503102e3f49fe9a53958bbabd11c8", + "status": "passed", + "theme_id": "dark-clarity" + } + ], + "prepared_files": [ + { + "path": "04-svg/prepared/page-001.svg", + "sha256": "e1447907a30468942c2605b6de2a0571dd6503102e3f49fe9a53958bbabd11c8" + } + ], + "schema_version": "svglide-theme-adherence/v1", + "stage": "theme_adherence", + "status": "passed", + "summary": { + "contrast_unresolved_count": 0, + "error_count": 0, + "prepared_svg_count": 1, + "unknown_color_count": 0, + "warning_count": 0 + }, + "unknown_colors": [] +} diff --git a/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/receipts/theme-validate.json b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/receipts/theme-validate.json new file mode 100644 index 000000000..4cbc84359 --- /dev/null +++ b/skills/lark-slides/scripts/fixtures/svglide_artboard/theme-system-p0/direct-svg/receipts/theme-validate.json @@ -0,0 +1,39 @@ +{ + "action": "create_live", + "checked_at": "2026-06-21T21:34:00+08:00", + "inputs": { + "plan_sha256": "b17941fe2a32a4fb4b5dce9404baac08d14183ffe2ae2a9b2ff07d6b3bb0c10a", + "slide_plan": "02-plan/slide_plan.json", + "template_registry": "skills/lark-slides/references/svglide-template-registry.json", + "template_registry_sha256": "78c6639ee435ff8b8818f64f7b536967a7486281eacab5abce8e20626e20f6b3", + "theme_registry": "skills/lark-slides/scripts/artboard_renderer/themes/registry.json", + "theme_registry_sha256": "62ea0e69b9f5380a11622a6a47f6947943601f4cc3f1bdba1c7f4e45d77db959" + }, + "issues": [], + "output_path": "06-check/theme-validate.json", + "pages": [ + { + "issues": [], + "page": 1, + "status": "passed", + "template_id": null, + "theme_id": "dark-clarity", + "theme_sha256": "707e671d244fb10d975a10da1c78692b215e8544bc44eb1bb4c0a55dfaec01c1" + } + ], + "schema_version": "svglide-theme-validate/v1", + "stage": "theme_validate", + "status": "passed", + "summary": { + "error_count": 0, + "page_count": 1, + "theme_count": 1, + "warning_count": 0 + }, + "theme_files": [ + { + "path": "skills/lark-slides/scripts/artboard_renderer/themes/dark-clarity.json", + "sha256": "f47cfdcc5c29d4b694820c097a8cccedc964d072211b579c0a91e3702189ab1b" + } + ] +} diff --git a/skills/lark-slides/scripts/svglide_pre_submit_review.py b/skills/lark-slides/scripts/svglide_pre_submit_review.py index e6c6107d4..6454b8bd1 100644 --- a/skills/lark-slides/scripts/svglide_pre_submit_review.py +++ b/skills/lark-slides/scripts/svglide_pre_submit_review.py @@ -151,6 +151,33 @@ def sorted_hash_records(records: list[dict[str, str]]) -> list[dict[str, str]]: return sorted(records, key=lambda item: item["path"]) +def normalize_reviewed_artifacts(value: Any) -> dict[str, Any] | None: + if isinstance(value, dict): + return value + if not isinstance(value, list): + return None + normalized: dict[str, Any] = {} + prepared: list[dict[str, str]] = [] + for item in value: + if not isinstance(item, dict): + return None + kind = item.get("kind") + path = item.get("path") + sha = item.get("sha256") + if not isinstance(kind, str) or not isinstance(path, str) or not isinstance(sha, str): + return None + record = {"path": path, "sha256": sha} + if kind == "prepared_svg": + prepared.append(record) + elif kind in ARTIFACT_PATHS: + normalized[kind] = record + else: + normalized[kind] = record + if prepared: + normalized["prepared_svg"] = prepared + return normalized + + def validate_hash_value( issues: list[dict[str, str]], *, @@ -386,7 +413,7 @@ def validate_reviewed_artifacts( project: Path, current_prepared: list[dict[str, str]], ) -> list[dict[str, Any]]: - reviewed = human.get("reviewed_artifacts") if isinstance(human.get("reviewed_artifacts"), dict) else {} + reviewed = normalize_reviewed_artifacts(human.get("reviewed_artifacts")) evidence: list[dict[str, Any]] = [] if not reviewed: issues.append(issue("human_review", "reviewed_artifacts_missing", "human review must include reviewed_artifacts", category="missing_input")) @@ -446,7 +473,7 @@ def validate_human_bindings( elif recorded_prepared != sorted_hash_records(current_prepared): issues.append(issue("human_review", "human_prepared_files_stale", "human review prepared_files do not match current prepared SVG files", category="stale_hash")) - reviewed_artifacts = human.get("reviewed_artifacts") if isinstance(human.get("reviewed_artifacts"), dict) else {} + reviewed_artifacts = normalize_reviewed_artifacts(human.get("reviewed_artifacts")) or {} quality_gate_review = reviewed_artifacts.get("quality_gate") if isinstance(reviewed_artifacts, dict) else None if isinstance(quality_gate_review, dict) and quality_gate_review.get("sha256") != human.get("quality_gate_sha256"): issues.append(issue("human_review", "human_quality_gate_hash_disagrees", "quality_gate_sha256 must match reviewed_artifacts.quality_gate.sha256", category="stale_hash")) diff --git a/skills/lark-slides/scripts/svglide_pre_submit_review_test.py b/skills/lark-slides/scripts/svglide_pre_submit_review_test.py index ee169bd1a..9436d0b2d 100644 --- a/skills/lark-slides/scripts/svglide_pre_submit_review_test.py +++ b/skills/lark-slides/scripts/svglide_pre_submit_review_test.py @@ -162,6 +162,25 @@ def human_review_payload(project: Path) -> dict[str, object]: } +def human_review_payload_with_artifact_array(project: Path) -> dict[str, object]: + payload = human_review_payload(project) + reviewed = payload["reviewed_artifacts"] + assert isinstance(reviewed, dict) + current_prepared = reviewed["prepared_svg"] + assert isinstance(current_prepared, list) + artifacts: list[dict[str, str]] = [ + {"kind": "prepared_svg", "path": item["path"], "sha256": item["sha256"]} + for item in current_prepared + if isinstance(item, dict) and isinstance(item.get("path"), str) and isinstance(item.get("sha256"), str) + ] + for kind in ("contact_sheet", "preview", "preview_manifest", "quality_gate"): + item = reviewed[kind] + assert isinstance(item, dict) + artifacts.append({"kind": kind, "path": item["path"], "sha256": item["sha256"]}) + payload["reviewed_artifacts"] = artifacts + return payload + + def write_human_review(project: Path, payload: dict[str, object] | None = None) -> Path: path = project / "06-check/pre-submit-human-review.json" write_json(path, payload or human_review_payload(project)) @@ -187,6 +206,16 @@ class PreSubmitReviewTest(unittest.TestCase): self.assertEqual(check["status"], "passed") self.assertEqual(check, receipt) + def test_document_array_reviewed_artifacts_contract_passes(self) -> None: + with tempfile.TemporaryDirectory() as tmp: + project = make_project(Path(tmp)) + write_human_review(project, human_review_payload_with_artifact_array(project)) + + result = pre_submit_review.run_pre_submit_review(project, project / "06-check/pre-submit-human-review.json") + + self.assertEqual(result["status"], "passed", result["issues"]) + self.assertEqual(result["issues"], []) + def test_missing_human_file_writes_failed_receipt(self) -> None: with tempfile.TemporaryDirectory() as tmp: project = make_project(Path(tmp)) diff --git a/skills/lark-slides/scripts/svglide_project_runner.py b/skills/lark-slides/scripts/svglide_project_runner.py index 09cbd12bf..39ebf0f93 100644 --- a/skills/lark-slides/scripts/svglide_project_runner.py +++ b/skills/lark-slides/scripts/svglide_project_runner.py @@ -1808,12 +1808,12 @@ def run_implemented_stage(project_root: Path, stage: str, state: dict[str, Any], (SCRIPT_DIR / "svglide_pre_submit_review.py").as_posix(), project_root.as_posix(), "--human-review", - (project_root / "06-check" / "pre-submit-review.input.json").as_posix(), + (project_root / "06-check" / "pre-submit-human-review.json").as_posix(), "--pretty", ], output_json=project_root / "06-check" / "pre-submit-review.json", inputs=[ - "06-check/pre-submit-review.input.json", + "06-check/pre-submit-human-review.json", "06-check/quality-gate.json", "06-check/theme-adherence.json", "06-check/visual-distinctness.json", diff --git a/skills/lark-slides/scripts/svglide_project_runner_test.py b/skills/lark-slides/scripts/svglide_project_runner_test.py index 2b51419ff..3f1aca285 100644 --- a/skills/lark-slides/scripts/svglide_project_runner_test.py +++ b/skills/lark-slides/scripts/svglide_project_runner_test.py @@ -528,6 +528,53 @@ class SVGlideProjectRunnerTest(unittest.TestCase): self.assertLess(called_stages.index("pre_submit_review"), called_stages.index("live_create")) self.assertTrue(all(profile == "production_live" for _, profile in calls)) + def test_pre_submit_review_uses_documented_human_review_path(self) -> None: + with tempfile.TemporaryDirectory() as tmpdir: + plan_root = Path(tmpdir) / ".lark-slides/plan" + result = runner.init_project("smoke", "Smoke", plan_root=plan_root) + project_root = Path(result["project_root"]) + state = runner.load_state(project_root) + state.setdefault("stages", {})["ppe_proof"] = {"status": "passed"} + captured: dict[str, object] = {} + original_run_script_stage = runner.run_script_stage + + def fake_run_script_stage( + project_root: Path, + state: dict[str, object], + stage: str, + command: list[str], + *, + output_json: Path | None = None, + inputs: list[str] | None = None, + outputs: list[str] | None = None, + command_runner=runner.subprocess.run, + ) -> dict[str, object]: + captured["command"] = command + captured["inputs"] = inputs + return runner.complete_stage( + project_root, + state, + stage, + "passed", + started_at=runner.now_iso(), + inputs=inputs, + outputs=outputs, + command=command, + ) + + try: + runner.run_script_stage = fake_run_script_stage + runner.run_implemented_stage(project_root, "pre_submit_review", state, profile="production_live") + finally: + runner.run_script_stage = original_run_script_stage + + command = captured["command"] + inputs = captured["inputs"] + self.assertIsInstance(command, list) + self.assertIsInstance(inputs, list) + self.assertIn((project_root / "06-check/pre-submit-human-review.json").as_posix(), command) + self.assertIn("06-check/pre-submit-human-review.json", inputs) + def test_init_creates_project_directories_manifest_and_state(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: plan_root = Path(tmpdir) / ".lark-slides/plan" diff --git a/skills/lark-slides/scripts/svglide_theme_system_p0_fixture_test.py b/skills/lark-slides/scripts/svglide_theme_system_p0_fixture_test.py new file mode 100644 index 000000000..0c042daa8 --- /dev/null +++ b/skills/lark-slides/scripts/svglide_theme_system_p0_fixture_test.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +# Copyright (c) 2026 Lark Technologies Pte. Ltd. +# SPDX-License-Identifier: MIT +from __future__ import annotations + +import shutil +import sys +import tempfile +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +import svglide_theme_adherence +import svglide_theme_validate + + +SCRIPT_DIR = Path(__file__).resolve().parent +FIXTURE_ROOT = SCRIPT_DIR / "fixtures" / "svglide_artboard" +EVIDENCE_PATH = SCRIPT_DIR.parent / "references" / "svglide-theme-system-p0-evidence.md" + + +def copy_fixture(relative: str, tmpdir: str) -> Path: + source = FIXTURE_ROOT / relative + target = Path(tmpdir) / relative.replace("/", "-") + shutil.copytree(source, target) + return target + + +def issue_codes(payload: dict[str, object]) -> set[str]: + issues = payload.get("issues") + if not isinstance(issues, list): + return set() + return {item.get("code") for item in issues if isinstance(item, dict) and isinstance(item.get("code"), str)} + + +class SVGlideThemeSystemP0FixtureTest(unittest.TestCase): + def test_evidence_file_exists_and_keeps_p0_scope(self) -> None: + self.assertTrue(EVIDENCE_PATH.exists(), f"missing evidence file: {EVIDENCE_PATH}") + text = EVIDENCE_PATH.read_text(encoding="utf-8") + for required in ["P0-0", "P0-8", "theme_validate", "theme_adherence", "pre_submit_review", "direct_svg"]: + self.assertIn(required, text) + for forbidden in ["自动审美已通过", "已支持完整主题系统", "PPTX export 已完成"]: + self.assertNotIn(forbidden, text) + + def test_positive_artboard_fixture_validates_theme_and_final_svg(self) -> None: + with tempfile.TemporaryDirectory() as tmpdir: + project = copy_fixture("theme-system-p0/artboard-satori", tmpdir) + + validation = svglide_theme_validate.validate_project(project) + svglide_theme_validate.write_outputs(project, validation) + adherence = svglide_theme_adherence.validate_project(project) + + self.assertEqual(validation["status"], "passed", validation["issues"]) + self.assertEqual(adherence["status"], "passed", adherence["issues"]) + self.assertEqual(adherence["summary"]["prepared_svg_count"], 3) + + def test_positive_direct_svg_fixture_validates_without_canvas_template(self) -> None: + with tempfile.TemporaryDirectory() as tmpdir: + project = copy_fixture("theme-system-p0/direct-svg", tmpdir) + + validation = svglide_theme_validate.validate_project(project) + svglide_theme_validate.write_outputs(project, validation) + adherence = svglide_theme_adherence.validate_project(project) + + self.assertEqual(validation["status"], "passed", validation["issues"]) + self.assertEqual(adherence["status"], "passed", adherence["issues"]) + + def test_negative_fixtures_fail_for_unknown_color_low_contrast_and_stale_receipt(self) -> None: + with tempfile.TemporaryDirectory() as tmpdir: + unknown = copy_fixture("theme-system-invalid/unknown-color", tmpdir) + validation = svglide_theme_validate.validate_project(unknown) + svglide_theme_validate.write_outputs(unknown, validation) + unknown_result = svglide_theme_adherence.validate_project(unknown) + + low_contrast = copy_fixture("theme-system-invalid/low-contrast", tmpdir) + validation = svglide_theme_validate.validate_project(low_contrast) + svglide_theme_validate.write_outputs(low_contrast, validation) + low_contrast_result = svglide_theme_adherence.validate_project(low_contrast) + + stale = copy_fixture("theme-system-invalid/stale-theme-validate", tmpdir) + stale_result = svglide_theme_adherence.validate_project(stale) + + self.assertEqual(unknown_result["status"], "failed") + self.assertIn("theme_unknown_color", issue_codes(unknown_result)) + self.assertEqual(low_contrast_result["status"], "failed") + self.assertIn("contrast_too_low", issue_codes(low_contrast_result)) + self.assertEqual(stale_result["status"], "failed") + self.assertIn("theme_validate_plan_stale", issue_codes(stale_result)) + + +if __name__ == "__main__": + unittest.main()