CodeVetter · Repo Unpack · Contributor handoff
electron/electron
This is a Chromium-tree project, not a standalone repo: it lives at src/electron inside a Chromium checkout, builds with GN/Ninja via @electron/build-tools ('e' commands), and tests run the built binary against spec/. Follow the pinned rules: no npx, no force-push conventions, patches are fixed not recreated, C++ is Chromium-style, JS/TS is oxlint+oxfmt. Read the root CLAUDE.md and docs/development/* before editing; run lint-staged-adjacent checks (oxlint, clang-format, gen-filenames) since filenames.gni is generated.
- Read first: root CLAUDE.md (build commands, patch rules, cppgc/GC rules, PR conventions), docs/development/source-code-directory-structure.md, docs/development/creating-api.md, docs/development/testing.md, then BUILD.gn/DEPS/filenames.gni for build wiring.
- Never use npx — it can fetch arbitrary packages; run binaries via node_modules/.bin/<tool> or yarn <tool>. This applies to authored scripts too.
- Build/test loop: e init/e use/e sync/e build/e start/e test (from @electron/build-tools); inside the repo, npm test runs script/spec-runner.js and npm run lint runs the full lint chain (oxlint, oxfmt, clang-format, docs lint, gn check).
- filenames.gni and filenames.auto.gni are generated: lint-staged runs script/gen-filenames.mts on *.{js,mjs,ts,mts,d.ts} changes, and bundle.mjs --print-graph produces the module list — hand-editing them is a trap.
- Patch workflow: fix existing patches rather than adding new ones, preserve TODO(name) attribution, edit the target repo then 'e patches <target>' to export; for CI patch conflicts fetch the update-patches.patch artifact from the Apply Patches run instead of a local e sync --3.
- Spec-writing rules (spec/CLAUDE.md): never expect() inside 'data'/'message'/'exit' listeners, wait for expected output not 'exit', accumulate stdout before asserting (stderr races), and register forked children with deferKillUtilityProcess so the global afterEach reaps them.
- C++ conventions follow Chromium style enforced by clang-format (script/run-clang-format.py); cppgc/Oilpan rules are strict — cppgc::MakeGarbageCollected only, Member/WeakMember edges, no destructors touching other GC objects, GCPA-managed callbacks via WeakCellFactory.
- API changes need synchronized updates across lib/<proc>/api/module-list.ts (+module-keys), the C++ binding registration, docs/api/*.md with YAML history blocks (lint:api-history validates against docs/api-history.schema.json), docs/breaking-changes.md for removals, and regenerated electron.d.ts.
- PR conventions: body must end with a 'Notes:' consumer-facing line ('Notes: none' if internal); label semver/{none,patch,minor,major} and target/{N}-x-y backports — bug fixes to all active branches except oldest, security fixes including oldest, features main-only.
- Known traps: the renderer's Module.wrapper hack must mirror build/bundle/bundle.mjs inject entries; the 'vm' module is unusable in renderers; process.exit is remapped to app.exit; renderer node globals are deleted unless nodeIntegration/contextIsolation; unhandled rejections are set to 'warn-with-error-code' not crash.
- Safe edit boundaries: lib/ changes are self-contained TS that get bundled — run build/bundle/typecheck.mjs and oxlint; shell/ C++ changes need clang-format and a full build; spec/ changes should mirror the api-<module>-spec.ts naming convention and use spec/lib helpers. (inferred)
- CI artifacts worth knowing: audit workflows upload 'audit-results.md' unarchived (archive_download_url returns raw markdown); the Apply Patches workflow uploads 'update-patches.patch'; spec-timings.json feeds shard balancing.