diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f809657f..9123966f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,8 +22,8 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 20 steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v5 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5 with: node-version: 22 cache: npm @@ -40,7 +40,7 @@ jobs: run: npm run e2e:ci - name: Retain browser diagnostics if: ${{ failure() }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: name: browser-diagnostics path: e2e-out/ci/ diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index d9529c96..a0fd0564 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -36,7 +36,7 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 5 steps: - - uses: contributor-assistant/github-action@v2.6.1 + - uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/license-check.yml b/.github/workflows/license-check.yml index 124f00c1..9120f9a5 100644 --- a/.github/workflows/license-check.yml +++ b/.github/workflows/license-check.yml @@ -34,19 +34,27 @@ on: pull_request: workflow_dispatch: +permissions: + contents: read + +concurrency: + group: license-check-${{ github.ref }} + cancel-in-progress: true + jobs: npm-licenses: runs-on: ubuntu-24.04 + timeout-minutes: 15 steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v5 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5 with: node-version: 22 cache: npm - run: npm ci - name: npm license allowlist run: >- - npx --yes license-checker --excludePrivatePackages + npx --yes license-checker@25.0.1 --excludePrivatePackages --excludePackages "meshstep@0.1.1" --onlyAllow "MIT;Apache-2.0;MIT OR Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;0BSD;Zlib;CC0-1.0;MPL-2.0;Unicode-3.0;Unicode-DFS-2016;Unlicense;CC-BY-4.0;BlueOak-1.0.0" --summary @@ -55,7 +63,7 @@ jobs: - run: npm --prefix mcp ci - name: npm license allowlist (mcp/) run: >- - npx --yes license-checker --start mcp + npx --yes license-checker@25.0.1 --start mcp --excludePackages "scanruler-mcp@$(node -p "require('./mcp/package.json').version")" --onlyAllow "MIT;Apache-2.0;MIT OR Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC;0BSD;Zlib;CC0-1.0;MPL-2.0;Unicode-3.0;Unicode-DFS-2016;Unlicense;CC-BY-4.0;BlueOak-1.0.0" --summary diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d9f1ce70..df28144a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,13 +21,18 @@ on: permissions: contents: write +# Two runs never publish at once; a queued one waits rather than cancelling. +concurrency: + group: release + cancel-in-progress: false + jobs: release: if: ${{ github.repository == 'CNCKitchen/scanruler' }} runs-on: ubuntu-24.04 timeout-minutes: 10 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 with: fetch-depth: 0 - name: Publish the releases diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..1c4b947d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,73 @@ +# AGENTS.md + +Notes for coding agents working in this repository. The longer versions are +[CONTRIBUTING.md](CONTRIBUTING.md), [docs/development.md](docs/development.md) +and [plugins/README.md](plugins/README.md); when they disagree with this file, +they win. + +## What this is + +ScanRuler is a browser app (React 19, Vite 6, TypeScript, Three.js) for +measuring 3D scans against CAD. It deploys as static assets on Cloudflare +(`wrangler.jsonc`, output in `dist/`). Heavy work runs in module Web Workers, +some of it in WebAssembly (`manifold-3d`, `occt-wasm`). `mcp/` is a separate +Node package: the MCP server that lets an agent drive a running app. + +## Setup + +Node 22 or newer. + +```bash +npm ci +npm --prefix mcp ci +``` + +## Verify before you hand work back + +These are what CI runs (`.github/workflows/ci.yml`), in order: + +```bash +node scripts/check-plugin-boundary.mjs # nothing outside plugins/ imports from it +node scripts/check-units.mjs # on-screen lengths go through the display units +npm test # vitest, unit + acceptance +npm --prefix mcp test # the MCP server's own tests +npm run build # tsc --noEmit, then vite build +npm run e2e:ci # headless-Chrome checks; needs Chrome and a build +``` + +Acceptance tests that need the large real scans (`ballbar.stl`, the +`side bracket left.stl` / `block-marius.stl` pair and others) are not in the +repo and report as skipped without them. That is expected; do not treat the +skips as failures, and do not loosen a tolerance to make a test pass. + +`npm run dev` plus `node scripts/e2e-.mjs` runs a single browser check +against the dev server; see docs/development.md for which ones need no scan. + +## Rules the checks enforce + +- **Plugin boundary.** Workspaces such as Reverse engineer and Mesh Tools live + in `plugins//` and talk to the app only through + `src/plugins/api.ts`. Nothing in `src/` may import from `plugins/`; only + the registries (`src/plugins/registry.ts`, `src/core/workerPlugins.ts`) + name it, by glob. A build must stand with any plugin or none. +- **Display units.** Lengths shown to the user go through `src/core/units.ts` + (millimetres or inches). Do not write `mm` in UI text. A deliberate `mm` + (an id, a stored field name) carries a `units-ok` comment saying why. + The agent command API always answers in millimetres. +- **Dependency licenses.** Only permissive licenses (list in CONTRIBUTING.md + section 2 and `.github/workflows/license-check.yml`). Never GPL, LGPL, or + AGPL code we do not own; do not transcribe algorithms from such projects. + Ask before adding any dependency. + +## Style + +- New source files start with `// SPDX-License-Identifier: AGPL-3.0-only`. +- Numerical code belongs in `src/core/` or a plugin's `core/` and must run + without a browser: no DOM and no Three.js scene objects in fitting or + deviation maths. +- Comments explain why a non-obvious approach was chosen, not what a line does. + Match the surrounding code. +- User-visible changes get an entry at the top of `CHANGELOG.md`. +- The version lives only in `package.json`. When it changes, regenerate the + MCP command list: + `UPDATE_MCP_COMMANDS=1 npx vitest run tests/mcpCommands.test.ts`. diff --git a/index.html b/index.html index c24a7858..cf7c4cde 100644 --- a/index.html +++ b/index.html @@ -31,13 +31,10 @@ html { background: #e7e6e1; } html[data-theme='dark'] { background: #1b1d20; } - + +
diff --git a/public/_headers b/public/_headers new file mode 100644 index 00000000..84e15850 --- /dev/null +++ b/public/_headers @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: AGPL-3.0-only +# Response headers for every asset Cloudflare serves from dist/. +# +# Content-Security-Policy, by directive: +# script-src own bundle only; 'wasm-unsafe-eval' lets the workers compile +# manifold-3d and occt-wasm. Workers inherit it from their own +# response, which this same rule covers. +# style-src 'unsafe-inline' for the inline