Files
star-map/.github/workflows/ci.yml
T
SenrokaiandClaude Opus 5 f156e03822 Answer the review: send the worker one request at a time, keep only the latest, and never wait for a dead one
The adversarial review confirmed three defects in this PR, all reproduced in
the browser.

1. Superseded graphs queued up in front of routes. The worker answers
   messages one at a time and cannot drop one it has started. With the
   jump-link layer on, every pause on the range slider posted a full graph
   build, seconds of work at 6-8 pc. Answers no longer wanted were thrown
   away only once built. A route asked for afterwards waited behind every
   one of them: a one-jump route took 44 s.

   RoutingClient now holds requests and sends them one at a time. While one
   is out, only the latest of each kind waits: a newer graph replaces an
   older one before it is ever built, and the older promise is rejected with
   SupersededRequest. Routes go ahead of graphs. The same question asked
   again while outstanding shares the answer rather than being worked twice,
   as when the layer is turned off and on during a build.

   The same scenario in the browser (layer on, range stepped 5 -> 8 pc with
   400 ms pauses, then Sol to Proxima): the route came back in 110 ms. The
   worker was sent "links 3, links 5, route, links 8"; 6 and 7 were never
   built.

2. A worker that failed left the panel stuck. With no error handling, a
   worker that failed to load (a 404 on its chunk after a redeploy) or
   threw left "Plotting…" and a disabled button for good, and a graph at a
   range could not be asked for again.

   The worker now answers an exception with a 'failed' message, which
   rejects that request. A worker that fails to load or dies is abandoned,
   and what it left outstanding, and everything asked afterwards, is
   answered in place. The scene releases the panel when a route fails, and
   forgets a graph range that was never drawn so it can be asked for again.

3. Nothing type-checked the worker. The application builder never reads
   webWorkerTsConfig, and bundles the worker with esbuild, which strips
   types without checking them. tsconfig.app.json leaves the file out. A
   type error in the worker shipped.

   `npm run worker:typecheck` (tsc -p tsconfig.worker.json) now runs in CI
   beside the other project checks. webWorkerTsConfig is removed from
   angular.json, since it only suggested that something checked the worker.

Tests with a fake worker cover one request at a time, a waiting graph
replaced and a route sent ahead of it, a question shared, a failure rejected
and the next request sent, and a failed worker's requests answered in place.
A scene test covers the panel released after a failed route. Negative
controls, each caught: several requests sent at once, a waiting graph kept,
graphs ahead of routes, a question asked twice, a failure answered as a
success, a failed worker waited on, the panel left pending, and a type error
in the worker (caught by worker:typecheck).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jxMkwA2rbicdGxHosecYi
2026-09-16 15:27:50 +02:00

92 lines
3.2 KiB
YAML

name: CI
# Runs on pull requests and on the branches they merge into, so a green tick means the code was
# checked in the state it will actually land in. `develop` is where work integrates and `main` is
# what it is promoted to; a merge into either is a state nothing else would otherwise check,
# since a pull request is checked before the merge rather than after it.
on:
push:
branches: [main, develop]
pull_request:
# For the data-refresh workflow: its bot push to main fires no `push` events (GitHub's
# recursion guard), so it dispatches CI here explicitly to put checks on the new commit.
workflow_dispatch:
# A second push to the same branch makes the first run's answer irrelevant.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
checks:
name: Typecheck, unit tests, build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
cache: npm
# `npm ci` rather than `npm install`: it installs exactly what package-lock.json pins and
# fails if the lockfile has drifted from package.json, so CI cannot silently test a
# different dependency tree than the one committed.
- run: npm ci
# Five TypeScript projects, checked by four different things. These three have no build of
# their own that checks them, so nothing else would ever compile them. The worker is bundled by
# the build, but esbuild only strips its types, and `tsconfig.app.json` leaves it out, since
# its lib is `webworker` rather than `dom`.
- name: Typecheck the ETL
run: npm run etl:typecheck
- name: Typecheck the end-to-end tests
run: npm run e2e:typecheck
- name: Typecheck the routing worker
run: npm run worker:typecheck
# `tsconfig.spec.json` is compiled here, `tsconfig.app.json` by the build below.
- name: Unit tests
run: npm test -- --no-watch
- name: Production build
run: npm run build
e2e:
name: End-to-end
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: 22
cache: npm
- run: npm ci
# `--with-deps` installs the system libraries headless Chromium needs, which a bare runner
# does not have. Only chromium: playwright.config.ts defines no other project.
- name: Install Playwright Chromium
run: npx playwright install --with-deps chromium
# Playwright starts the dev server itself (see `webServer` in playwright.config.ts).
# GitHub sets CI=true, which turns on `forbidOnly` and the two retries.
- name: End-to-end tests
run: npm run e2e
# The HTML reporter's output is the only way to see why a headless browser failed. Only
# kept when something did fail — on a green run it is several megabytes saying so.
- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report/
retention-days: 7