From 572d772b8aa5e372aa455261280f6843d9882da5 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 08:13:19 +0000 Subject: [PATCH 1/2] docs: instruct Claude Code to attach CI labels when opening PRs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .github/workflows/check.yml's pull_request trigger fires only on opened, synchronize and reopened — not labeled. Several jobs are gated on labels being present in that triggering event's payload (host, Apple 🍎, Android 🤖, MacOS 💻, Ferric 🦀, weak-node-api), so a label attached after PR creation (the only way the create_pull_request MCP tool allows, since it has no labels parameter) never actually triggers those jobs without a follow-up push. Adds .claude/CLAUDE.md documenting the label -> job mapping and the attach-then-push-again workflow, prompted by PR #434 needing labels added and retriggered after the fact. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm --- .claude/CLAUDE.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .claude/CLAUDE.md diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 00000000..021918c7 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1,55 @@ +# CLAUDE.md (`.claude/`) + +Guidance specific to Claude Code sessions that open pull requests against this +repository (including automated/scheduled sessions). See the root `CLAUDE.md` +and `AGENTS.md` for everything else. + +## Attach CI labels when you open a PR + +`.github/workflows/check.yml`'s `pull_request` trigger only fires on +`opened`, `synchronize` and `reopened` — **not** `labeled`. Several of its +jobs are gated behind a label check evaluated against that triggering +event's payload, e.g.: + +```yaml +if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/next' || contains(github.event.pull_request.labels.*.name, 'host') +``` + +A label added after the PR is already open does nothing on its own — there +is no new `opened`/`synchronize`/`reopened` event for the workflow to +re-evaluate against, so the gated job silently never runs, and that gap is +easy to miss since the Check run still shows green (the job wasn't +skipped-and-failed, it just never triggered). + +The `create_pull_request` GitHub MCP tool has no `labels` parameter, so +labels can only be attached in a follow-up call after the PR exists — which +is exactly the case above. **Whenever you open a PR here:** + +1. Decide which of the labels below apply, based on what the diff touches. +2. Attach them immediately after creating the PR (e.g. `issue_write` with + `method: "update"`, or the equivalent `gh pr edit --add-label`). +3. Push one more commit to the branch — even a trivial or `--allow-empty` + one — so a `synchronize` event fires and the gated jobs actually run with + the labels now present. Labeling without this step means the relevant CI + never runs before merge. + +## Label → job map + +| Label | Gated job(s) | Attach when the diff touches | +| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `host` | Host C++ tests | `packages/host/cpp/`, `packages/host/android/CMakeLists.txt`, the generated injector, or anything in `packages/weak-node-api` that the host links against | +| `Apple 🍎` | iOS test app build/run | iOS/macOS/tvOS/visionOS build config, XCFrameworks, Cocoapods, Xcode project files, or any change to Node-API behavior addons rely on (buffers, fatal-error handling, etc.) that device tests would catch | +| `Android 🤖` | Android test app build/run (self-hosted runner; currently label-gated even on `main`/`next`, see the `if:` comment in `check.yml` — check whether that's still true before relying on it) | Gradle, NDK, Android SDK, `.android.node` packaging, or the same cross-platform Node-API behavior changes as above | +| `MacOS 💻` | macOS test app | `react-native-macos`-specific code paths | +| `Ferric 🦀` | Ferric Apple triplets build | `packages/ferric`, `packages/ferric-example`, or anything napi-rs/Cargo related | +| `weak-node-api` | weak-node-api tests | `packages/weak-node-api` | + +A PR can need more than one label — e.g. a change to `RuntimeNodeApi.cpp` +that alters buffer semantics warrants `host` plus `Apple 🍎` and +`Android 🤖`, since the actual behavior change can only be verified on a +real device. + +When in doubt, prefer attaching a label and letting the job run (mirroring +what the label's own description says on GitHub) over guessing it isn't +needed — a job that runs and passes costs a bit of CI time; a real device +regression that ships because the label was skipped costs much more. From 38c3c61731116c74e9277a14d94948119d1ec07b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 08:18:32 +0000 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20correct=20the=20host=20label=20name?= =?UTF-8?q?=20to=20"Host=20=F0=9F=8F=A1"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The label table and example condition used plain "host", copied from .github/workflows/check.yml's host-cpp-tests job at the time. That job's condition was itself wrong — the repository's real label is "Host 🏡" (see issues #428/#420/#412) — confirmed and fixed on #434's branch. Update this doc to match, and note that a label condition needs to be checked against the real, existing label rather than trusted at face value. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01DaK9eAAF5G8wj6UT8VekAm --- .claude/CLAUDE.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 021918c7..173babe3 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -12,7 +12,7 @@ jobs are gated behind a label check evaluated against that triggering event's payload, e.g.: ```yaml -if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/next' || contains(github.event.pull_request.labels.*.name, 'host') +if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/next' || contains(github.event.pull_request.labels.*.name, 'Host 🏡') ``` A label added after the PR is already open does nothing on its own — there @@ -21,6 +21,14 @@ re-evaluate against, so the gated job silently never runs, and that gap is easy to miss since the Check run still shows green (the job wasn't skipped-and-failed, it just never triggered). +The label name in the `if:` condition has to match a real, currently-existing +GitHub label exactly (name and emoji). `host-cpp-tests` checked for a label +literally named `host` for a while, when the repository's real label was +`Host 🏡` — the condition never matched anything anyone would actually apply, +so the job silently only ran on pushes to `main`/`next`. Confirm the label +exists (e.g. via the GitHub MCP `get_label` tool) before trusting a condition +or table like the one below. + The `create_pull_request` GitHub MCP tool has no `labels` parameter, so labels can only be attached in a follow-up call after the PR exists — which is exactly the case above. **Whenever you open a PR here:** @@ -37,7 +45,7 @@ is exactly the case above. **Whenever you open a PR here:** | Label | Gated job(s) | Attach when the diff touches | | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `host` | Host C++ tests | `packages/host/cpp/`, `packages/host/android/CMakeLists.txt`, the generated injector, or anything in `packages/weak-node-api` that the host links against | +| `Host 🏡` | Host C++ tests | `packages/host/cpp/`, `packages/host/android/CMakeLists.txt`, the generated injector, or anything in `packages/weak-node-api` that the host links against | | `Apple 🍎` | iOS test app build/run | iOS/macOS/tvOS/visionOS build config, XCFrameworks, Cocoapods, Xcode project files, or any change to Node-API behavior addons rely on (buffers, fatal-error handling, etc.) that device tests would catch | | `Android 🤖` | Android test app build/run (self-hosted runner; currently label-gated even on `main`/`next`, see the `if:` comment in `check.yml` — check whether that's still true before relying on it) | Gradle, NDK, Android SDK, `.android.node` packaging, or the same cross-platform Node-API behavior changes as above | | `MacOS 💻` | macOS test app | `react-native-macos`-specific code paths | @@ -45,7 +53,7 @@ is exactly the case above. **Whenever you open a PR here:** | `weak-node-api` | weak-node-api tests | `packages/weak-node-api` | A PR can need more than one label — e.g. a change to `RuntimeNodeApi.cpp` -that alters buffer semantics warrants `host` plus `Apple 🍎` and +that alters buffer semantics warrants `Host 🏡` plus `Apple 🍎` and `Android 🤖`, since the actual behavior change can only be verified on a real device.