CI / Plugin Package & Version Gate (push) Failing after 52s
CI / Python 3.9 Compatibility (push) Successful in 13s
CI / Lint & Verify (ubuntu-latest, Python 3.11) (push) Successful in 29s
Deploy gallery to GitHub Pages / build (push) Canceled after 0s
Deploy gallery to GitHub Pages / deploy (push) Canceled after 0s
CI / Lint & Verify (ubuntu-latest, Python 3.12) (push) Successful in 2m56s
CI / Lint & Verify (macos-latest, Python 3.11) (push) Canceled after 0s
CI / Lint & Verify (macos-latest, Python 3.12) (push) Canceled after 0s
CI / Lint & Verify (windows-latest, Python 3.11) (push) Canceled after 0s
CI / Lint & Verify (windows-latest, Python 3.12) (push) Canceled after 0s
* Add adversarial tests for verify-semantic-motion.py and verify-sequence-oauth.py Every other verify-*.py in scripts/ has a matching test-verify-*.py (verify-docs-sync, verify-geometry, verify-motion, verify-plugin-package), and CONTRIBUTING.md lists both verify-semantic-motion.py and verify-sequence-oauth.py as required PR gates, but neither had a dedicated test file exercising its own pass/fail behavior. - test-verify-semantic-motion.py: asserts the shipped SKILL.md / semantic-patterns.md / animation.md and the policy-trace animated example pass verify_markdown()/verify_example(), plus adversarial cases for a missing semantic-pattern router link, a missing pattern name, and a duplicate HTML/SVG id in the example. - test-verify-sequence-oauth.py: CLI smoke-tests the shipped repo (expects ALL GATES PASSED) and an adversarial case that removes the dashed-return marker from the OAuth 401 response in a temporary copy of the repo tree (verify-sequence-oauth.py resolves paths from its own __file__, so the adversarial run executes the copied script against the copied tree). Also wires both new tests into CONTRIBUTING.md's gate table and combined validation command, following the exact pattern used for the existing test-verify-*.py gates. (CI workflow wiring left out of this PR since it touches .github/workflows/ci.yml — happy to add in a follow-up if useful.) * Address review feedback on PR #69 - Wire test-verify-semantic-motion.py and test-verify-sequence-oauth.py into .github/workflows/ci.yml so CONTRIBUTING's new gates actually run in CI, with matching step-summary rows. - Bump .claude-plugin/plugin.json and .codex-plugin/plugin.json in sync to 2.5.7 so scripts/verify-plugin-package.py origin/main passes again. - Simplify test-verify-sequence-oauth.py's adversarial fixture to copy only the files verify-sequence-oauth.py's main() reads before the dashed-return check (type-sequence.md + cold-cache/oauth example trios), instead of shutil.copytree of the whole repository. * Re-bump plugin manifests above current main The prior 2.5.7 bump was superseded by subsequent merges of main into this branch (main itself advanced past it to 2.6.5). Re-run bump-plugin-version.py so the branch is strictly above upstream main again (2.6.6), per @philyuchkoff's review: the diff had settled back to no version delta at all. --------- Co-authored-by: alvinhui <alvinhui@users.noreply.github.com> Co-authored-by: Cathryn Lavery <50469282+cathrynlavery@users.noreply.github.com>
367 lines
18 KiB
YAML
367 lines
18 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [ main ]
|
|
pull_request:
|
|
branches: [ main ]
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
PYTHONIOENCODING: utf-8
|
|
PYTHONUTF8: "1"
|
|
|
|
jobs:
|
|
plugin-package:
|
|
name: Plugin Package & Version Gate
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
|
|
steps:
|
|
- name: Checkout repository history
|
|
uses: actions/checkout@v7
|
|
env:
|
|
GIT_CONFIG_COUNT: "1"
|
|
GIT_CONFIG_KEY_0: "core.autocrlf"
|
|
GIT_CONFIG_VALUE_0: "false"
|
|
with:
|
|
fetch-depth: 0
|
|
persist-credentials: false
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v7
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version: "22"
|
|
|
|
- name: Test plugin package tooling
|
|
run: python3 scripts/test-plugin-package.py
|
|
|
|
- name: Require a synchronized plugin version bump
|
|
env:
|
|
BASE_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
|
|
run: python3 scripts/verify-plugin-package.py "$BASE_REF"
|
|
|
|
- name: Validate Claude marketplace package without warnings
|
|
run: npx --yes @anthropic-ai/claude-code@2.1.229 plugin validate . --strict
|
|
|
|
python39-compat:
|
|
name: Python 3.9 Compatibility
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v7
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Set up Python 3.9
|
|
uses: actions/setup-python@v7
|
|
with:
|
|
python-version: "3.9"
|
|
|
|
- name: Verify linter compatibility
|
|
run: |
|
|
python scripts/test-lint-a11y.py
|
|
python scripts/lint-skin.py --all --baseline
|
|
|
|
validate:
|
|
name: Lint & Verify (${{ matrix.os }}, Python ${{ matrix.python-version }})
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
python-version: ["3.11", "3.12"]
|
|
runs-on: ${{ matrix.os }}
|
|
timeout-minutes: 20
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v7
|
|
env:
|
|
GIT_CONFIG_COUNT: "1"
|
|
GIT_CONFIG_KEY_0: "core.autocrlf"
|
|
GIT_CONFIG_VALUE_0: "false"
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- name: Set up Python
|
|
uses: actions/setup-python@v7
|
|
with:
|
|
python-version: ${{ matrix.python-version }}
|
|
|
|
- name: Verify accessible SVG contract
|
|
if: always()
|
|
id: a11y
|
|
run: python scripts/test-lint-a11y.py
|
|
|
|
- name: Verify semantic pattern documentation
|
|
if: always()
|
|
id: semantic_docs
|
|
run: python scripts/verify-semantic-motion.py --markdown-only
|
|
|
|
- name: Verify animated example structure
|
|
if: always()
|
|
id: animated_example
|
|
run: python scripts/verify-semantic-motion.py --example-only
|
|
|
|
- name: Lint animated example skin
|
|
if: always()
|
|
id: animated_lint
|
|
run: python scripts/lint-skin.py skills/diagram-design/assets/example-policy-trace-animated.html
|
|
|
|
- name: Verify every shipped motion file
|
|
if: always()
|
|
id: shipped_motion
|
|
run: python scripts/verify-motion.py --shipped
|
|
|
|
- name: Verify URL SVG normalization
|
|
if: always()
|
|
id: url_icons
|
|
run: python scripts/test-build-icons-quoted-attributes.py
|
|
|
|
- name: Verify Devicon normalization
|
|
if: always()
|
|
id: devicon
|
|
run: python scripts/test-build-icons-devicon.py
|
|
|
|
- name: Verify mojibake repair utility
|
|
if: always()
|
|
id: mojibake
|
|
run: python scripts/test-fix-mojibake.py
|
|
|
|
- name: Run skin linter
|
|
if: always()
|
|
id: skin
|
|
run: python scripts/lint-skin.py --all --baseline
|
|
|
|
- name: Verify quantitative polar chart
|
|
if: always()
|
|
id: polar
|
|
shell: bash
|
|
run: |
|
|
python scripts/test-verify-polar.py
|
|
python scripts/verify-polar.py
|
|
|
|
- name: Verify sequence OAuth specs
|
|
if: always()
|
|
id: oauth
|
|
run: python scripts/verify-sequence-oauth.py
|
|
|
|
- name: Verify semantic-motion verifier
|
|
if: always()
|
|
id: semantic_motion_tests
|
|
run: python scripts/test-verify-semantic-motion.py
|
|
|
|
- name: Verify sequence-oauth verifier
|
|
if: always()
|
|
id: oauth_tests
|
|
run: python scripts/test-verify-sequence-oauth.py
|
|
|
|
- name: Verify draw.io import script
|
|
if: always()
|
|
id: drawio
|
|
run: |
|
|
python scripts/verify-drawio-import.py
|
|
python scripts/test-verify-drawio-import.py
|
|
|
|
- name: Verify Mermaid import script
|
|
if: always()
|
|
id: mermaid
|
|
run: python scripts/verify-mermaid-import.py
|
|
|
|
- name: Verify optional motion contract
|
|
if: always()
|
|
id: motion
|
|
run: python scripts/test-verify-motion.py
|
|
|
|
- name: Verify doctor diagnostics contract
|
|
if: always()
|
|
id: doctor
|
|
run: python scripts/verify-doctor.py
|
|
|
|
- name: Verify doctor diagnostics tests
|
|
if: always()
|
|
id: doctor_tests
|
|
run: python scripts/test-verify-doctor.py
|
|
|
|
- name: Verify docs and routing sync
|
|
if: always()
|
|
id: docs_sync
|
|
shell: bash
|
|
run: |
|
|
python scripts/verify-docs-sync.py
|
|
python scripts/test-verify-docs-sync.py
|
|
|
|
- name: Verify canonical screenshot freshness
|
|
if: always()
|
|
id: screenshots
|
|
run: python scripts/verify-screenshot-freshness.py
|
|
|
|
- name: Verify packaged self-check
|
|
if: always()
|
|
id: self_check
|
|
run: python scripts/test-self-check.py
|
|
|
|
- name: Verify label geometry
|
|
if: always()
|
|
id: geometry
|
|
run: python scripts/verify-geometry.py --all
|
|
|
|
- name: Verify label geometry checker
|
|
if: always()
|
|
id: geometry_tests
|
|
run: python scripts/test-verify-geometry.py
|
|
|
|
- name: Verify treemap area fidelity and label fit
|
|
if: always()
|
|
id: treemap
|
|
run: python scripts/verify-treemap.py --all
|
|
|
|
- name: Verify treemap checker
|
|
if: always()
|
|
id: treemap_tests
|
|
run: python scripts/test-verify-treemap.py
|
|
|
|
- name: Verify dumbbell domain and contrast rules
|
|
if: always()
|
|
id: dumbbell
|
|
run: python scripts/verify-dumbbell.py
|
|
|
|
- name: Verify dumbbell checker
|
|
if: always()
|
|
id: dumbbell_tests
|
|
run: python scripts/test-verify-dumbbell.py
|
|
- name: Verify slopegraph shared scale and endpoint values
|
|
if: always()
|
|
id: slopegraph
|
|
run: python scripts/verify-slopegraph.py --all
|
|
|
|
- name: Verify slopegraph checker
|
|
if: always()
|
|
id: slopegraph_tests
|
|
run: python scripts/test-verify-slopegraph.py
|
|
|
|
- name: Verify sankey flow conservation
|
|
if: always()
|
|
id: sankey
|
|
run: python scripts/verify-sankey.py --all
|
|
|
|
- name: Verify sankey checker
|
|
if: always()
|
|
id: sankey_tests
|
|
run: python scripts/test-verify-sankey.py
|
|
|
|
- name: Verify ridgeline shared amplitude
|
|
if: always()
|
|
id: ridgeline
|
|
run: python scripts/verify-ridgeline.py --all
|
|
|
|
- name: Verify ridgeline checker
|
|
if: always()
|
|
id: ridgeline_tests
|
|
run: python scripts/test-verify-ridgeline.py
|
|
|
|
- name: Verify bubble positions and area encoding
|
|
if: always()
|
|
id: bubble
|
|
run: python scripts/verify-bubble.py --all
|
|
|
|
- name: Verify bubble checker
|
|
if: always()
|
|
id: bubble_tests
|
|
run: python scripts/test-verify-bubble.py
|
|
|
|
# lint-render.py's oracle is pixels, so the browser is part of the contract:
|
|
# pin Playwright exactly, and let that pin choose the Chromium build. Bump
|
|
# both together, deliberately, by editing PLAYWRIGHT_VERSION.
|
|
#
|
|
# One platform only. Antialiasing and font fallback differ per OS, so running
|
|
# a pixel comparison across the whole matrix would compare six oracles rather
|
|
# than one, and the thresholds are calibrated on Linux Chromium. The other
|
|
# matrix legs still run every source-level gate.
|
|
- name: Install Playwright Chromium
|
|
if: always() && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
|
|
env:
|
|
PLAYWRIGHT_VERSION: "1.62.0"
|
|
shell: bash
|
|
run: |
|
|
pip install "playwright==${PLAYWRIGHT_VERSION}"
|
|
playwright install --with-deps chromium
|
|
python -c "import importlib.metadata as m; print('playwright', m.version('playwright'))"
|
|
|
|
# The checks before the sweep: a broken linter must not be able to report
|
|
# a clean repository.
|
|
- name: Verify render linter checks
|
|
if: always() && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
|
|
id: render_selftest
|
|
run: python scripts/lint-render.py --self-test
|
|
|
|
- name: Run render linter
|
|
if: always() && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
|
|
id: render
|
|
run: python scripts/lint-render.py --all
|
|
|
|
- name: Verify generated icon assets
|
|
if: always()
|
|
id: icons
|
|
shell: bash
|
|
run: |
|
|
python scripts/build-icons.py
|
|
git diff --ignore-space-at-eol --exit-code -- \
|
|
skills/diagram-design/assets/icons.html \
|
|
skills/diagram-design/references/primitive-icons.md
|
|
|
|
- name: Generate CI execution summary table
|
|
if: always()
|
|
shell: bash
|
|
run: |
|
|
echo "### 📊 CI Verification Summary (${{ matrix.os }}, Python ${{ matrix.python-version }})" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Verification Gate | Outcome |" >> $GITHUB_STEP_SUMMARY
|
|
echo "|---|---|" >> $GITHUB_STEP_SUMMARY
|
|
echo "| SVG Accessibility Contract | ${{ steps.a11y.outcome == 'success' && '✅ Passed' || (steps.a11y.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Semantic Pattern Documentation | ${{ steps.semantic_docs.outcome == 'success' && '✅ Passed' || (steps.semantic_docs.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Animated Example Structure | ${{ steps.animated_example.outcome == 'success' && '✅ Passed' || (steps.animated_example.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Animated Example Skin | ${{ steps.animated_lint.outcome == 'success' && '✅ Passed' || (steps.animated_lint.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Shipped Motion HTML | ${{ steps.shipped_motion.outcome == 'success' && '✅ Passed' || (steps.shipped_motion.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| URL SVG Normalization | ${{ steps.url_icons.outcome == 'success' && '✅ Passed' || (steps.url_icons.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Devicon Normalization | ${{ steps.devicon.outcome == 'success' && '✅ Passed' || (steps.devicon.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Mojibake Repair Utility | ${{ steps.mojibake.outcome == 'success' && '✅ Passed' || (steps.mojibake.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Color Palette & Skin Linter | ${{ steps.skin.outcome == 'success' && '✅ Passed' || (steps.skin.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Quantitative Polar Chart | ${{ steps.polar.outcome == 'success' && '✅ Passed' || (steps.polar.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Sequence Diagram Grammar | ${{ steps.oauth.outcome == 'success' && '✅ Passed' || (steps.oauth.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Semantic-Motion Verifier Tests | ${{ steps.semantic_motion_tests.outcome == 'success' && '✅ Passed' || (steps.semantic_motion_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Sequence-OAuth Verifier Tests | ${{ steps.oauth_tests.outcome == 'success' && '✅ Passed' || (steps.oauth_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Draw.io Import Extractor | ${{ steps.drawio.outcome == 'success' && '✅ Passed' || (steps.drawio.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Mermaid Import Extractor | ${{ steps.mermaid.outcome == 'success' && '✅ Passed' || (steps.mermaid.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Optional Motion Contract | ${{ steps.motion.outcome == 'success' && '✅ Passed' || (steps.motion.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Doctor Diagnostics Contract | ${{ steps.doctor.outcome == 'success' && '✅ Passed' || (steps.doctor.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Doctor Diagnostics Tests | ${{ steps.doctor_tests.outcome == 'success' && '✅ Passed' || (steps.doctor_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Docs & Routing Sync | ${{ steps.docs_sync.outcome == 'success' && '✅ Passed' || (steps.docs_sync.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Canonical Screenshot Freshness | ${{ steps.screenshots.outcome == 'success' && '✅ Passed' || (steps.screenshots.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Packaged Self-Check | ${{ steps.self_check.outcome == 'success' && '✅ Passed' || (steps.self_check.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Label Geometry | ${{ steps.geometry.outcome == 'success' && '✅ Passed' || (steps.geometry.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Label Geometry Checker | ${{ steps.geometry_tests.outcome == 'success' && '✅ Passed' || (steps.geometry_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Treemap Area & Label Fit | ${{ steps.treemap.outcome == 'success' && '✅ Passed' || (steps.treemap.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Treemap Checker | ${{ steps.treemap_tests.outcome == 'success' && '✅ Passed' || (steps.treemap_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Dumbbell Domain & Contrast | ${{ steps.dumbbell.outcome == 'success' && '✅ Passed' || (steps.dumbbell.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Dumbbell Checker | ${{ steps.dumbbell_tests.outcome == 'success' && '✅ Passed' || (steps.dumbbell_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Slopegraph Shared Scale | ${{ steps.slopegraph.outcome == 'success' && '✅ Passed' || (steps.slopegraph.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Slopegraph Checker | ${{ steps.slopegraph_tests.outcome == 'success' && '✅ Passed' || (steps.slopegraph_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Sankey Flow Conservation | ${{ steps.sankey.outcome == 'success' && '✅ Passed' || (steps.sankey.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Sankey Checker | ${{ steps.sankey_tests.outcome == 'success' && '✅ Passed' || (steps.sankey_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Ridgeline Shared Amplitude | ${{ steps.ridgeline.outcome == 'success' && '✅ Passed' || (steps.ridgeline.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Ridgeline Checker | ${{ steps.ridgeline_tests.outcome == 'success' && '✅ Passed' || (steps.ridgeline_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Bubble Position & Area | ${{ steps.bubble.outcome == 'success' && '✅ Passed' || (steps.bubble.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Bubble Checker | ${{ steps.bubble_tests.outcome == 'success' && '✅ Passed' || (steps.bubble_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Primitive Icons & Documentation | ${{ steps.icons.outcome == 'success' && '✅ Passed' || (steps.icons.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Render Linter Self-Test | ${{ steps.render_selftest.outcome == 'success' && '✅ Passed' || (steps.render_selftest.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|
|
echo "| Rendered Layout (paint oracle) | ${{ steps.render.outcome == 'success' && '✅ Passed' || (steps.render.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
|