# How to Create and Validate Site-Specific Learnings with manifest.json in ego-lite

> Learn to create and validate site-specific learnings using manifest.json in ego-lite. Follow our guide to define and enforce schema compliance for your custom learnings.

- Repository: [CitroLabs/ego-lite](https://github.com/citrolabs/ego-lite)
- Tags: how-to-guide
- Published: 2026-07-28

---

**The ego-lite repository lets you define site-specific learnings by creating a [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) file under `skills/ego-browser/learnings/<site>/` and running `npm run validate:site-skills` to enforce schema compliance.**

Each learning in the **citrolabs/ego-lite** ecosystem is a self-contained package that teaches browser-automation agents how to interact with specific websites. You manage these learnings through a structured [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) file that declares domains, documentation notes, and executable tools. This guide walks through the exact structure, validation rules, and commands needed to create and verify site-specific learnings.

## Understanding the manifest.json Structure

Every site-specific learning lives in its own directory under `skills/ego-browser/learnings/<site>/`. The [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) file inside that directory must declare these core fields:

- **`id`**: Must match the directory name (e.g., `"x-com"` for `.../learnings/x-com`).
- **`name`**: Human-readable site name displayed in logs and interfaces.
- **`domains`**: Array of host patterns the learning applies to.
- **`notes`**: Array of Markdown file paths documenting the learning.
- **`nodeTools`**: Server-side JavaScript utilities imported via Node that agents can invoke.
- **`browserTools`**: Client-side JavaScript utilities executed in the browser context.

## Step-by-Step Guide to Creating a Site-Specific Learning

### Setting Up the Directory Structure

Create a new directory under `skills/ego-browser/learnings/` using a kebab-case identifier. Inside, establish this layout:

```

skills/ego-browser/learnings/example/
├── manifest.json
├── notes/
│   └── overview.md
├── tools/
│   └── search-items.js
└── browser-tools/
    └── click-first.js

```

The `id` field in your [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) must exactly match the directory name (`example` in this case).

### Defining Required Fields in manifest.json

At minimum, your [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) must include the required identity fields and domain declarations:

```json
{
  "id": "example",
  "name": "Example.com",
  "domains": ["example.com", "*.example.com"],
  "notes": ["notes/overview.md"]
}

```

Domain patterns must follow strict host string rules: no protocol, no trailing dots, and only a single leading `*.` if using wildcards. The validation engine in [`src/learning/validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/validate-learning-format.ts) uses the `isValidDomain` function to enforce these constraints.

### Adding Node Tools and Browser Tools

Tools are categorized by execution context. **nodeTools** run server-side and require a `callable` export name, while **browserTools** execute in the browser context.

```json
{
  "nodeTools": {
    "search_items": {
      "description": "Search items on Example.com and return titles.",
      "path": "tools/search-items.js",
      "callable": "searchItems",
      "args": {
        "query": {
          "type": "string",
          "required": true,
          "description": "Search query."
        }
      },
      "returns": {
        "type": "array",
        "description": "Array of result titles."
      }
    }
  },
  "browserTools": {
    "click_first_result": {
      "description": "Clicks the first search result.",
      "path": "browser-tools/click-first.js",
      "args": {},
      "returns": {
        "type": "object",
        "description": "Metadata about the clicked element."
      }
    }
  }
}

```

## Validation Requirements and Checks

The schema enforcement engine resides in [`src/learning/validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/validate-learning-format.ts). When you invoke validation, the system performs these seven checks:

1. **Structural validation**: Confirms [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) is valid JSON and contains required fields (`id`, `name`, `domains`).
2. **Domain validation**: Verifies each domain pattern is a valid host string using the `isValidDomain` function.
3. **Note validation**: Ensures each entry in `notes` points to a Markdown file inside `notes/` via the `isNotePath` helper, and contains no temporary snapshot references (`@123` or `ref=123`).
4. **Tool map validation**: Validates that `nodeTools` and `browserTools` entries have safe names (checked by `isSafeToolName`), descriptions, and correct relative paths (`tools/*.js` or `browser-tools/*.js`). Node tools must declare a `callable` property. This logic is handled by `validateToolMap` and `validateToolSchema`.
5. **Argument and return schema validation**: Each tool's `args` and `returns` objects must specify supported types (`string`, `number`, `integer`, `boolean`, `array`, `object`) with non-empty descriptions, verified by `validateValueSchema`.
6. **File existence verification**: The `requireFile` function confirms every referenced tool file exists on disk.
7. **Temporary reference rejection**: Any tool or note containing snapshot temporary references triggers an error, encouraging stable locators.

## How to Validate Your Learning

### Command Line Validation

Run the npm script defined in the project to check all site learnings:

```bash
npm run validate:site-skills

```

This executes [`src/scripts/validate-site-skills.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/scripts/validate-site-skills.ts), printing either "All site learnings are valid" or a detailed list of errors.

### Programmatic Validation

Import the validation function directly from the learning module:

```typescript
import { validateSiteSkills } from 'package/ego-browser/src/learning/index.js';

const errors = await validateSiteSkills();
if (errors.length) {
  console.error('Learning validation failed:', errors);
  process.exit(1);
}

```

The `validateSiteSkills` function is re-exported from [`src/learning/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/index.ts) alongside `validateLearning` and `validateLearnings`.

### Using Validated Learnings in Agents

Once validated, agents access site tools through the `runSiteTool` helper:

```javascript
// Inside an ego-browser agent script
const results = await runSiteTool('example', 'search_items', { query: 'hello world' });
console.log('Found titles:', results);

```

The harness automatically loads all validated learnings at startup, making tools available under their declared IDs.

## Summary

- Site-specific learnings reside in `skills/ego-browser/learnings/<site>/` with a mandatory [`manifest.json`](https://github.com/citrolabs/ego-lite/blob/main/manifest.json) file.
- The manifest must declare `id`, `name`, `domains`, and may include `notes`, `nodeTools`, and `browserTools`.
- Validation enforces domain format rules, file existence, argument schemas, and prohibits temporary snapshot references via [`src/learning/validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/validate-learning-format.ts).
- Run `npm run validate:site-skills` to check all learnings, or import `validateSiteSkills` from [`src/learning/index.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/index.ts) for programmatic checks.
- Validated learnings are automatically loaded by the harness and accessible via `runSiteTool(siteId, toolName, args)`.

## Frequently Asked Questions

### What happens if my domain pattern includes a protocol like https://?

The validator rejects the pattern. According to `isValidDomain` in [`src/learning/validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/src/learning/validate-learning-format.ts), domain patterns must not include protocols or trailing dots. Use bare hostnames like `"example.com"` or `"*.example.com"` instead.

### Can I reference TypeScript files directly in the tools path?

No. The `path` property under `nodeTools` and `browserTools` must point to compiled JavaScript files using the `tools/*.js` or `browser-tools/*.js` pattern. The `requireFile` check verifies physical file existence, so ensure your build process outputs `.js` files to these locations.

### Why does validation fail when I include @123 in my note paths?

Temporary snapshot references like `@123` or `ref=123` are explicitly rejected by the validation engine to prevent brittle locators. The check at lines 27-35 of [`validate-learning-format.ts`](https://github.com/citrolabs/ego-lite/blob/main/validate-learning-format.ts) treats these as errors, requiring you to use stable identifiers before the learning can pass validation.

### How do I share a learning across multiple domain variations?

Include all relevant patterns in the `domains` array. For example, `["example.com", "*.example.com", "subdomain.example.com"]` covers the root domain, all subdomains via wildcard, and specific subdomains. Each pattern undergoes independent validation by `isValidDomain` to ensure proper formatting.