Skip to content

Droid-First Development

Most libraries document their API for humans and hope for the best when an AI assistant writes the integration code. Umpire takes a different approach: every package ships a tiny agent-facing instruction file that teaches the correct integration patterns before a single line gets written.

We call this droid-first development. The docs are for you. The rules are for your copilot.

Umpire also publishes an llms.txt file for agents that enter through the docs site instead of an installed package. It follows the llms.txt convention: a short Markdown guide that points language models at the docs, API reference, examples, package adapters, and source files most likely to answer implementation questions.

AGENTS.md is still the instruction surface for package consumers and contributors. llms.txt serves a different job: it is the front door for agents browsing the public site, keeping the important links and Umpire-specific semantics close to the top of the context.

Each published @umpire/* package includes:

  • AGENTS.md as the canonical, cross-tool instruction file
  • .claude/rules/umpire-*.md as a Claude-oriented compatibility file generated from the same source

That keeps the hints discoverable without making them long. The goal is “small and useful,” not “more docs, but for robots.”

Here is the kind of guidance @umpire/core ships:

- Create an umpire with umpire({ fields, rules }).
- Satisfaction is presence-based by default; use isEmpty
only when your domain needs a stricter empty check.
- requires checks both value satisfaction and availability.
- disables and oneOf check source values only, not
source availability.
- Rules on the same target are ANDed; use anyOf(...)
for OR logic.
- Use play(before, after) only for transition-time reset
suggestions, not on every render.

And @umpire/react:

- Use useUmpire(ump, values, conditions?) to derive
availability inside React components.
- check is derived each render; do not mirror it into
React state or recompute it in useEffect.
- fouls are transition-time recommendations from the
previous snapshot; the hook tracks that internally.

And @umpire/solid:

- Use useUmpire(ump, values, conditions?) inside Solid
components for local state.
- Use fromSolidStore(ump, { values, set, conditions? }) when
the form lives in shared store or context state.
- check() and fouls() are accessors; read them directly instead
of mirroring them into other state.

No prompt engineering required. Agents that look for AGENTS.md get the canonical file. Claude-oriented tooling still finds the compatibility rule file in the installed package.

Without droid-first rules, an AI assistant writing Umpire integration code will probably:

  1. Wrap check() in a useEffect and sync it to state
  2. Call play() on every render instead of on transitions
  3. Store availability in a useState instead of deriving it
  4. Spread the reactive proxy and defeat fine-grained signal tracking

These are the exact mistakes that are easy to make and hard to debug. The package-level agent hints prevent all four without wasting a big chunk of the consumer’s context window.

PackageCanonical fileKey guidance
@umpire/coreAGENTS.mdSatisfaction semantics, requires vs disables, check() vs play() vs challenge()
@umpire/reactAGENTS.mduseUmpire(), derived render-time availability, internal previous-snapshot tracking
@umpire/solidAGENTS.mduseUmpire(), fromSolidStore(), direct accessor reads, shared store integration
@umpire/signalsAGENTS.mdreactiveUmp(), fine-grained reads, effect() requirement for fouls
@umpire/storeAGENTS.mdStrict fromStore() contract, select() as the aggregation point
@umpire/zustandAGENTS.mdNative fromStore() fit, no manual previous-state bookkeeping
@umpire/reduxAGENTS.mdfromReduxStore(), internal previous-state tracking
@umpire/piniaAGENTS.mdfromPiniaStore(), $state snapshotting before delegation
@umpire/tanstack-storeAGENTS.mdfromTanStackStore(), previous .state snapshotting
@umpire/vuexAGENTS.mdfromVuexStore(), state snapshotting before delegation
@umpire/zodAGENTS.mdActive-schema composition and error filtering for enabled fields only
@umpire/jsonAGENTS.mdPortable rule round-tripping, named checks, excluded preservation
@umpire/readsAGENTS.mdMemoized reads, read-backed rule bridges, direct-dependency inspection
@umpire/testingAGENTS.mdStructural invariant probing with monkeyTest()
@umpire/devtoolsAGENTS.mdDev-only panel mounting, registration, reads inspection

The repo’s AGENTS.md is the canonical instruction source. CLAUDE.md and .cursor/rules/umpire.md should stay symlinked to it so Claude Code, Codex, and Cursor all receive identical guidance from a single file.

AGENTS.md ← canonical source
CLAUDE.md → symlink to AGENTS.md
.cursor/rules/umpire.md → symlink to AGENTS.md

One source of truth. Three agents. No drift.

AI assistants are going to write integration code against your library whether you guide them or not. The question is whether they’ll write it correctly.

Shipping a tiny AGENTS.md file per package is cheap. The payoff is that developers using Claude Code, Cursor, Codex, or anything else that respects AGENTS.md get correct patterns by default, without reading the docs first, without trial and error, and without burning context on a wall of prose.

The docs are still here for the humans. But the droids get first-class support too.


This page was written by Claude. 😉