* fix(commands): stop hardcoding the visual-type count Integrates the final reviewed diff from PR #112. * docs(high-level): correct effective width checklist Integrates the final reviewed diff from PR #105. * fix(commands): quote profile argument hint Integrates the final reviewed diff from PR #126. * test(sankey): gate conservation and ribbon geometry Integrates the final reviewed diff from PR #103. * feat(tooling): add doctor diagnostics gate Integrates the final reviewed diff from PR #132. * feat(scripts): lint diagrams as rendered Integrates the final reviewed diff from PR #18. * feat(types): add quantitative polar chart Integrates the final reviewed diff from PR #101. * chore(release): consolidate reviewed changes in 2.6.0 Documents every combined gate and synchronizes Claude, Codex, and Factory metadata for the 39-type release. * fix(doctor): support installed-skill projects --------- Co-authored-by: mysoniw <soniw1@gmail.com> Co-authored-by: thang <76550342+thangldw@users.noreply.github.com> Co-authored-by: YinShaokun <yinshaokun@didiglobal.com> Co-authored-by: 0xDarkMatter <0xDarkMatter@users.noreply.github.com> Co-authored-by: Rupam Pal <rupam.pal@euromonitor.com> Co-authored-by: Yuvraj3905 <yuvraj202001@gmail.com>
105 lines
3.7 KiB
Python
105 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Fail when a canonical example or committed screenshot drifts from its manifest."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import sys
|
|
|
|
from screenshot_catalog import (
|
|
MANIFEST,
|
|
ROOT,
|
|
canonical_slugs,
|
|
png_dimensions,
|
|
screenshot_path,
|
|
sha256,
|
|
source_path,
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
errors: list[str] = []
|
|
slugs = canonical_slugs()
|
|
if len(slugs) != 39 or len(slugs) != len(set(slugs)):
|
|
errors.append(f"expected 39 unique canonical types; found {len(slugs)}")
|
|
|
|
if not MANIFEST.is_file():
|
|
errors.append("docs/screenshots/manifest.json is missing")
|
|
payload: dict[str, object] = {}
|
|
else:
|
|
try:
|
|
payload = json.loads(MANIFEST.read_text(encoding="utf-8"))
|
|
except (json.JSONDecodeError, OSError) as exc:
|
|
errors.append(f"cannot read screenshot manifest: {exc}")
|
|
payload = {}
|
|
|
|
if payload.get("schema_version") != 1:
|
|
errors.append("screenshot manifest schema_version must be 1")
|
|
renderer = payload.get("renderer")
|
|
expected_renderer = {
|
|
"engine": "playwright-chromium",
|
|
"scale": 2.0,
|
|
"viewport": [1440, 1000],
|
|
"capture": "first-svg",
|
|
"font_gate": "document.fonts.ready",
|
|
}
|
|
if renderer != expected_renderer:
|
|
errors.append("screenshot manifest must use the canonical 1440x1000, 2x SVG renderer")
|
|
|
|
entries = payload.get("entries", [])
|
|
if not isinstance(entries, list):
|
|
errors.append("screenshot manifest entries must be a list")
|
|
entries = []
|
|
manifest_slugs = [entry.get("slug") for entry in entries if isinstance(entry, dict)]
|
|
if manifest_slugs != slugs:
|
|
errors.append("screenshot manifest order does not match SKILL.md's canonical type order")
|
|
|
|
by_slug = {
|
|
entry.get("slug"): entry
|
|
for entry in entries
|
|
if isinstance(entry, dict) and isinstance(entry.get("slug"), str)
|
|
}
|
|
for slug in slugs:
|
|
source = source_path(slug)
|
|
screenshot = screenshot_path(slug)
|
|
entry = by_slug.get(slug)
|
|
if not source.is_file():
|
|
errors.append(f"missing canonical example: {source.relative_to(ROOT)}")
|
|
continue
|
|
if not screenshot.is_file():
|
|
errors.append(f"missing canonical screenshot: {screenshot.relative_to(ROOT)}")
|
|
continue
|
|
if not entry:
|
|
errors.append(f"manifest has no entry for {slug}")
|
|
continue
|
|
|
|
expected_source = source.relative_to(ROOT).as_posix()
|
|
expected_screenshot = screenshot.relative_to(ROOT).as_posix()
|
|
if entry.get("source") != expected_source:
|
|
errors.append(f"{slug}: manifest source must be {expected_source}")
|
|
if entry.get("screenshot") != expected_screenshot:
|
|
errors.append(f"{slug}: manifest screenshot must be {expected_screenshot}")
|
|
if entry.get("source_sha256") != sha256(source):
|
|
errors.append(f"{slug}: source changed; rerun scripts/render-canonical-screenshots.py")
|
|
if entry.get("screenshot_sha256") != sha256(screenshot):
|
|
errors.append(f"{slug}: screenshot changed without a matching manifest refresh")
|
|
try:
|
|
width, height = png_dimensions(screenshot)
|
|
except ValueError as exc:
|
|
errors.append(str(exc))
|
|
else:
|
|
if [entry.get("width"), entry.get("height")] != [width, height]:
|
|
errors.append(f"{slug}: manifest dimensions do not match the PNG")
|
|
|
|
if errors:
|
|
print("FAIL screenshot freshness")
|
|
for error in errors:
|
|
print(f" - {error}")
|
|
return 1
|
|
print("OK screenshot freshness: 39 canonical sources and PNG digests match")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|