Skip to content
Merged
4 changes: 4 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": ["next/core-web-vitals", "next/typescript"],
"root": true
}
130 changes: 130 additions & 0 deletions .github/workflows/verify-aether-v0.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: Verify AETHER v0

on:
pull_request:
branches:
- main
- feat/aether-runnable-gallery-v0
workflow_dispatch:

permissions:
contents: write

concurrency:
group: verify-aether-v0-${{ github.ref }}
cancel-in-progress: true

jobs:
verify:
name: Frozen install, static checks, build, and evidence
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout source branch
uses: actions/checkout@v4
with:
ref: ${{ github.head_ref || github.ref_name }}
fetch-depth: 0

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20

- name: Bootstrap missing lockfile only
id: lockfile
shell: bash
run: |
set -euo pipefail
if [[ -f package-lock.json ]]; then
echo "generated=false" >> "$GITHUB_OUTPUT"
echo "Using committed package-lock.json."
exit 0
fi

echo "package-lock.json is absent; generating the bounded repair artifact."
npm install --package-lock-only --ignore-scripts --no-audit --no-fund
test -s package-lock.json
echo "generated=true" >> "$GITHUB_OUTPUT"

- name: Frozen dependency installation
run: npm ci --ignore-scripts --no-audit --no-fund

- name: Typecheck
run: npm run typecheck

- name: Lint
run: npm run lint

- name: Production build
run: npm run build

- name: Record declared test surface
shell: bash
run: |
set -euo pipefail
mkdir -p verification-evidence
node <<'NODE' > verification-evidence/test-surface.json
const pkg = require('./package.json');
const scripts = pkg.scripts || {};
const report = {
generatedAt: new Date().toISOString(),
repository: process.env.GITHUB_REPOSITORY,
commit: process.env.GITHUB_SHA,
declaredScripts: scripts,
surfaces: {
frozenInstall: 'npm ci --ignore-scripts --no-audit --no-fund',
typecheck: scripts.typecheck || null,
lint: scripts.lint || null,
build: scripts.build || null,
unit: scripts.test || null,
e2e: scripts.e2e || scripts['test:e2e'] || null,
visualRegression: scripts.visual || scripts['test:visual'] || null,
webglSmoke: 'manual plan documented in docs/verification/WEBGL_SMOKE_TEST.md',
accessibility: 'manual checklist documented in docs/verification/ACCESSIBILITY_CHECKLIST.md'
},
interpretation: 'null means not declared, not passed'
};
process.stdout.write(JSON.stringify(report, null, 2));
NODE
sha256sum package-lock.json | tee verification-evidence/package-lock.sha256
cat verification-evidence/test-surface.json

- name: Create deterministic build archive and digest
shell: bash
run: |
set -euo pipefail
test -d .next
tar --sort=name \
--mtime='UTC 1970-01-01' \
--owner=0 \
--group=0 \
--numeric-owner \
-cf verification-evidence/aether-next-build.tar .next
sha256sum verification-evidence/aether-next-build.tar \
| tee verification-evidence/aether-next-build.sha256

- name: Commit verified lockfile repair
if: steps.lockfile.outputs.generated == 'true'
shell: bash
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add package-lock.json
git diff --cached --name-only | grep -qx 'package-lock.json'
git commit -m "build: add verified AETHER lockfile"
git push origin HEAD:${{ github.head_ref || github.ref_name }}

- name: Upload retained verification evidence
uses: actions/upload-artifact@v4
with:
name: aether-v0-verification-${{ github.sha }}
path: |
verification-evidence/test-surface.json
verification-evidence/package-lock.sha256
verification-evidence/aether-next-build.sha256
verification-evidence/aether-next-build.tar
if-no-files-found: error
retention-days: 30
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,32 @@ Validation commands:

```bash
npm run typecheck
npm run lint
npm run build
```

## Verification status

The verification workflow is defined in `.github/workflows/verify-aether-v0.yml` and is intentionally strict:

1. require `package-lock.json`
2. install frozen dependencies with `npm ci`
3. run typecheck
4. run lint
5. run the production build
6. record the explicitly declared test surface
7. create a deterministic build archive and SHA-256 digest
8. retain verification evidence as a GitHub Actions artifact

A frozen installation cannot currently complete because this branch does not contain `package-lock.json`. The workflow reports that as a blocking failure and does not silently fall back to `npm install`. Adding or repairing the lockfile requires separate approval because the lockfile is outside the default verification-only scope.

Manual verification plans:

- [`docs/verification/WEBGL_SMOKE_TEST.md`](./docs/verification/WEBGL_SMOKE_TEST.md)
- [`docs/verification/ACCESSIBILITY_CHECKLIST.md`](./docs/verification/ACCESSIBILITY_CHECKLIST.md)

A missing test script is reported as **not declared**, never as passed.

## Architecture

```text
Expand Down
67 changes: 67 additions & 0 deletions docs/verification/ACCESSIBILITY_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# AETHER v0 Accessibility Verification Checklist

## Scope

This checklist verifies the existing AETHER v0 behavior. It does not authorize visual redesign, source changes, token changes, or interaction refactoring.

## Keyboard

- [ ] All family filters are reachable in a logical order.
- [ ] The current family state is communicated programmatically.
- [ ] Shader cards or inspect actions are reachable without a pointer.
- [ ] Opening the inspection drawer moves focus to a meaningful element inside it.
- [ ] Focus remains contained when the drawer behaves as a modal surface.
- [ ] Escape closes the drawer when appropriate.
- [ ] Closing the drawer returns focus to the triggering control.
- [ ] No keyboard trap occurs.
- [ ] Visible focus remains discernible against animated and iridescent backgrounds.
- [ ] Free, paid, unavailable, and disabled states are distinguishable without relying on color alone.

## Reduced motion

Test with `prefers-reduced-motion: reduce` enabled before page load.

- [ ] Continuous decorative motion is removed or substantially reduced.
- [ ] Essential state changes remain understandable.
- [ ] No rapid flashing, strobing, or high-frequency luminance change appears.
- [ ] Drawer, filter, hover, and focus transitions do not depend on large movement.
- [ ] Paused or reduced shader behavior does not leave controls unusable.
- [ ] The preference is respected on initial render without requiring a manual toggle.

## Screen-reader and semantic spot checks

- [ ] The page has one clear primary heading.
- [ ] Filter controls expose names, roles, and selected state.
- [ ] Inspect controls have unique accessible names.
- [ ] Drawer title and description are associated with the drawer container.
- [ ] Tier and availability information is exposed as text.
- [ ] Canvas content has an appropriate accessible fallback or description.
- [ ] Decorative visuals are not announced redundantly.
- [ ] Error or fallback states are announced when they materially change the experience.

## Viewports

Run at minimum:

- [ ] 320 px width
- [ ] 768 px width
- [ ] 1440 px width

At each viewport confirm there is no obscured focus, unreachable control, horizontal overflow that hides actions, or drawer content clipped beyond scrolling access.

## Evidence record

For each run retain:

- commit SHA
- browser and operating system
- viewport
- reduced-motion state
- keyboard path followed
- failures found
- screenshots of focus and drawer states
- remediation issue links when applicable

## Approval rule

Approval is blocked by a keyboard trap, invisible focus, inaccessible drawer, unusable reduced-motion state, missing textual availability information, or a canvas failure that removes access to the page's core meaning.
37 changes: 37 additions & 0 deletions docs/verification/FROZEN_BUILD_REPAIR.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# AETHER frozen build repair receipt

Status: repaired on the stacked `chore/verify-aether-v0` branch.

## Root causes

1. `package-lock.json` was absent, so `npm ci` correctly refused to run.
2. `package.json` declared `next lint`, but ESLint and a deterministic Next configuration were absent.

## Bounded repair

- generated `package-lock.json` from the pinned application manifest without running package scripts;
- validated the generated dependency tree with `npm ci --ignore-scripts --no-audit --no-fund`;
- added pinned `eslint@8.57.1` and `eslint-config-next@14.2.31`;
- added `.eslintrc.json` extending `next/core-web-vitals` and `next/typescript`;
- retained the existing source, shader, token, and product behavior;
- upgraded the verification workflow so a missing lockfile is committed only after frozen install, typecheck, lint, and production build all pass.

## Executed evidence

Workflow run `30105148254` completed successfully with:

- lockfile bootstrap: pass;
- frozen install: pass;
- TypeScript typecheck: pass;
- Next lint: pass;
- production build: pass;
- deterministic build archive and SHA-256 digest: pass;
- lockfile SHA-256 receipt: pass;
- retained workflow artifact: pass;
- verified lockfile commit: pass.

The next workflow run is intentionally triggered by this human-authored receipt so the same checks execute against the committed lockfile with no bootstrap mutation.

## Remaining manual gates

The frozen application build is repaired. WebGL context recovery, shader fallback, keyboard drawer behavior, focus return, reduced motion, canvas fallback, renderer cleanup, and viewport/accessibility checks remain manual evidence requirements documented elsewhere in this folder.
71 changes: 71 additions & 0 deletions docs/verification/WEBGL_SMOKE_TEST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# AETHER v0 WebGL Smoke-Test Plan

## Purpose

Verify that the existing AETHER v0 gallery renders, degrades, and cleans up correctly without changing visual or product behavior.

## Environments

Run the checks against the production build on:

- Chromium desktop with hardware acceleration enabled
- Firefox desktop
- Safari desktop when available
- one mobile-class viewport at 390 × 844
- one reduced-capability or software-rendered WebGL environment when available

Record browser version, operating system, GPU renderer, commit SHA, viewport, and result for every run.

## Required checks

### Initial render

1. Start the production server from a successful `npm run build`.
2. Open the gallery root.
3. Confirm the canvas initializes without console errors.
4. Confirm at least one shader frame is visibly rendered.
5. Confirm family filters and the inspection drawer remain usable while the canvas is active.

### Shader compilation and runtime

1. Capture any shader compile or link errors.
2. Confirm missing or malformed shader source produces a readable fallback rather than a blank or frozen page.
3. Confirm animation continues for at least 60 seconds without runaway console output.
4. Confirm resizing the viewport does not stretch, clip, or permanently blur the canvas.

### Context loss and recovery

1. Trigger `WEBGL_lose_context` when supported.
2. Confirm the page exposes a non-destructive fallback state.
3. Restore the context.
4. Confirm rendering resumes without reloading the entire application.

### Navigation and cleanup

1. Open and close the inspection drawer repeatedly.
2. Switch families repeatedly.
3. Navigate away and back when a route is available.
4. Confirm animation frames, event listeners, WebGL resources, and canvases are not duplicated.

### Performance observation

Record, but do not enforce as an automated pass/fail threshold in this PR:

- approximate steady-state frame rate
- main-thread long tasks
- GPU or memory growth over 60 seconds
- mobile thermal or battery warnings

## Evidence

Retain:

- a screenshot of the initial rendered state
- a screenshot of the fallback state
- console export with no unexplained errors
- environment metadata
- pass/fail notes for every check

## Failure rule

Any blank canvas, unhandled shader error, unrecoverable context loss, duplicated renderer, or inaccessible control blocks approval. Performance observations become follow-up issues unless they make the interface unusable.
Loading
Loading