From 2e9d27b4e4542dd1e6a710abdcc6f80720c16fde Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 01:20:48 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=EC=8B=9C=EC=9E=91=20=E2=80=94=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC=202=EB=8B=A8=EA=B3=84=20PR=2012?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015BBxbLQw4YT47XGgbhCeqz From 181fe53cb34d70de2322cb90d5b4c25b94bb421f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 01:24:31 +0000 Subject: [PATCH 2/3] docs: say what CI runs now that drift.yml runs on every pull request CONTRIBUTING, web/README, web/ARCHITECTURE and docs/REPOSITORY described web-ci and check_links.py only, and REPOSITORY still said no CI checks the absolute links. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015BBxbLQw4YT47XGgbhCeqz --- CONTRIBUTING.md | 4 +++- docs/REPOSITORY.md | 6 +++--- web/ARCHITECTURE.md | 4 ++-- web/README.md | 14 ++++++++------ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 157ffb33..243adfbb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,11 +15,13 @@ Patternflow is a device, a firmware, a website and a pile of documentation, and | An enclosure or mechanical **remix** | `hardware/case/remixes/-/`, with a README that states Author, License, what it is based on, which board it fits, and the print or cut settings | one new folder; the official folders stay as they are | the README fields are checked | | A **bridge** to host software (TouchDesigner, Node-RED, Max, a DAW…) | `integrations//` with a README, plus a row in `integrations/README.md`. It talks to the panel through the contracts in `docs/` and never imports firmware or web code | one new folder | the README has to name the contract it is built against | | A change to a **wire contract** | [`docs/rest-api.md`](docs/rest-api.md), [`osc-spec.md`](docs/osc-spec.md), [`midi-spec.md`](docs/midi-spec.md), [`mqtt-spec.md`](docs/mqtt-spec.md), [`audio-ws-spec.md`](docs/audio-ws-spec.md), [`pfst-v2-spec.md`](docs/pfst-v2-spec.md) | the spec: bump its version line and add a Version history row | `docs-links` | -| Web app work | `web/` — start at [`web/README.md`](web/README.md) and [`web/ARCHITECTURE.md`](web/ARCHITECTURE.md) | | `web-ci`: lint, typecheck, the `check:*` suites, `next build` | +| Web app work | `web/` — start at [`web/README.md`](web/README.md) and [`web/ARCHITECTURE.md`](web/ARCHITECTURE.md) | | `web-ci`: lint, typecheck, `check:ci` (the smoke suites and the drift checks), `next build` | | A **pattern** | **not this repository.** Make it in the [Pattern Lab](https://patternflow.work/pattern-lab), press **Publish**, and it is on the [Community](https://community.patternflow.work/community) for every Patternflow to play, under the license you choose. [PATTERN_GUIDE.md](PATTERN_GUIDE.md) walks the loop. The presets bundled in the firmware and the Basics pack are curated by the maintainer from the repository's own sources | | | Parts sourcing tips, better photos of a build step, a clearer sentence: all of those are the first row. +Every pull request, whatever its row, also runs `drift`. It fails when two things the tree keeps by hand stop agreeing, the BOM and the parts table in `BUILD_GUIDE.md` for one, and when a word of the maintainer's own writing changes ([docs/rfc-cleanup.md §2.9](docs/rfc-cleanup.md#29-human-written-text-lock) names the files). + ## Sending it 1. Fork the repository and branch from `dev` (`fix/…`, `feat/…`, `docs/…`). diff --git a/docs/REPOSITORY.md b/docs/REPOSITORY.md index ea4963b7..fa6dfd3e 100644 --- a/docs/REPOSITORY.md +++ b/docs/REPOSITORY.md @@ -12,7 +12,7 @@ A map for people. (AI coding agents get the same map with more rules in [`../AGE | `docs/` | Contracts other software is built against, how the firmware is put together, walk-throughs, the audio guide (`AUDIO_GUIDE.md`), the assembly map, the legacy v2.x build guide (`legacy/`), the writing voice (`voice.md` and `voice-banned.txt`), project records (`releases/`, one file per release) and media | [`README.md`](README.md) | | `tools/` | Clients Patternflow ships that run on their own: the audio-react browser extension (also the authoring source of the device's `/audio-in` page), the Android capture app, an RTP-MIDI probe. Also `case-models/`, the scripts that make the /build page's 3D model of each case, the official one and every remix on its case switch | [`tools/README.md`](../tools/README.md) | | `integrations/` | Bridges that run inside or beside someone else's software (Ableton / Max for Live today) and talk to the panel only through the contracts in `docs/` | [`integrations/README.md`](../integrations/README.md) | -| `.github/` | Issue and pull-request templates (including the guide's **Stuck here?** form and **Share your build**), `SUPPORT.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, the CI workflows (web, every firmware composition, the core-names-no-feature checks, doc links, community folders carrying their README, console-page sync, firmware release assets, Discord dev-log) and their helper scripts | the header comment of each workflow | +| `.github/` | Issue and pull-request templates (including the guide's **Stuck here?** form and **Share your build**), `SUPPORT.md`, `CODE_OF_CONDUCT.md`, `SECURITY.md`, the CI workflows (web, every firmware composition, the core-names-no-feature checks, doc links, the drift checks, community folders carrying their README, console-page sync, firmware release assets, Discord dev-log) and their helper scripts | the header comment of each workflow | | root | `README.md`, the three guides (`BUILD_GUIDE.md`, `PATTERN_GUIDE.md`, `FEATURE_GUIDE.md`), `CHANGELOG.md`, `CONTRIBUTING.md`, `CITATION.cff`, the two license texts and the `LICENSE` pointer, `AGENTS.md` (+ `CLAUDE.md`, a one-line shim) | | The rule for where a document lives: **the root keeps three guides and no others, so a first-time visitor reads it at a glance; a contract, a reference, a walk-through for one task or a record is in `docs/`; a folder explains itself in its own README.** @@ -41,11 +41,11 @@ Filenames are lowercase with underscores. Tags are `vX.Y.Z`; the firmware source ## Files that other things link to — check before renaming -The site, the firmware and the release notes hold absolute `blob/main/…` links into this tree, and none of them is checked by CI. Before renaming or moving any of these, grep `web/src`, `web/content`, `firmware/patternflow/console` and `firmware/patternflow/features/*/*_index.h`: +The site, the firmware and the release notes hold absolute `blob/main/…` links into this tree, and a pull request that breaks one fails `check:links-absolute`. Before renaming or moving any of these, grep `web/src`, `web/content`, `firmware/patternflow/console` and `firmware/patternflow/features/*/*_index.h`: - `README.md`, `BUILD_GUIDE.md` (the site links its section anchors — §1, §2, §4, §8, §8.2 and §10 when this was written — so a renamed heading breaks them), `FEATURE_GUIDE.md`, `PATTERN_GUIDE.md` - `docs/AUDIO_GUIDE.md`, `docs/assembly/README.md`, `docs/midi-ableton.md`, `docs/midi-spec.md` (the last two are baked into the Audio edition's console pages) - `docs/EDITIONS.md`, `docs/rest-api.md`, `firmware/README.md`, `hardware/bom/bom_v3.9.csv` - `web/src/app/compliance/` — the `/compliance` URL is printed on the box. Never move, redirect or noindex it. -`.github/scripts/check_links.py` checks the *relative* links in every tracked markdown file and runs on every pull request that touches one. +`.github/scripts/check_links.py` checks the *relative* links in every tracked markdown file and runs on every pull request that touches one. `check:links-absolute` (`web/scripts/doc-map.ts --check absolute`, in `drift.yml`) checks the absolute ones on every pull request, against the tree or the tag a link names. diff --git a/web/ARCHITECTURE.md b/web/ARCHITECTURE.md index 0668203d..abd5a30e 100644 --- a/web/ARCHITECTURE.md +++ b/web/ARCHITECTURE.md @@ -118,6 +118,6 @@ PostHog (`src/providers/PostHogProvider.tsx`, event helpers in `src/lib/posthogE - Styling: Tailwind v4 (`@import "tailwindcss"` in `globals.css`) + global custom CSS; larger components use CSS Modules (`*.module.css`). - **Layering:** `lib/` is the bottom. `app/` and `components/` import from it; it never imports from them (ESLint enforces it). A type a component and a serializer share belongs in `lib/` — `lib/community/cardTypes.ts` is the precedent. - Adding a preset: add the JS file under `src/lib/presets/`, register it in `index.ts`, then generate the firmware `.h` with the Pattern Lab's **To hardware** → *Copy the conversion prompt* flow. -- Tests are the `check:*` scripts in `package.json` — bespoke smoke suites under `scripts/*-smoke.ts`, one per subsystem — plus `check:panels`, a Vitest + jsdom + Testing Library harness (`vitest.config.ts`, `test/setup.ts`) for the Pattern Lab panels, where pointer gestures, keyboard shortcuts, undo and the Director's keyframes need a DOM. Those tests live beside the panels (`panels/*.test.tsx`) and read the store's buffers rather than the canvas, which jsdom does not have. `npm run check:ci` runs every one that needs nothing a CI runner lacks; `check:module` wants the Xtensa toolchain and stays local. -- CI (`.github/workflows/web-ci.yml`) lints, typechecks (`npm run typecheck`), builds and runs `check:ci` on every PR touching `web/`. +- Tests are the `check:*` scripts in `package.json` — bespoke smoke suites under `scripts/*-smoke.ts`, one per subsystem — plus `check:panels`, a Vitest + jsdom + Testing Library harness (`vitest.config.ts`, `test/setup.ts`) for the Pattern Lab panels, where pointer gestures, keyboard shortcuts, undo and the Director's keyframes need a DOM. Those tests live beside the panels (`panels/*.test.tsx`) and read the store's buffers rather than the canvas, which jsdom does not have. `npm run check:ci` runs every one that needs nothing a CI runner lacks; `check:module` wants the Xtensa toolchain and stays local. The drift checks are `check:*` scripts too, one file each in `scripts/drift/`. Each compares two things the tree keeps by hand, the BOM and the build guide's parts table for one, and `check:drift`, the last step of `check:ci`, runs them all. +- CI (`.github/workflows/web-ci.yml`) lints, typechecks (`npm run typecheck`), builds and runs `check:ci` on every PR touching `web/`. `.github/workflows/drift.yml` runs `check:drift` on every PR, whatever it touches, and `check:human-lock` against the PR's base. It sets `PF_DRIFT_STRICT=1`: at a desk a check skips a side it cannot read (a tag the clone lacks, no `pdftotext`), and in CI that skip fails. - **Deployment weight:** Vercel meters storage per *retained deployment* — every one counts at its full output size, static files and function bundles both, with nothing shared between deployments — so size × push count is what the Hobby allowance (10 GB each) is spent on. Only `main` deploys: `vercel.json` turns deployments off for `dev`, which was three quarters of them and whose previews nobody opened. `public/` is ~90 % of a deployment's static weight (journal and build-map photos). Server functions must carry none of it: `outputFileTracingExcludes` in `next.config.ts` keeps `public/` out of every function, and a build that prints "Dynamic filesystem access causes tracing of the whole project" has found a new way in — the comment there has the history. diff --git a/web/README.md b/web/README.md index 1fc90892..546e5cb7 100644 --- a/web/README.md +++ b/web/README.md @@ -14,11 +14,13 @@ npm run dev # http://localhost:3000 Other scripts: ```bash -npm run build # production build — must pass before merging (CI hard gate) -npm run lint # eslint -npm run typecheck # tsc --noEmit -npm run check:ci # every smoke suite CI runs (check:module wants the Xtensa toolchain and stays local) -npm start # serve the production build +npm run build # production build — must pass before merging (CI hard gate) +npm run lint # eslint +npm run typecheck # tsc --noEmit +npm run check:ci # every smoke suite CI runs, then the drift checks (check:module wants the Xtensa toolchain and stays local) +npm run check:drift # the drift checks alone; check:bom, check:editions and the others run one each +npm run check:human-lock # fails if a word of the maintainer's own writing changed since the merge-base with origin/dev +npm start # serve the production build ``` Requires Node 20+ (matches CI). @@ -44,6 +46,6 @@ None are required — the site runs fully without them. The community host needs ## Contributing -See the repo-level [CONTRIBUTING.md](../CONTRIBUTING.md). Web changes go through a PR into `main`; CI runs lint, typecheck, the `check:*` suites and `next build` on anything under `web/`. +See the repo-level [CONTRIBUTING.md](../CONTRIBUTING.md). Web changes go through a PR into `main`; CI runs lint, typecheck, `check:ci` and `next build` on anything under `web/`, and the drift checks and `check:human-lock` on every pull request. Code in `web/` is MIT licensed ([LICENSE-MIT](../LICENSE-MIT)). From 3b14d6f5252b9a90697e6583b2caeb3c89770e1f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 01:28:55 +0000 Subject: [PATCH 3/3] docs: name web-ci's steps by their npm scripts in web/README Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_015BBxbLQw4YT47XGgbhCeqz --- web/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/README.md b/web/README.md index 546e5cb7..3ac0be4f 100644 --- a/web/README.md +++ b/web/README.md @@ -46,6 +46,6 @@ None are required — the site runs fully without them. The community host needs ## Contributing -See the repo-level [CONTRIBUTING.md](../CONTRIBUTING.md). Web changes go through a PR into `main`; CI runs lint, typecheck, `check:ci` and `next build` on anything under `web/`, and the drift checks and `check:human-lock` on every pull request. +See the repo-level [CONTRIBUTING.md](../CONTRIBUTING.md). Web changes go through a PR into `main`; CI runs `lint`, `typecheck`, `check:ci` and `build` on anything under `web/`, and the drift checks and `check:human-lock` on every pull request. Code in `web/` is MIT licensed ([LICENSE-MIT](../LICENSE-MIT)).