# Best Practices for Writing Stable Site Skills in ego-lite

> Write stable site skills for ego-lite using best practices like fixed folders, stable selectors, and URL validation. Create resilient learning packs that avoid browser update issues.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: best-practices
- Published: 2026-07-27

---

**Follow the fixed folder layout, use stable selectors and URLs, avoid pixel coordinates and secrets, and validate with `npm run validate:site-skills` to create resilient site learning packs that withstand browser updates and layout changes.**

The **ego-browser** harness drives the closed-source *ego lite* browser, enabling AI agents to interact with web pages through reusable **site skills** (also called **learnings**). These learning packs, stored in `skills/ego-browser/learnings/<site>/`, serve as reusable maps that agents use to navigate and manipulate websites programmatically. Writing **stable site skills** requires strict adherence to structural conventions and hard constraints defined in the [`CONTRIBUTING.md`](https://github.com/citrolabs/ego-lite/blob/main/CONTRIBUTING.md) guide to ensure reliability across browser versions and UI redesigns.

## Understanding the Site Learning Pack Structure

Every site learning pack follows a rigid directory convention that the runtime discovers automatically. This uniformity enables validation, testing, and consistent agent behavior.

### Fixed Folder Layout Convention

Each learning pack must live under `skills/ego-browser/learnings/<site>/` and contain exactly four artifact types:

- [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) – Declares domains, tools, and schemas
- `notes/*.md` – Human-readable documentation  
- `tools/*.js` – Node-side utilities executed in the CLI process
- `browser-tools/*.js` – Scripts injected into the browser context

The runtime loader ([`src/learning/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/index.ts)) discovers packs by this specific shape. The validator ([`src/learning/validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/validate-learning-format.ts)) relies on this layout to locate and check every artifact automatically. Deviating from this structure causes the validation step to fail before the skill can load.

### The Manifest as Single Source of Truth

The [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) file defines the contract between the agent and the site. It must declare `id`, `name`, `domains`, `notes`, `nodeTools`, and `browserTools`. Every tool requires explicit parameter schemas with `type`, `required`, and `description` fields.

This manifest powers the `runSiteTool` and `runSiteBrowserTool` helpers. Proper schemas enable automatic validation and generate actionable error messages when agents invoke tools incorrectly. Refer to the [x-com manifest.json](https://github.com/citrolabs/ego-lite/blob/main/skills/ego-browser/learnings/x-com/manifest.json) for a production example.

## Hard Constraints for Stable Site Skills

The [`CONTRIBUTING.md`](https://github.com/citrolabs/ego-lite/blob/main/CONTRIBUTING.md) document defines non-negotiable constraints that prevent fragility and security vulnerabilities.

### Stable URLs and Selectors Only

**Stable site skills** rely exclusively on canonical URLs and resilient DOM selectors. Use CSS selectors targeting ARIA roles, data-testid attributes, or semantic HTML rather than generated class names or positional XPath expressions.

Selectors depending on dynamic IDs or auto-generated classes break whenever the site redeploys, causing flaky automation. Stable selectors make learning packs resilient to minor UI redesigns and enable faster re-snapshotting when layouts change.

### No Pixel Coordinates or Geometry-Based Actions

Never embed absolute pixel coordinates or geometry-based click coordinates in tools. Pixel-based actions fail when DPI settings, window sizes, or responsive layouts change. The ego-lite runtime provides logical element references (`loc=` or `@N` syntax) that adapt automatically to viewport variations.

### Security: No Hard-Coded Secrets

Do not hard-code API keys, cookies, session tokens, or credentials in tool scripts or markdown notes. Hard-coded secrets expose sensitive data to any agent loading the skill, violating security policies and potentially leaking private user information across sessions.

## Designing for Reusability and Maintenance

Stable site skills function as reusable site maps rather than brittle, task-specific scripts.

### Capture Site Shape, Not Specific Tasks

Write notes and tools that describe **how the site is built**—documenting DOM hierarchies, ARIA roles, and data-testid attributes—rather than encoding a single user workflow. This approach creates a composable "map" that agents can use to construct varied task flows dynamically.

The `help()` function exposed by agents parses JSDoc and markdown notes at runtime. Comprehensive documentation in `notes/*.md` files ensures that `help()` reflects the actual DOM structure, enabling agents to reason about page state correctly.

### Naming and API Consistency

Use camelCase for public helper functions and lead with verbs for asynchronous actions (e.g., `runSiteTool`, `ensureSession`). This convention aligns with the helper surface defined in [`src/helpers.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/helpers.ts), making the API discoverable and predictable across different site learning packs.

## Validation and Testing

Automated checks prevent broken skills from reaching production.

### Automated Validation

Run `npm run validate:site-skills` after modifying any learning pack. This command invokes the validator ([`src/learning/validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/validate-learning-format.ts)) to check manifest syntax, required fields, and file existence. The validator guarantees that the learning pack can be loaded by the runtime before code review.

### Behavior Testing with Stubs

Add at least one behavior test in [`test/site-skills.test.js`](https://github.com/citrolabs/ego-lite/blob/main/test/site-skills.test.js) or similar files. Use `__testing.setOverrides()` or a `FakeEgo` double to stub Chrome DevTools Protocol (CDP) calls, isolating tests from live browser dependencies.

Testing protects against regressions when the runtime evolves, particularly when snapshot formats or helper implementations change. Stubbing CDP calls ensures tests remain fast and deterministic while verifying tool logic.

## Summary

- **Structure**: Maintain the fixed folder layout ([`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json), `notes/`, `tools/`, `browser-tools/`) so the runtime and validator can discover your pack.
- **Selectors**: Use stable CSS selectors based on ARIA roles or data attributes; avoid generated classes and pixel coordinates.
- **Security**: Never commit API keys, cookies, or tokens to learning pack files.
- **Documentation**: Write notes that capture site architecture (DOM shape) rather than single workflows to maximize reusability.
- **Quality Gates**: Run `npm run validate:site-skills` and add behavior tests using `__testing.setOverrides()` before submitting changes.

## Frequently Asked Questions

### What is the required folder structure for a site learning pack?

Every pack must reside under `skills/ego-browser/learnings/<site>/` and contain [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json), `notes/*.md`, `tools/*.js`, and `browser-tools/*.js`. The runtime loader ([`src/learning/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/index.ts)) discovers packs by this specific shape, and the validator ([`src/learning/validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/validate-learning-format.ts)) enforces it strictly.

### Why should I avoid using generated CSS class names in selectors?

Generated class names (e.g., `div.css-1a2b3c`) change on every deployment or build, causing tools to fail when the site updates. Stable selectors using ARIA roles, data-testid attributes, or semantic HTML remain valid across UI redesigns, ensuring your **stable site skills** continue functioning.

### How do I test site skills without a live browser?

Use the `__testing.setOverrides()` API or a `FakeEgo` test double to stub CDP calls in your test files. This approach isolates logic from browser state, allowing fast, deterministic validation of tool behavior without requiring a live Chrome instance.

### Where should I document the DOM structure for a site?

Document site architecture in `notes/*.md` files within the learning pack. These markdown files serve as the knowledge base for the `help()` function and future contributors. Keep them synchronized with the actual DOM implementation to ensure agents receive accurate guidance when parsing page structure.