---
title: Native coding
description: Use Hay’s coding runtime from a plugin, with scope and verification visible in chat.
group: Build
---

# Native coding

The selected model supplies inference. Hay owns source reads, the isolated checkout, file edits, command execution, verification, and Git operations. Connect a repository from Hay’s chat composer; plugins cannot select arbitrary filesystem roots.

## Propose a scoped change

```js
const draft = await Hay.coding.createSpec({
  title: 'Improve the empty state',
  goal: 'Explain how to start when the list is empty.',
  acceptance: 'The empty list shows useful next steps.',
  decisions: 'Use existing components and styles.',
  paths: ['src/EmptyState.tsx', 'tests/empty-state.test.tsx'],
  commands: ['npm test', 'npm run build']
});
await Hay.coding.run(draft.id, draft.specRevision);
```

A spec has six required fields: `title`, `goal`, `acceptance`, `decisions`, `paths`, and `commands`. Paths and commands are immutable after creation. Commands are direct argument vectors parsed by Hay, not arbitrary shell scripts. Execution needs `coding.run`, `models.invoke`, and review of the exact scope.

## Watch real progress

```js
const off = Hay.coding.onChange(async change => {
  if (change.jobId !== draft.id) return;
  const job = await Hay.coding.get(draft.id);
  renderStatus(job.status, job.events, job.checks);
});
const finished = await Hay.coding.wait(draft.id);
```

`coding.get`, `list`, and `review` need `coding.read`. `coding.wait` needs `coding.run`; it waits asynchronously while chat remains available. Stop cancels the run and preserves the checkout. Long healthy jobs do not share a whole-run ten- or fifteen-minute deadline; model calls and tool operations retain their own limits.

`coding.project()` returns the connected project and a bounded file inventory. `coding.readFile(path)` reads bounded planning context, refusing ignored, private, and symlinked files.

## Test built output in the browser

After all approved checks pass on the final revision:

```js
const page = await Hay.coding.preview(draft.id);
const observed = await Hay.browser.observe(page.sessionId);
```

Preview needs `coding.read` and `browser.control`. It serves approved built web output on loopback in a temporary browser session. It has no native bridge or saved account sign-ins. Hay renderer previews use labelled sample data; they cannot establish native backend behavior.

Use fresh observations and reviewed browser actions to test the actual page. Source maps, private files, non-web content and symlinks are not served.

## Review, commit, and apply

```js
const reviewed = await Hay.coding.review(draft.id);
// Only after the user reviews the complete current diff:
await Hay.coding.commit(draft.id, reviewed.diff.revision,
  'feat: explain how to start an empty list');
await Hay.coding.integrate(draft.id);
```

Commit and integrate need `coding.commit` and separate trusted reviews. Every approved check must pass on the exact diff revision. Truncated, stale, and out-of-scope diffs are refused. The user sees a concise change card in chat and opens the full diff in the side panel.

A commit is made on the task branch. Integration fast-forwards a clean original repository still at the recorded base. It does not push, overwrite dirty work, or silently resolve conflicts.

## Runtime boundaries

Native execution currently needs the macOS sandbox. The worker has scoped writes, loopback-only networking, read-only existing dependencies, and no provider credentials or direct Git writes. Dependency downloads and unsandboxed fallback execution are unavailable.

Use `coding.stop(id)` to stop owned work and unsubscribe listeners when disposing the view. Interrupted checkouts are retained; closing and reopening Hay does not automatically replay a coding run.
