CONTRIBUTING.md
Contributing to Emc²
For people and AI contributors. Project map, change rules and mail privacy rules are in AGENTS.md. Report vulnerabilities privately via SECURITY.md, never in public issues.
Coordinate before starting
- Find the canonical upstream from metadata and remotes; a fork's
originmay not be it. If you cannot inspect upstream, stop and report the gap. - Search open and closed issues and PRs by problem and area. Read comments, assignees, linked PRs and changed files.
- Reuse an active item if its owner agrees. Do not reopen rejected work.
- Record each new task in an issue first. A small isolated docs fix may use its PR as the record.
- In that one issue or PR, record scope, owned paths, base and feature branch, owner, linked PR, required checks and status.
- Before editing, resuming or pushing, confirm the current owner. Get the owner's or a maintainer's agreement before overlapping or taking over. Record splits, pauses, transfers and releases. Silence or staleness never transfers ownership. If claims conflict, stop for a maintainer.
- One task per feature branch and worktree, one delivery owner, one writer per branch. Helpers work on non-overlapping scopes and return evidence. A human owner is accountable for agent output.
Issues and labels
Use the templates in .github/ISSUE_TEMPLATE/ and apply one type label:
| Label | Use for |
|---|---|
bug | Broken behavior |
feature | A new capability |
enhancement | Improvement to existing behavior |
documentation | Documentation-only changes |
chore | CI, dependencies, releases, maintenance |
Set up
Node 24.22.1 or a newer 24.x (see .nvmrc) and npm. From the repository root:
| 1 | npm ci |
| 2 | npm run dev # Electron with React/CSS hot reload; restart after desktop or server changes |
npm run dev:web (optional browser target) reads .env copied from
.env.example; never commit it. Open the session link the mail
server prints at startup (it changes on every launch); /api refuses requests
without it. Other environment variables
(Jev/TYPESAFE_*, MAIL_*) are described in README.md and
docs/KB.md.
Branches and commits
- Branch from current
mainas<type>/<topic>, usingfeat,fix,docs,choreortest. - Imperative commit subjects;
feat:,fix:ortest:prefixes are common. - Never start a subject with
Release; it is reserved for manual version commits. - Check author/committer metadata for private details before pushing.
Verify
| Change | Run |
|---|---|
| Application | npm test and npm run build (build = strict tsc; there is no lint script) |
| MCP | Also node scripts/check-mcp.mjs (isolated handshake and access check; not live delivery) |
| Electron, preload, storage, packaging | Also npm run check:desktop with an isolated profile per docs/KB.md; require startup result and exit |
| Native Windows or macOS | Startup on the affected OS and architecture (macOS needs desktop and Keychain access) |
Android (android/) | Bounded unit tests, lint, APK build and synthetic Device Hub smoke per docs/android.md; desktop checks still apply to shared/root application changes |
| Documentation only | Relative links, command names, factual claims, git diff --check |
- Tests use
tsx --testoverserver/,desktop/,src/andscripts/test files.npm testruns files serially so mounted UI checks do not compete for CPU; individual test and CI step deadlines still apply. Run one file withnode --import tsx --test <file>. - There is no
scripts/documentation.py; do not invent doc commands. - Report live sign-in, mailbox changes, sending, interactive UI and downloaded-app acceptance separately from automated results.
- Follow the live-browser-testing restriction in PRODUCT.md.
Bounded local verification
On shared Linux hosts, the following wrapper is required for tests and builds.
Set FLEET_BIN to the verified Fleet executable supplied by the host owner.
Do not silently install a runner or run without containment when it is missing.
| 1 | test -x "$FLEET_BIN" || exit 125 |
| 2 | "$FLEET_BIN" verification bounded --memory-mib 2048 --timeout 120 -- \ |
| 3 | env NODE_OPTIONS=--max-old-space-size=1024 \ |
| 4 | node --import tsx --test src/snooze.test.tsx |
| 5 | "$FLEET_BIN" verification bounded --memory-mib 3072 --timeout 180 -- \ |
| 6 | env NODE_OPTIONS=--max-old-space-size=2048 npm run build |
Replace the test path with the affected file or add --test-name-pattern
before it. Run files serially. For a required full-suite check, use a bounded
serial run after checking existing evidence:
| 1 | "$FLEET_BIN" verification bounded --memory-mib 2048 --timeout 900 -- \ |
| 2 | env NODE_OPTIONS=--max-old-space-size=1024 \ |
| 3 | node --import tsx --test --test-concurrency=1 --test-timeout=120000 \ |
| 4 | server/*.test.ts desktop/*.test.ts src/*.test.tsx scripts/*.test.mjs |
The wrapper sets the cgroup memory limit, disables swap, applies an external deadline and cleans up descendants. Exit 125 means containment or cleanup failed. Stop and report it. After a failed test, preserve its first log, diagnose the first failure and run only the affected check after one bounded fix. Never rerun a runaway mounted test unchanged. A new session or worktree does not invalidate matching evidence.
Record command, exit status, source/input hashes and log path. Native checks also need OS, architecture, artifact digest, deadline and process cleanup. Fleet's Linux command is not a macOS or Windows runner; use the platform's reviewed bounded lifecycle. Keep npm scripts portable for the CI OS matrix. The journey map identifies focused checks; the installed smoke recipe defines acceptance beyond them.
CI and releases
- Build installers and release is the only
workflow. It runs only on an explicit manual dispatch selecting an existing
stable
v<major>.<minor>.<patch>tag. The tag must resolve to the run commit and match the source version. Creating or pushing the tag does not run it. - Pull requests, merge groups, branch pushes and schedules start no workflows. Automatic patch releases, scheduled CodeQL, dependency review, dependency auto-merge and Dependabot update configuration are removed. Review dependency advisories, licenses and updates manually.
- An authorized manual release runs actionlint, tests, strict builds and installer creation for Windows x64, macOS arm64, macOS x64 and Linux x64. Windows and macOS also run isolated startup checks. Every job uses a GitHub-hosted runner; macOS x64 runs on the native Intel image and keeps its x64 Node guard. See ADR 0063.
- Publishing waits for every platform job. It validates the complete installer and updater manifest set, rejects artifact digest mismatches, generates checksums and publishes only against the selected existing tag. Tags cannot be moved or deleted. This workflow never creates a tag or version commit.
- Repository protections and Actions enablement are separate settings. Deleting
workflows does not remove required checks, merge-queue rules or approvals.
A maintainer must reconcile any requirements for removed CI before merging;
do not fabricate passing statuses or bypass protections. Direct pushes,
force pushes and deleting
mainremain prohibited. - Local verification in the table above and independent review remain required unless the task owner explicitly prohibits execution. Report such checks as unrun. Do not dispatch Actions as a substitute without authorization.
- Workflow changes normally use actionlint 1.7.12 and the release/workflow source tests locally. PR creation does not authorize hosted execution or publishing.
- Use Node 24.22.1 (
.nvmrc;nvm usepicks it up) for local builds and tests. - Commits must use your GitHub noreply email. Enable GitHub's Keep my email
addresses private and Block command line pushes that expose my email,
then set
git config user.email <id>+<login>@users.noreply.github.com. Before pushing, runnode scripts/check-commit-emails.mjs origin/main..HEAD. Commit privacy is no longer checked automatically by a PR workflow. - Local packaging:
npm run package(Windows NSIS),npm run package:mac(DMG and ZIP),npm run package:mac:dir(app bundle, source-install route). Mac builds need Xcode Command Line Tools. Output goes torelease/. npm run releasebuilds and publishes the Windows installer locally. It and manual workflow dispatch require explicit release authorization.- Never commit generated output or local app data.
Pull requests
- Fill in the PR template: task record, ownership, verification and review.
- Explain the problem, resulting behavior and checks; include repro steps for bugs and list remaining proof gaps.
- Keep the diff in the agreed scope; use synthetic data in captures.
- Before merge, a reviewer other than the author reviews the current base, head and full diff. Required checks and approvals pass; blocking feedback is resolved. Assignment is not approval or merge authority.