Skip to content
PorabuildHeroGPUI component library
v0.14.0 · Apache-2.0

AGENTS.md

The repository layers a short root instruction file, four task guides, and scoped rules so coding agents load the right context.

AGENTS.md is the emerging convention for repository-level instructions that coding agents read before working; CLAUDE.md is Claude Code's name for the same thing. A single monolithic file scales badly — everything an agent might ever need lands in the context of every task, and the rules that actually matter drown. HeroGPUI instead layers its instructions so each task reads only what it needs.

The structure

LayerWhereRole
Root AGENTS.mdAGENTS.mdThe entry file every agent reads. Names the supported targets, before-editing rules, core commands, task guides, and component-surface synchronization contract. This page reads that file directly at build time instead of maintaining a second copy.
Task guidesdocs/agents/*.md (four files)The middle layer, loaded per task. Each guide owns one kind of work and points to the next when a task crosses boundaries.
Scoped AGENTS.md.shots/, crates/herogpui-components/, gallery/Local rules that apply only inside a subtree — audit-reader integrity for .shots, builder and test discipline for the component crate, demo and reference-metadata rules for the gallery. Each links back to the task guide that owns its topic.
Website subtreeweb/AGENTS.mdThe Next.js app carries its own warning — that its Next version has breaking changes versus an agent's training data and that the vendored docs under node_modules/next/dist/docs/ are authoritative. next dev re-adds this block; web/CLAUDE.md pulls it in with the @AGENTS.md import directive.

There is exactly one root file to maintain: the root CLAUDE.md is a symlink to AGENTS.md, so both agent tools read the identical text. The task guides are plain markdown under docs/agents/, referenced from the root file and from the scoped AGENTS.md files.

The four task guides

GuideFileOwns
Workflow and architecturedocs/agents/workflow.mdThe repository map and source hierarchy — which source owns which claim, down to the pinned React Aria / Stately versions for inherited behavior. Also the project invariants and the change-to-verification matrix below.
Component implementationdocs/agents/components.mdPinned-GPUI constraints that repeatedly produce plausible-but-wrong component code: controlled/uncontrolled semantics, keyed-state lifetimes, focus and overlay rules, and the headless behavior-test harness patterns.
Upstream contract and auditsdocs/agents/parity.mdThe pinned upstream contract and the audit suite that checks it — which audit owns which claim, what a recorded omission must look like, and audit-reader integrity (an audit must fail loudly when it cannot find its input; empty input is not a zero-gap result).
Gallery and visual verificationdocs/agents/gallery.mdHow to rebuild, smoke-test, deep-link, drive real input off-screen, and capture screenshots without stealing the user's desktop — plus the rule that a screenshot proves pixels, not behavior.

The root file names the layering explicitly: llms.txt (see llms.txt) supplements the task guides and does not replace reading the implementation and tests.

The rules, and why they exist

Preserve the dirty checkout

Rule one of the root file: run git status --short and inspect the relevant diff before editing, and treat every pre-existing modification as user-owned. This checkout routinely carries unfinished component, gallery, audit, and screenshot work in parallel with whatever an agent was asked to do. The workflow guide extends the rule: do not format, revert, regenerate, or include unrelated paths, and a read-only review does not authorize edits, builds, or test runs. The cost of ignoring it is destroying someone's in-progress work; the cost of following it is one extra command.

Pin every upstream contract

The first paragraph of the root file pins the framework targets: the Zed GPUI git revision in Cargo.toml and Cargo.lock, with Rust 1.98. Newer upstream APIs are not evidence that an API is available here. Rule three pins the design-system contract the same way: HeroUI v3.2.6 with its React Aria and Stately versions. Check llms.txt and the task guides before using an API that is not present in the checkout.

Verification matches the change

The workflow guide maps each change type to the command that iterates on it and the gate required before broad handoff — for example, a Rust logic change iterates with a focused cargo test -p herogpui-components --test <name> and finishes with the component suite, format, lint, and the relevant audits, while a documentation change verifies links and commands rather than compiling Rust. Two integrity rules close the loop: do not claim the full gate passed after running a focused test, and finish with evidence — report which checks ran, which did not, and why.

Why the file stays short

The root AGENTS.md is 163 lines. Everything else is one link away, scoped to the subtree or task that needs it. When a rule would only matter for one kind of work, it belongs in the guide for that work — not in the file every task pays for.

Adapting the pattern

The root file in full — short enough to read on every task, specific enough that an agent who stops here still avoids the two classic mistakes (clobbering the dirty checkout and using an unsupported upstream API):

AGENTS.md
# HeroGPUI agent guide

HeroGPUI is a native Rust/GPUI port of HeroUI v3.2.6. The repository targets
Rust 1.98 and the published `gpui-pre` crates pinned in `Cargo.toml`
and `Cargo.lock` at exactly `=0.3.5` — a prerelease publish of the Zed GPUI sources by
crates.io user huacnlee (Jason Lee, the gpui-kit maintainer), 0.3.5 being a
snapshot of `zed@d89e9c2`. `gpui-pre`'s
version numbers are its own; they do not track Zed release tags. The pin is
exact (`=0.3.5`), as gpui-kit's is: `gpui-pre` is a 0.x prerelease snapshot,
so a caret would let any `0.3.x` snapshot of different Zed sources resolve in
a downstream build. `.shots/package_audit.py` rejects a non-`=` requirement.

## No setup step: vanilla registry GPUI

HeroGPUI builds on the published `gpui-pre` family as-is. There is no
`[patch.crates-io]` table, no generated source tree, and no materialization
command: a fresh clone (and rust-analyzer on it) runs `cargo` directly, and
a downstream crate needs only `herogpui` itself. The retired renderer,
accessibility, and web-platform deviations live under
`docs/upstream/retired-patches/` for the upstream-PR effort only; they are
not wired into any build. Component-level replacements for them live in
`crates/herogpui-components/src/` (`a11y.rs`, `util::shift_wheel_scroll_x`,
`util::inner_fill_radius`).

Use the unpacked registry sources for API evidence —
`~/.cargo/registry/src/index.crates.io-*/gpui-pre-0.3.5/`. That is neither a
Zed git checkout, the older third-party `gpui-unofficial` republish this
replaced, nor the unrelated crates.io `gpui` 0.2.2 crate.

## Before editing

1. Run `git status --short` and inspect the relevant diff. Preserve unrelated
   work in this frequently dirty checkout.
2. Read the target implementation, its callers, and its focused tests before
   changing it. Keep fixes narrow.
3. Use HeroUI v3.2.6 and its pinned React Aria/Stately versions for parity work.
   Do not infer behavior from HeroUI v2, latest docs, or a newer GPUI checkout.
4. Read the task guide below before acting. Scoped `AGENTS.md` files under
   `.shots/`, `crates/herogpui-components/`, and `gallery/` add local rules.

## Core commands

```powershell
cargo check --workspace
cargo test -p herogpui-components
cargo fmt --all -- --check
bash .shots/lint.sh
```

`.shots/lint.sh` is the lint gate CI runs: workspace-lint inheritance, clippy
with warnings denied, and cargo-deny when installed.
For Windows shells, `.shots/lint.ps1` only forwards to `.shots/lint.sh`.

Use a focused test binary while iterating. After a component or gallery change,
build with `.shots/rebuild.ps1`; the gallery executable is often locked after a
smoke or capture run. Select gates from the workflow matrix; reserve the full
CI-shaped set for release-facing code changes or an explicit request.

## Task guides

- [Workflow and architecture](docs/agents/workflow.md) — repository map,
  source hierarchy, scope discipline, and change-to-verification matrix.
- [Component implementation](docs/agents/components.md) — pinned GPUI state,
  events, focus, overlays, layout, and behavior-test patterns.
- [Parity and audits](docs/agents/parity.md) — pinned upstream contract,
  audit selection, omission rules, and audit-reader integrity.
- [Gallery and visual verification](docs/agents/gallery.md) — rebuild, smoke,
  deep links, off-screen input, screenshots, and focus-sensitive capture.

`llms.txt` is the public component API reference. It supplements the
task guides; it does not replace reading the implementation and tests.

## Keep component surfaces in sync

When a component's public API, behavior, reference status, or gallery example
changes, update the complete affected surface in the same change:

- the implementation and focused behavior tests under
  `crates/herogpui-components/`;
- the matching Rust gallery example and `reference_metadata` entry;
- `llms.txt` when the public Rust API or behavior changed; and
- the generated website component data in `web/src/data/reference.json` and
  `web/src/data/rust-examples.json`.

Regenerate both website datasets from `web/` with `pnpm run extract`, then
verify them with `pnpm run extract:check`. Do not hand-edit generated JSON.

## The web gallery is built from this workspace

There is one copy of every component, and it needs no wasm-specific code.
`crates/herogpui-web` is an ordinary workspace member that links the
`herogpui-gallery` library (`gallery/src/lib.rs`, which `gallery/src/main.rs`
also uses) and compiles for `wasm32-unknown-unknown`, so the browser runs the
same sources the native binary does. Earlier layouts built the artifact from a
second checkout pinned to an older commit and kept the two trees in step by
hand; nothing of that survives, and no tool reports a component as behind.

The artifact is **not committed**. CI's `wasm` job builds it on every pull
request and master push, and `wasm-publish` publishes it as its own GitHub
prerelease, `gallery-<key16>`, keyed by the hash of every wasm build input
(assets are attached while it is a draft, so immutable releases are fine);
the Vercel build (`pnpm run build` in `web/`) downloads the artifact for its
own checkout and verifies its SHA-256 before `next build`. To build
it locally instead (the site build then uses the local copy):

```bash
rustup toolchain install "$(cat .shots/wasm-toolchain.txt)" --profile minimal -t wasm32-unknown-unknown
cargo install -f wasm-bindgen-cli --version <the wasm-bindgen in Cargo.lock>
# binaryen version_133's wasm-opt on PATH, or pass --no-opt
bash .shots/build-wasm.sh          # -> web/public/gallery/ (gitignored)
```

The dated nightly is required and is not a preference — `wasm_thread`, pulled
in by the GPUI web platform's `multithreaded` default feature, opens its
`lib.rs` with a `#![feature]` attribute, so stable fails with `error[E0554]`
from a dependency this repository does not own. Two facts about how, because
both are easy to get wrong:

- The switch is **not** `default-features = false` on a dependency edge of
  ours. `gpui_platform` depends on `gpui_web` with default features on, so a
  second edge from this workspace is unioned with that one and changes nothing.
  Upstream's `multithreaded` default is therefore accepted as-is, which pulls
  `wasm_thread` (a `#![feature]` crate) into the wasm32 graph: the `wasm` CI
  job builds that target on nightly, while everything native stays on
  `rust-toolchain.toml`'s pinned stable. See `docs/upstream/gpui-web-scroll-and-ime.md`.
- Nothing was using it. `crates/herogpui-web` starts the app with
  `gpui_platform::single_threaded_web()`, and the multi-threaded platform runs
  its background executors on web workers over shared wasm memory, which a
  browser grants only in a cross-origin-isolated context — and the gallery is
  served by the Vercel-hosted website (`web/DEPLOYMENT.md`), whose
  `web/next.config.ts` sends no COOP/COEP headers. Verified in a browser at
  `gpui-pre` 0.3.3, when the since-retired fork still built this target on
  stable: that single-threaded artifact booted, rendered, pressed, focused,
  took keyboard input and resolved a `background_executor().timer()`,
  indistinguishably from the nightly/`multithreaded` artifact, which was
  23 KB larger. So the default costs the nightly requirement and nothing
  else; actually starting the multi-threaded platform needs a deployment that
  can use web workers.

Never set `RUSTFLAGS` for this target —
`.cargo/config.toml` states `rustflags = []` there deliberately, and the
environment variable replaces that list rather than adding to it
(`.shots/build-wasm.sh` refuses to run with it set). Use the `wasm-bindgen`
CLI whose version matches the `wasm-bindgen` crate in `Cargo.lock`; a
mismatch fails on a descriptor schema neither side names. The script also
runs binaryen's `wasm-opt -O1` (version pinned in the script and, with its
archive digest, in CI); `-O1` is the level that shrinks the compressed
transfer as well as the raw module (`web/DEPLOYMENT.md` section 6).
The standalone gallery stays single-threaded; why cross-origin isolation for
the multi-threaded platform is not worth it is in
`docs/upstream/gpui-web-multithreaded.md`.

After a Rust change, run `pnpm run wasm:manifest` from `web/`: it records
the artifact key (`inputsSha256`) and every gallery example body in
`web/src/data/wasm-parity.json`, and `pnpm run extract:check` fails when
they have parted company with the tree. No artifact rebuild is needed for
that; CI builds and publishes the artifact for the new key.
`docs/upstream/gpui-web-scroll-and-ime.md` covers the retired source fork of
that crate (+9/-5 lines in `src/events.rs`, kept under
`docs/upstream/retired-patches/` and as a ready-to-submit patch under
`docs/upstream/prs/`) and the known web limitations vanilla accepts:
shift+wheel reaches horizontal scrollers through `util::shift_wheel_scroll_x`,
while the IME-mirror resync after paste still waits upstream.

What transfers to another repository:

  • Keep the entry file under a screenful; move per-task detail into guides it links to.
  • State the pinned dependency versions in the first paragraph; it is the cheapest defense against training-data drift.
  • Put “inspect the working tree first” in the rules if contributors (human or agent) share one checkout.
  • Add scoped AGENTS.md files only where a subtree has genuinely local rules, and have each one link back to the owning guide.
  • Symlink CLAUDE.md to AGENTS.md so the two tool conventions never diverge.