Skip to content

Align SSR

Align SSR #1900

Workflow file for this run

name: Semiotic
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
# Run freshness, size, and packed-consumer gates independently from coverage
# and the broader contract suite so regressions appear within the first CI
# job instead of after the long-running test path completes.
fast-contracts:
name: Fast feedback gates (Node 22)
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v7
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: 22.x
cache: "npm"
- name: Install dependencies
run: npm install
- name: Run freshness and size gates
run: npm run check:fast
# Full gate suite once on the primary LTS. Cross-Node compatibility is
# covered by the smoke matrix below — re-running docs/AI/size checks on
# every Node version was pure wall-clock cost with no signal.
testing-full:
name: Full checks (Node 22)
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v7
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: 22.x
cache: "npm"
- name: Install dependencies
run: npm install
- name: Build library
run: npm run dist
- name: Build MCP server
run: npm run build:mcp
- name: Check Cloud Run deployment manifest
run: npm run check:cloud-run-manifest
- name: Check repository-built nightly Cloud Run deployment
run: npm run check:cloud-run-nightly
- name: Test nightly deployment config and hosted smoke runner
run: |
npm run test:cloud-run-nightly
npm run test:smoke-hosted-mcp
- name: Run tests with coverage
run: npm run test:coverage
- name: Run type check
run: npm run typescript
- name: Run test type check
run: npm run typescript:tests
- name: Run MCP type check
run: npm run typescript:mcp
- name: Check chart spec registry round-trip
run: npm run check:chart-specs
- name: Check capability matrix freshness
run: npm run check:capabilities
- name: Check visual baseline capability coverage
run: npm run check:visual-baseline-capabilities
- name: Check docs coverage and per-page quality
run: npm run check:docs-coverage
- name: Check docs example manifest and source integrity
run: npm run check:docs-example-integrity
- name: Check prop-table drift vs chartSpecs
run: npm run check:docs-prop-tables
- name: Check playground control drift vs chartSpecs
run: npm run check:docs-playground-controls
- name: Check llms.txt freshness
run: npm run check:llms
- name: Check blog metadata registry freshness
run: npm run check:blog-entries
- name: Check CLAUDE.md component coverage
run: npm run check:claude-md-coverage
- name: Check context7.json freshness
run: npm run check:context7
- name: Check MCP Registry submission cross-references
run: npm run check:mcp-registry
- name: Check AI/MCP surface parity
run: npm run check:surface
- name: Check generated AI surface manifest freshness
run: npm run check:ai-surface
- name: Check SSR alignment
run: npm run check:ssr
- name: Check public API surface (no breaking changes)
run: npm run check:api-surface
- name: Check HOC JSDoc coverage (≥2 @example blocks per HOC)
run: npm run check:jsdoc-coverage
- name: Check ai/examples.md coverage + drift
run: npm run check:ai-examples-coverage
- name: Build docs site and smoke-check generated routes
run: npm run check:website-build
- name: Pack smoke test (verify all entry points resolve)
run: npm run check:pack
- name: Verify TypeScript declarations
run: |
for f in dist/semiotic.d.ts dist/semiotic-xy.d.ts dist/semiotic-ordinal.d.ts dist/semiotic-network.d.ts dist/semiotic-geo.d.ts dist/semiotic-realtime.d.ts dist/semiotic-ai.d.ts dist/semiotic-data.d.ts dist/semiotic-server.d.ts dist/semiotic-themes.d.ts; do
if [ ! -f "$f" ]; then
echo "MISSING: $f"
exit 1
fi
done
echo "All declaration files present"
- name: Validate canvas stub completeness
run: |
# Extract canvas method names used in production renderers
USED=$(grep -roh 'ctx\.\([a-zA-Z]*\)' src/components/stream/renderers/ | \
sed 's/ctx\.//' | sort -u | grep -v '^$')
# Extract stubbed method names from setupTests.ts
STUBBED=$(grep -oP '"\K[a-zA-Z]+(?=")' src/setupTests.ts | sort -u)
# Find methods used in renderers but not stubbed
MISSING=""
for method in $USED; do
if ! echo "$STUBBED" | grep -qx "$method"; then
# Skip property assignments (not methods)
case "$method" in
fillStyle|strokeStyle|lineWidth|lineCap|lineJoin|globalAlpha|\
globalCompositeOperation|font|textAlign|textBaseline|shadowColor|\
shadowBlur|shadowOffsetX|shadowOffsetY|lineDashOffset|miterLimit|\
canvas|imageSmoothingEnabled|direction|filter|letterSpacing|\
fontKerning|fontStretch|fontVariantCaps|textRendering|wordSpacing)
;; # property, not a method — skip
*)
MISSING="$MISSING $method"
;;
esac
fi
done
if [ -n "$MISSING" ]; then
echo "Canvas methods used in renderers but not stubbed in setupTests.ts:"
echo "$MISSING"
echo ""
echo "Add these to the canvasMethods array in src/setupTests.ts"
exit 1
fi
echo "All canvas methods are stubbed"
# Smoke matrix: install, build, typecheck, and unit tests on each
# supported Node. Catches runtime/engine issues without re-running the
# full docs/AI contract suite four times.
testing-matrix:
name: Smoke (Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# 22.x is covered by testing-full; matrix is the other engines only.
node-version: ["20.19.0", "24.x", "26.x"]
steps:
- name: Checkout source code
uses: actions/checkout@v7
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- name: Install dependencies
run: npm install
- name: Build library
run: npm run dist
- name: Build MCP server
run: npm run build:mcp
- name: Run tests
run: npx vitest run
- name: Run type check
run: npm run typescript
- name: Run MCP type check
run: npm run typescript:mcp
testing:
name: testing
runs-on: ubuntu-latest
needs: [fast-contracts, testing-full, testing-matrix]
if: always()
steps:
- name: Aggregate matrix result
run: |
if [[ "${{ needs['fast-contracts'].result }}" != "success" || "${{ needs['testing-full'].result }}" != "success" || "${{ needs['testing-matrix'].result }}" != "success" ]]; then
{
echo "## Testing failed"
echo ""
echo "Fast feedback gates result: \`${{ needs['fast-contracts'].result }}\`"
echo "Full checks result: \`${{ needs['testing-full'].result }}\`"
echo "Smoke matrix result: \`${{ needs['testing-matrix'].result }}\`"
echo ""
echo "Open the failed job above for the concrete error."
} >> "$GITHUB_STEP_SUMMARY"
echo "::error title=Testing failed::Fast feedback gates: ${{ needs['fast-contracts'].result }}; Full checks: ${{ needs['testing-full'].result }}; Smoke matrix: ${{ needs['testing-matrix'].result }}"
exit 1
fi
bench:
name: Performance regression
runs-on: ubuntu-latest
# PR-only: captures main's bench numbers in the same job on the same
# hardware, then compares the PR's results against them. Avoids the
# cross-architecture noise that plagues a committed-baseline approach
# (M-series Mac local capture vs. x86 Linux CI runner produces uniform
# 60–90% "slowdowns" across every benchmark). On `push: main` there's
# nothing to compare main against, so the job is skipped.
if: github.event_name == 'pull_request'
steps:
- name: Checkout source code
uses: actions/checkout@v7
with:
# `bench:pr-vs-main` worktrees `origin/main` to capture a
# CI-native baseline. Default checkout is shallow to the
# triggering ref only; full history makes worktree-add reliable
# without needing extra fetch plumbing.
fetch-depth: 0
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: 22.x
cache: "npm"
- name: Install dependencies
run: npm install
- name: Compare PR vs main on identical hardware
run: npm run bench:pr-vs-main
e2e:
runs-on: ubuntu-latest
needs: testing
# Pin the rendering environment. `ubuntu-latest` is a moving image whose
# font/freetype stack drifts from committed baselines, causing sub-pixel
# text-AA diffs across the whole visual suite. Running inside the same
# Playwright image the baselines are generated in (scripts/run-playwright-
# linux-bootstrap.mjs) makes CI renders match the baselines exactly and
# survives future `ubuntu-latest` bumps.
container: mcr.microsoft.com/playwright:v1.61.1-noble
# In a container job GitHub sets $HOME=/github/home (owned by the image's
# pwuser) while steps run as root. Firefox refuses to launch unless $HOME
# is owned by the current user, so point it at root's own home. Chromium
# and WebKit are unaffected; without this only Firefox fails to start.
env:
HOME: /root
steps:
- name: Checkout source code
uses: actions/checkout@v7
- name: Use Node.js
uses: actions/setup-node@v6
with:
node-version: 22.x
cache: "npm"
- name: Install dependencies
run: npm install
- name: Install Playwright browsers
run: npx playwright install --with-deps chromium firefox webkit
- name: Build library
run: npm run dist
- name: Run E2E tests
# CI is the visual regression gate: a missing baseline is an
# unreviewed visual contract, not a reason to create one during a
# pull request. Maintainers can use `test:visual:bootstrap` locally
# to generate a proposed baseline, then commit it with review.
run: npx playwright test --update-snapshots=none
- name: Capture docs example source and local Chromium contract evidence
run: npm run test:examples:source
# The contract test validates this complete local-only capture before it
# writes the JSON. Retain it for review, but do not compare it against a
# committed baseline: capture time and runner geometry are per-run facts.
# It records reduced-motion and browser-emitted visibility behavior without
# claiming policy-specific animation, performance, or cross-browser parity.
- name: Upload local docs example contract evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: docs-example-local-contract-evidence
path: test-results/**/docs-example-local-contract-evidence.v2.json
if-no-files-found: warn
retention-days: 14
- name: Upload snapshot baselines
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-snapshots
path: integration-tests/*.spec.ts-snapshots/
retention-days: 30
- name: Upload test artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: |
test-results/
integration-tests/test-results/
retention-days: 7