# Contributing

> Local development setup, agent guidance, release and changelog merge process, and pre-1.0 contribution expectations.

- Repository: vercel-labs/native
- GitHub: https://github.com/vercel-labs/native
- Human docs: https://grok-wiki.com/public/docs/vercel-labs-native-8ccc3580636a
- Complete Markdown: https://grok-wiki.com/public/docs/vercel-labs-native-8ccc3580636a/llms-full.txt

## Source Files

- `CONTRIBUTING.md`
- `AGENTS.md`
- `RELEASING.md`
- `scripts/changelog-merge.sh`
- `CHANGELOG.md`

---

---
title: "Contributing"
description: "Local development setup, agent guidance, release and changelog merge process, and pre-1.0 contribution expectations."
---

The toolkit repository is developed as a Zig engine plus an npm-distributed CLI (`@native-sdk/cli`). Contributors work against `main` with a path-aware local gate (`scripts/gate.sh`), ship user-visible notes as `changelog.d/` fragments rather than editing `CHANGELOG.md`, and leave releases to maintainers who bump `packages/native-sdk/package.json` and let `.github/workflows/release.yml` publish.

<Info>
Native SDK is **pre-1.0**: public APIs still move. Bug reports and focused pull requests are welcome; larger design changes should start as an issue before code.
</Info>

## Prerequisites

<table>
  <thead>
    <tr>
      <th>Requirement</th>
      <th>Use</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://ziglang.org/download/">Zig 0.16.0+</a></td>
      <td>Engine, runtime, examples, and most local checks</td>
    </tr>
    <tr>
      <td>Node.js + npm</td>
      <td>CLI package under <code>packages/native-sdk</code>, generated frontend projects</td>
    </tr>
    <tr>
      <td>pnpm</td>
      <td>Documentation site under <code>docs/</code></td>
    </tr>
    <tr>
      <td>macOS</td>
      <td>Primary host; WKWebView and Chromium/CEF development</td>
    </tr>
    <tr>
      <td>Linux + GTK4 + WebKitGTK 6</td>
      <td>System WebView path on Linux</td>
    </tr>
  </tbody>
</table>

App-author docs live at [zero-native.dev](https://zero-native.dev). This page is for changes to the toolkit repository itself.

## Repository layout

:::files
.
├── src/                 # Engine and runtime (canvas/markup under src/primitives/canvas/)
├── examples/            # Showcase apps (most: app.zon + src/ only)
├── packages/native-sdk/ # npm CLI (@native-sdk/cli) and platform packages
├── docs/                # Next.js docs site (docs/AGENTS.md for MDX conventions)
├── skills/ skill-data/  # Agent skills shipped with the CLI
├── tools/ scripts/      # Dev tooling, gate, changelog merge
├── changelog.d/         # Per-change fragments (not CHANGELOG.md)
├── CONTRIBUTING.md
├── AGENTS.md
└── RELEASING.md
:::

## Local development checks

### Core engine

```bash
zig build test                 # root engine + runtime suites
zig build validate             # sample app.zon manifest check
zig build test-example-notes   # one example suite (pattern: test-example-<name>)
```

### Web engine paths

Default development loop uses the system WebView:

```bash
zig build run-webview -Dweb-engine=system
zig build test-webview-system-link
```

Chromium on macOS (install CEF first):

```bash
native cef install
zig build run-webview -Dweb-engine=chromium
zig build test-webview-cef-smoke -Dplatform=macos -Dweb-engine=chromium
zig build test-package-cef-layout -Dplatform=macos
```

### CLI package and docs

```bash
npm --prefix packages/native-sdk run version:check
npm --prefix packages/native-sdk run scripts:check

pnpm --dir docs install --frozen-lockfile
pnpm --dir docs check
```

### Packaging and signing

```bash
zig build package
native package --target macos --manifest app.zon --assets assets --binary zig-out/lib/libnative-sdk.a
zig build test-package-signing   # ad-hoc codesign survival (macOS; skips loudly without codesign)
```

For Chromium packages, set `.web_engine = "chromium"` and `.cef` in `app.zon`, or pass temporary `--web-engine` / `--cef-dir` overrides.

### Automation development

```bash
zig build run-webview -Dautomation=true
native automate wait
native automate list
native automate bridge '{"id":"ping","command":"native.ping","payload":null}'
```

Automation artifacts land under `.zig-cache/native-sdk-automation`.

## Local gate

`scripts/gate.sh` is the required pre-PR check. It maps your diff (including untracked files) against a base ref (default `main`) to the suites that cover it, runs every step even after a failure, and exits non-zero if any step failed.

```bash
scripts/gate.sh fast           # affected-only: root suites + touched examples
scripts/gate.sh full           # CI-shaped local suite set
scripts/gate.sh full --all     # force docs check even if docs/ unchanged
scripts/gate.sh full --perf    # add macOS GPU percentile perf check (slow, load-sensitive)
```

### Path → suite mapping (`fast`)

<table>
  <thead>
    <tr>
      <th>Changed paths</th>
      <th>What runs</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code>src/**</code>, <code>build.zig</code>, <code>build.zig.zon</code>, <code>build/**</code>, <code>tools/**</code>, <code>tests/**</code>, <code>assets/**</code></td>
      <td>Framework change: root suites + <strong>all</strong> example suites</td>
    </tr>
    <tr>
      <td><code>src/platform/macos/**</code></td>
      <td>Above, plus Chromium host link (<code>cef-host-link</code>) on macOS when CEF is present</td>
    </tr>
    <tr>
      <td><code>examples/&lt;name&gt;/**</code></td>
      <td>That example only (<code>test-example-&lt;name&gt;</code>; mobile group → <code>test-examples-mobile</code>)</td>
    </tr>
    <tr>
      <td><code>docs/**</code></td>
      <td>Docs-only diffs: only <code>pnpm --dir docs check</code></td>
    </tr>
    <tr>
      <td>Other (README, <code>.github</code>, packages, scripts, skills, <code>changelog.d</code>, …)</td>
      <td>Root suites only</td>
    </tr>
  </tbody>
</table>

`full` always runs root test + validate, every example suite, Chromium host link, macOS automation smokes (when on macOS), markup check over example markup, and the docs check when `docs/` changed or `--all` is set.

### Render-benchmark ratchet

Framework diffs on macOS also run `zig build bench-render -Doptimize=ReleaseFast -- --check tools/bench-render-budgets.txt` by default. Skip only when the machine is too noisy:

```bash
NATIVE_SDK_SKIP_BENCH_CHECK=1 scripts/gate.sh fast
```

## Agent and maintainer conventions

`AGENTS.md` is the short operating contract for humans and coding agents:

- Run `scripts/gate.sh fast` before finishing a change.
- Do **not** edit `CHANGELOG.md` for day-to-day work; use `changelog.d/` fragments for user-visible changes. Internal-only polish needs no fragment.
- Update pinned goldens (pixel signatures, schema fingerprints, command counts) deliberately: review the rendered or counted output first; keep the pin’s comment a self-contained description of the value.
- Docs MDX tables use HTML `<table>` syntax (`docs/AGENTS.md`), not markdown pipe tables.

Skills the CLI ships live under `skills/` and `skill-data/` (`native skills list`).

## Changelog fragments

Concurrent branches do not edit `CHANGELOG.md`. Each user-visible change adds `changelog.d/<slug>.md`.

### Fragment format

<table>
  <thead>
    <tr>
      <th>Rule</th>
      <th>Detail</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>First line</td>
      <td>Tag + text: <code>feature:</code>, <code>improvement:</code>, or <code>fix:</code> followed by the bullet body</td>
    </tr>
    <tr>
      <td>Further lines</td>
      <td>Extra bullets (prefer <code>- </code> prefix; bare lines get <code>- </code> prefixed on merge)</td>
    </tr>
    <tr>
      <td>One tag per file</td>
      <td>Multi-section changes ship multiple fragments</td>
    </tr>
    <tr>
      <td>Voice</td>
      <td>Bold lead-in, then the story; one line per bullet (never hard-wrap)</td>
    </tr>
  </tbody>
</table>

Tag → section mapping:

| Tag | `CHANGELOG.md` section |
| --- | --- |
| `feature:` | `### New Features` |
| `improvement:` | `### Improvements` |
| `fix:` | `### Bug Fixes` |

Example fragment:

```markdown
improvement: **Faster frobnication**: the frobnicator now memoizes per-frame, cutting rebuild time ~40% on the kanban example.
- **Frobnication telemetry**: automation snapshots report `frob_cache_hits=`.
```

### Merge script

```bash
scripts/changelog-merge.sh
```

Behavior:

1. Reads every `changelog.d/*.md` except `changelog.d/README.md`.
2. Appends bullets to the end of the matching section under `## Unreleased` (creates missing sections in order: New Features, Improvements, Bug Fixes; creates `## Unreleased` if absent).
3. Deletes merged fragments.
4. **Fails loudly** on unknown tags or malformed first lines (silent drops are not allowed).

Merge is typically run during release prep, not on every PR.

## Pull requests

<Steps>
  <Step title="Branch from main">
    Fork if you lack push access. Keep the change focused.
  </Step>
  <Step title="Implement and gate">
    Run `scripts/gate.sh fast` (or `full` for broad/framework work). Fix failures before opening the PR.
  </Step>
  <Step title="Changelog fragment">
    If the change is user-visible, add `changelog.d/<slug>.md`. Do not edit `CHANGELOG.md`.
  </Step>
  <Step title="Open the PR">
    Target `main`. Describe what changed and why. For larger design shifts, open an issue first.
  </Step>
  <Step title="Signed commits">
    Commits must be cryptographically signed (`git commit -S` or `commit.gpgsign = true`) so GitHub shows **Verified**. The `Signed-off-by` trailer from `git commit -s` is a DCO attestation, not a signature.
  </Step>
</Steps>

CI (`.github/workflows/ci.yml`) exercises Zig core, platform WebView link/smoke paths, package signing on macOS, GPU smokes/perf jobs, and related matrix work. Local `gate.sh full` is the closest self-service mirror.

## Pre-1.0 contribution expectations

- Prefer **small, focused PRs** with clear verification (gate suites, example tests, or a concrete repro for bugs).
- Treat mobile (iOS/Android) surfaces as experimental; desktop hosts are the mature contribution surface.
- Do not treat public APIs as frozen; match existing patterns in `src/`, examples, and skills rather than inventing parallel APIs.
- Maintainers control release voice and timing; contributors ship fragments, not release branches, unless asked.

## Releases (maintainers)

Releases are **manual, single-PR** events. The maintainer owns changelog voice and format.

### Prepare a release PR

1. Branch (for example `prepare-v1.2.0`).
2. Bump the version in `packages/native-sdk/package.json` (source of truth).
3. Run `npm --prefix packages/native-sdk run version:sync` — stamps the version into `tools/native-sdk/main.zig`, every package under `packages/native-sdk/npm/*/package.json`, and `@native-sdk/cli` `optionalDependencies` pins.
4. Run `scripts/changelog-merge.sh` to fold pending `changelog.d/` fragments into `## Unreleased`.
5. Write the release entry in `CHANGELOG.md`, wrapped in `<!-- release:start -->` … `<!-- release:end -->`.
6. Remove those markers from the **previous** release entry; only the latest release keeps them.
7. Open a PR and merge to `main`.

### What CI does after merge

On push to `main`, `.github/workflows/release.yml`:

1. Compares `packages/native-sdk/package.json` version to npm `@native-sdk/cli`.
2. If the version is **new**: cross-builds CLI binaries for all platforms, creates the GitHub release (`v$VERSION`) using notes between the release markers, publishes the eight `@native-sdk/cli-*` platform packages under `packages/native-sdk/npm/*`, then publishes `@native-sdk/cli` last so the main package only lands after every binary it pins is live.
3. If npm already has the version but the GitHub release is missing assets: recreates/updates the GitHub release from the marked changelog entry.

Publishing uses **npm trusted publishing (OIDC)** — no npm token secret. Each of the nine packages must have a trusted publisher on npmjs.com for repository `vercel-labs/zero-native`, workflow `release.yml`, environment `Release`. Publishes run with `--provenance`. Missing trusted-publisher configuration fails with an OIDC authentication error for that package.

Verify version pins before release:

```bash
npm --prefix packages/native-sdk run version:check
```

## Related pages

<CardGroup>
  <Card title="Testing" href="/testing">
    Full-loop UI tests, headless truth drivers, and frame-level verification.
  </Card>
  <Card title="Testing in CI" href="/testing-ci">
    CI workflows, gate scripts, platform truth drivers, and eval harness wiring.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    native init, dev, check, test, build, automate, doctor, and skills commands.
  </Card>
  <Card title="Agent skills" href="/agent-skills">
    Shipped skill packs and how agents discover them via the CLI.
  </Card>
  <Card title="Packaging" href="/packaging">
    ReleaseFast binaries to distributable app bundles and asset layout.
  </Card>
  <Card title="Updates and distribution" href="/updates-distribution">
    npm multi-platform package layout, version sync, and binary distribution.
  </Card>
  <Card title="Code signing" href="/code-signing">
    Codesign tooling and signing constraints for package pipelines.
  </Card>
  <Card title="Overview" href="/overview">
    What the SDK exposes and the path from install to a running window.
  </Card>
</CardGroup>
