> For the complete documentation index, see [llms.txt](https://ai-os-and-trend-finder.gitbook.io/ai-os-and-trend-finder-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ai-os-and-trend-finder.gitbook.io/ai-os-and-trend-finder-docs/docs/extensions/readme_docs-extensions.md).

# Extension System

This guide is for adding a new repo-local extension from a fresh checkout. In this project, "extension" is the implemented plugin shape: a compile-time registered module with client views and, optionally, a script-side collector. There is no dynamic package loading or remote plugin installation.

## Related Docs

| Document                                                                                                                   | Use It For                                                                                                                                     |
| -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| [Extension package README](/ai-os-and-trend-finder-docs/src/extensions/readme_extensions.md)                               | Short file map and current registered extensions                                                                                               |
| [Trend Finder Docs](/ai-os-and-trend-finder-docs/docs/extensions/trend-finder/readme_trend-finder.md)                      | Directory map for the shipped Trend Finder extension manuals                                                                                   |
| [Trend Finder Concepts](/ai-os-and-trend-finder-docs/docs/extensions/trend-finder/concepts.md)                             | Entry point for Trend Finder mental model, core objects, surfaces, and common confusions                                                       |
| [Trend Finder Pipeline](/ai-os-and-trend-finder-docs/docs/extensions/trend-finder/pipeline.md)                             | Aggregate run stages, browser payload boundaries, local run control, scheduler state, enrichment/spend, static export, and implementation map  |
| [Trend Finder Sources](/ai-os-and-trend-finder-docs/docs/extensions/trend-finder/sources.md)                               | Built-in adapters, reviewed Apify declarations, Source Setup targets, source caps, health, spend/cache labels, and trust/provenance            |
| [Trend Finder Runtime And Provenance](/ai-os-and-trend-finder-docs/docs/extensions/trend-finder/runtime-and-provenance.md) | AI analysis, deterministic fallback, validation, runtime readiness, provenance labels, live progress, Reference mode, and Engine Replay states |
| [Trend Finder Scoring](/ai-os-and-trend-finder-docs/docs/extensions/trend-finder/scoring.md)                               | Score factors, source-local baselines, placement exclusions, actionability bands, hidden gem thresholds, and watchlist rules                   |
| [Trend Finder Creator Lens](/ai-os-and-trend-finder-docs/docs/extensions/trend-finder/creator-lens.md)                     | Creator Lens fields, default profile, save/rerun behavior, and AI/fallback effects                                                             |
| [Trend Finder History](/ai-os-and-trend-finder-docs/docs/extensions/trend-finder/history.md)                               | Topic identity, movement, historical context, predictions, retros, and script-only backtests                                                   |
| [Trend Finder UI Surfaces](/ai-os-and-trend-finder-docs/docs/extensions/trend-finder/ui-surfaces.md)                       | Trends, Workbench, Hidden Gems, Sources, Watchlist, Brief, Signal Radar, Source Setup, assets, run controls, and Reference mode                |
| [Trend Finder MCP](/ai-os-and-trend-finder-docs/docs/extensions/trend-finder/mcp.md)                                       | Local stdio MCP server, tool list, setup commands, and generated-payload-only privacy boundary                                                 |
| [AI Rogue Docs](/ai-os-and-trend-finder-docs/docs/extensions/ai-rogue/readme_ai-rogue.md)                                  | Directory map for the production-enabled AI Rogue extension manuals                                                                            |
| [AI Rogue New Level Authoring Guide](/ai-os-and-trend-finder-docs/docs/extensions/ai-rogue/new-level-authoring-guide.md)   | Inventory-first workflow for "build X more levels" requests, asset gap reports, implementation touchpoints, and validation gates               |
| [AI Rogue Enablement Decision](/ai-os-and-trend-finder-docs/docs/extensions/ai-rogue/enablement-decision.md)               | Production default-enable decision, quality gates, privacy checks, and follow-ups                                                              |
| [AI Rogue Extension Plan](/ai-os-and-trend-finder-docs/docs/extensions/ai-rogue/plan-2026-06-21.md)                        | Implementation plan, runtime boundary, economy, first slice, and phase plan                                                                    |
| [AI Rogue Visual Assets](/ai-os-and-trend-finder-docs/docs/extensions/ai-rogue/visual-assets.md)                           | Runtime art brief, prompt/provenance checklist, atlas inventory, and acceptance gates                                                          |
| [Scripts README](/ai-os-and-trend-finder-docs/scripts/readme_scripts.md#extension-collector-system)                        | Collector runner files and script commands                                                                                                     |
| [Architecture](/ai-os-and-trend-finder-docs/docs/architecture.md#extension-platform)                                       | System-level overview of host routes, runtime data, and settings                                                                               |
| [ADR 0001](/ai-os-and-trend-finder-docs/docs/adr/0001-extension-platform-foundation.md)                                    | Why extensions are static, opt-in, and compile-time registered                                                                                 |
| [Local API Notes](/ai-os-and-trend-finder-docs/docs/api/readme_api.md#extension-data)                                      | How extension data reaches the browser through `/__live-data`                                                                                  |
| [Environments](/ai-os-and-trend-finder-docs/docs/environments.md#public-browser-configuration)                             | `VITE_CLAUDE_OS_ENABLED_EXTENSIONS` and env-var safety                                                                                         |
| [Testing](/ai-os-and-trend-finder-docs/docs/testing.md)                                                                    | Vitest, Playwright, and coverage conventions                                                                                                   |

If your extension collects public source data, also read the nearest source compliance doc before writing adapter code:

* [Hacker News](/ai-os-and-trend-finder-docs/docs/sources/source-compliance-hackernews.md)
* [Reddit](/ai-os-and-trend-finder-docs/docs/sources/source-compliance-reddit.md)
* [GitHub](/ai-os-and-trend-finder-docs/docs/sources/source-compliance-github.md)
* [YouTube](/ai-os-and-trend-finder-docs/docs/sources/source-compliance-youtube.md)
* [Product Hunt](/ai-os-and-trend-finder-docs/docs/sources/source-compliance-producthunt.md)

## Mental Model

An extension has three possible parts:

| Part                | Required              | Location                                                                             | Purpose                                                                |
| ------------------- | --------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
| Client extension    | Yes                   | `src/extensions/<id>/`                                                               | Declares views, sidebar entry, labels, capabilities, and UI components |
| Collector extension | No                    | `scripts/extensions/<id>/`                                                           | Generates runtime data during `bun run aggregate`                      |
| Documentation/tests | Yes for real features | `docs/`, `src/lib/__tests__/`, `scripts/extensions/<id>/**/__tests__/`, `tests/e2e/` | Explains behavior and protects contracts                               |

The client and collector should use the same extension ID. The collector writes runtime output under `LiveData.extensions.items[extensionId]`; the client reads that payload through `useExtensionData()` via `ExtensionHost`.

Runtime data has two supported modes:

| Mode             | Use It When                                                                                     |
| ---------------- | ----------------------------------------------------------------------------------------------- |
| `extension-item` | A script-side collector writes browser-safe data under `LiveData.extensions.items[extensionId]` |
| `host-live-data` | A client-local extension reads the host LiveData payload and does not emit a collector item     |

`extension-item` is the default when `runtimeDataMode` is omitted. Use `host-live-data` for extensions like AI Rogue that are enabled through the registry/env switch but derive their state from browser-safe host data and browser-local persistence. Do not add a fake collector item just to prove that such an extension is enabled.

## Before You Start

Choose these up front:

* Extension ID: kebab-case, stable, and unique, such as `my-extension`.
* Default view ID: a view ID that exists in the extension `views` array.
* Runtime data mode: `extension-item` for collector-backed data, or `host-live-data` for client-local extensions that use the host payload.
* Data shape: define a small typed shape and validate unknown runtime data in the client, usually with Zod, when using `extension-item`.
* Collector payload shape: if the extension has a collector, validate the generated payload with `validateData()` before it can enter runtime data.
* External sources: if the collector calls a public API or website, create or update `docs/sources/source-compliance-<source>.md` before implementation.
* Secrets: use private script env vars only. Never put API keys in `VITE_` variables, browser state, generated examples, or docs.

Generated private runtime data stays in `src/data/live-data.json`, which is gitignored. Aggregate diagnostics stay in `logs/aggregate-*.jsonl` and `logs/aggregate.latest.jsonl`, which are also gitignored. Only committed safe examples belong in `src/data/live-data.example.json`.

Trend Finder also stores local movement snapshots below `.cache/extensions/trend-finder/snapshots/`. That cache is ignored by git and should not be committed or read directly by client code.

## Recommended File Layout

```
src/extensions/my-extension/
|-- client.tsx
|-- schema.ts              # optional but recommended for runtime data parsing
`-- views/
    `-- overview-view.tsx

scripts/extensions/my-extension/
|-- collector.ts           # optional
`-- __tests__/
    `-- collector.test.ts
```

For a public source adapter, use a subdirectory like:

```
scripts/extensions/my-extension/sources/
|-- types.ts
|-- example-source-adapter.ts
`-- __tests__/
    `-- example-source-adapter.test.ts
```

The Trend Finder extension is the current working example:

* Client: [`src/extensions/trend-finder/client.tsx`](https://github.com/moshehbenavraham/ai-os/blob/main/src/extensions/trend-finder/client.tsx)
* Collector: [`scripts/extensions/trend-finder/collector.ts`](https://github.com/moshehbenavraham/ai-os/blob/main/scripts/extensions/trend-finder/collector.ts)
* Source adapter: [`scripts/extensions/trend-finder/sources/hn-adapter.ts`](https://github.com/moshehbenavraham/ai-os/blob/main/scripts/extensions/trend-finder/sources/hn-adapter.ts)

Current Trend Finder behavior includes:

* Client views: Trends, Workbench, Hidden Gems, Sources, Watchlist, and Brief.
* Dedicated Engine Replay route: `/extensions/trend-finder/engine`.
* A route-aware Trend Finder shell with shared hero imagery, contextual freshness/source/provenance state, compact run action, and extension-local `Extension v0.1.0` labeling.
* Trends first viewport with Signal Radar, compact ranked topics, selected trend inspection, score factors, evidence, creator angle, and provenance.
* Data-driven Signal Radar axes over momentum and openness/novelty, with lifecycle colors, evidence-sized topic bubbles, and explicit fallback labels.
* Dedicated Hidden Gems, Watchlist, and Brief surfaces instead of reusing one full trend card layout for every route.
* Runtime readiness panel with redacted provider, credential, and fallback states.
* Source health and warning displays for active, degraded, offline, and compliance-blocked sources.
* Local-only Source Setup for reviewed Apify sources, with allowlisted target edits and credential/config presence labels only.
* Source trust, source-role, and evidence-contribution matrices that connect source state back to trend confidence.
* Score breakdowns, derived saturation, hidden-gem score, consensus ratio, role shares, builder signal, risk flags, velocity dynamics, evidence links, creator angles, angle-pack copy, suggested hooks, and audience questions for topics.
* Source-local baseline/actionability chips that exclude clear promoted, pinned, stickied, sponsored, or ad-like placement rows from organic baseline support.
* Signal Workbench for dense topic scanning, source/evidence filters, and browser-local triage/watching annotations that never mutate generated payloads.
* Signal Workbench theme grouping, cross-source outlier preset, and top-N outlier ideas over reviewed source-local baselines.
* Private daily topic series, bounded 14-day sparklines, Hugging Face download deltas, lifecycle stages, convergence timelines, and score trajectories.
* Dated lifecycle predictions, target-date retro grading, calibration metrics, and a browser-safe Story Log projection.
* Creator Lens competitor names with bounded validation and collect-time public YouTube channel-title matching.
* Scheduler first-run status, reviewed cadence controls, local timer intent, live run progress, and recurring spend projection through browser-safe labels only.
* Phase 28 scoring integrity: deduplicated scoring evidence, per-signal quality, calibration version stamps, confidence dampening, visibility bands, signal aging, saturation refinement, lifecycle score movement, research-only risk, action verdicts, and generated Watchlist rules.
* Phase 28 source closeout: reviewed keyword packs, balanced/focused scan modes, deterministic keyword rotation, coverage QA, reviewed direct public API/feed adapters, direct-vs-Apify fallback labels, and zero-cost public API spend rows.
* Evidence asset preview labels and protected local asset fetches when a reviewed manifest-approved asset is available.
* Dashboard Brief navigation for today's pick, movement groups, demand centers, top creator angles, source health, prediction calibration, Story Log, and evidence to verify.
* Opt-in static Brief export through `bun run trend-finder:export-brief`, which writes a browser-safe local report under the private Trend Finder cache by default.
* Engine Replay latest-run proof for sanitized source, evidence, runtime, scoring, and artifact stages. It supports fixture/demo, sanitized, stale, missing, disabled, error, empty, and offline labels without reading raw logs.
* Engine Replay Reference mode that renders the Trend Finder Markdown manuals in app while keeping the Markdown files as the documentation source of truth.
* Per-view visibility controls for hiding and restoring Trend Finder panels, topics, sources, evidence, score factors, and contribution rows. Settings are presentation-only and persist in browser localStorage, with an optional loopback-only file overlay at `data/trend-finder.visibility.json`.
* Script-only historical backtest CLI for reviewed windows, private archive storage, and bounded browser summaries when explicitly published.
* Generated data delivery through `LiveData.extensions.items["trend-finder"]`.

The static Brief report workflow is local and opt-in. It reads the current validated Trend Finder payload, writes `index.html` and `manifest.json` under `.cache/extensions/trend-finder/static-brief/` by default, and excludes raw prompts, provider responses, raw source dumps, raw logs, private caches, raw billing payloads, credentials, account auth, local triage notes, and local filesystem paths. It can include today's pick, movement groups, demand centers, angle-pack copy, bounded sparklines, prediction calibration, Story Log summaries, and asset fallback labels when those fields are present in the validated payload. Public hosting, generated artifact commits, historical replay, hosted observability, generic host replay, and raw trace playback are not default workflows.

Deferred source candidates live in [`trend-finder/sources.md`](/ai-os-and-trend-finder-docs/docs/extensions/trend-finder/sources.md#deferred-source-coverage-candidates) and remain compliance-first future work. Candidate names in PRDs, source coverage artifacts, or manuals are not collection approvals.

Phase 29 release documentation status: the Trend Finder manuals are the current operator-facing record for the shipped comparison-adoption work. They document editorial guardrails, attention/reception/corroboration, evidence rationales, run narratives, required-field closeout, source-death baselines, seed review, industry events, security lens, static Brief archival, One to Watch, pre-run estimate, and the podcast defer decision as shipped or explicitly deferred behavior. Session 17 stays deferred by Session 16; broader social reach remains a deliberate non-goal.

## AI Rogue Extension

AI Rogue is the second registered repo-local extension. Phase 30 completed the first playable slice. Phase 34 closed the audited default-enable blockers and AI Rogue is now production-enabled by default in the browser registry. Use `VITE_CLAUDE_OS_ENABLED_EXTENSIONS=none` only when a build must explicitly hide extension entries. Phase 39 moved level authoring into `src/extensions/ai-rogue/runtime/content/` and the shipped main run is now a four-level authored run backed by registry-owned depth, theme, music, ambience, objective, enemy, and finale metadata.

Current AI Rogue behavior includes:

* Client views: Play, Ledger, Loadout, and Settings.
* Primary route: `/extensions/ai-rogue/play`.
* Lazy PixiJS v8 WebGL runtime loaded from the Play view, kept out of the shared app shell and registry chunks.
* Deterministic TypeScript dungeon simulation with seeded RNG, movement, FOV/fog, hazards, pickups, registry-backed authored levels, Kernel Sentinel finale contracts, a trait-recombined enemy ecology, combat, and terminal run states.
* Browser-local wallet, ledger, preferences, run history, save slots, and AI Rogue-only reset behavior.
* Insight Shards economy derived from browser-safe AI OS activity with manual claims, daily caps, idempotent redemption keys, and provenance labels.
* Progression through capped model-flavored resources, skill-linked class effects, relics, objectives, and local run-history achievements.
* Committed gameplay and UI atlases under `src/assets/ai-rogue/`, each below the 200 KB media-policy limit.
* Committed generated Ogg music, theme ambience, SFX, enemy-family cues, boss cues, and adaptive stingers under `src/assets/ai-rogue/audio/`, routed through lazy Web Audio playback with mute/volume preferences and silent fallback.
* No collector, remote game-content loading, hosted writes, analytics, raw private telemetry export, raw prompts, transcripts, command bodies, local paths, logs, credentials, WebGPU-only path, worker simulation, broad inventory rewrite, map-editor dependency, gamepad, or pointer-lock behavior in the current slice.

Use [`ai-rogue/README_ai-rogue.md`](/ai-os-and-trend-finder-docs/docs/extensions/ai-rogue/readme_ai-rogue.md) as the AI Rogue document map, [`ai-rogue/new-level-authoring-guide.md`](/ai-os-and-trend-finder-docs/docs/extensions/ai-rogue/new-level-authoring-guide.md) for future "build X more levels" inventory and asset-gap workflow, and [`ai-rogue/enablement-decision.md`](/ai-os-and-trend-finder-docs/docs/extensions/ai-rogue/enablement-decision.md) for the current production-enable quality-gate record.

### Trend Finder Engine Replay

Engine Replay is a dashboard-native proof surface, not a normal extension tab. It uses a dedicated route so it can sit beside the extension views while preserving the Trend Finder tab navigation for creator workflows. The route has two modes: Replay for latest-run proof and Reference for the in-app Trend Finder manuals.

```
http://127.0.0.1:5189/extensions/trend-finder/engine
```

The route reads the current Trend Finder extension payload from `/__live-data` and renders `data.engineTrace` after client-side validation. When a sanitized trace is missing, it may derive a static source-to-artifact story from safe Trend Finder payload fields. It does not read `logs/aggregate.latest.jsonl`, raw collector events, raw model prompts or responses, Actor/Dataset internals, raw billing payloads, account auth files, or other private local telemetry.

Reference mode imports the curated Trend Finder Markdown docs through `src/extensions/trend-finder/reference-docs.ts` and renders those manuals with local tabs. Links between registered Trend Finder manuals switch Reference tabs; other local docs render as code references. It is explanatory documentation, not a run trace, runtime inspection path, dynamic docs loader, or private-cache browser.

Implemented labels include:

* `Sanitized trace` for the latest browser-safe run summary.
* `Fixture/demo trace` for committed or hand-authored demo proof.
* `Stale sanitized trace` when the trace does not match the newest aggregate timestamp.
* `Sanitized trace missing` when the shell is rendering safe payload-derived state without a trace.
* `Trace unavailable`, `Trend Finder disabled`, empty, and offline states for explicit failure or unavailable paths.

Deferred behavior:

* Historical run selection, which depends on a future durable local run store.
* Hosted observability or production trace storage.
* Generic host replay outside Trend Finder.
* Raw JSONL, prompt, response, provider request, Actor, Dataset, or private telemetry playback.
* Default public deployment or generated report commits.

## 1. Create the Client Extension

Create `src/extensions/my-extension/client.tsx`.

```tsx
import { Puzzle } from "lucide-react";
import type { ClientExtension, ExtensionViewProps } from "../types";

function OverviewView({ status, data, warnings, error, refreshLiveData }: ExtensionViewProps) {
  return (
    <section className="space-y-4">
      <h1 className="text-2xl font-semibold">My Extension</h1>
      <p>Status: {status}</p>
      {error ? <p role="alert">{error}</p> : null}
      {warnings.length > 0 ? <p>{warnings.length} warnings</p> : null}
      <button type="button" onClick={refreshLiveData}>
        Refresh data
      </button>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </section>
  );
}

export const myExtension: ClientExtension = {
  id: "my-extension",
  name: "My Extension",
  description: "Short description shown in extension listings and settings.",
  version: "0.1.0",
  defaultViewId: "overview",
  runtimeDataMode: "extension-item",
  views: [{ id: "overview", label: "Overview", component: OverviewView }],
  nav: [{ label: "Overview", viewId: "overview", icon: Puzzle }],
  panels: [],
  capabilities: [
    {
      kind: "readGeneratedData",
      reason: "Reads its generated runtime data from LiveData.extensions",
    },
  ],
};
```

Use `lucide-react` icons for the sidebar entry and view tabs when possible. Keep multi-view extensions to one global sidebar entry; the host renders horizontal view navigation from the `views` array inside the extension page.

## 2. Register the Client Extension

Update `src/extensions/registry.ts`:

```ts
import { myExtension } from "./my-extension/client";
import { trendFinderExtension } from "./trend-finder/client";

export const extensions: ClientExtension[] = [trendFinderExtension, myExtension];
```

The host routes already exist:

| Route                               | Purpose                                  |
| ----------------------------------- | ---------------------------------------- |
| `/extensions`                       | Lists registered extensions              |
| `/extensions/my-extension`          | Redirects to the default view            |
| `/extensions/my-extension/overview` | Renders the view through `ExtensionHost` |

Enable the extension locally in `.env.local`:

```bash
VITE_CLAUDE_OS_ENABLED_EXTENSIONS=my-extension
```

Multiple IDs can be comma-separated. `all` enables every registered extension, and `none` disables all extension navigation. After changing `.env.local`, restart Vite; `VITE_` values are compiled into the browser environment at server start. Editing `.env.local.example` documents the default template only; it does not change an existing `.env.local` or an already-running dev server.

## 3. Read Runtime Data Safely

Extension views receive data as `unknown | null` through `ExtensionViewProps`. Validate it before rendering feature UI. Use an empty state when data is absent, disabled, invalid, or still pending.

For `extension-item` extensions, `data` is the collector item payload from `LiveData.extensions.items[extensionId].data`.

Recommended pattern:

```ts
import { z } from "zod";

const MyExtensionDataSchema = z.object({
  items: z.array(z.object({ id: z.string(), label: z.string() })).default([]),
});

export type MyExtensionData = z.infer<typeof MyExtensionDataSchema>;

export function parseMyExtensionData(raw: unknown): MyExtensionData {
  const parsed = MyExtensionDataSchema.safeParse(raw);
  return parsed.success ? parsed.data : { items: [] };
}
```

You do not need a new top-level `LiveData` field for normal extension payloads. The shared `extensions` branch is already part of the runtime contract.

Add a safe `src/data/live-data.example.json` entry only if the extension needs committed demo data. Client-local `host-live-data` extensions may be enabled without any matching `LiveData.extensions.items` entry in the example payload. For example, AI Rogue intentionally has no collector item because it reads the host LiveData payload and stores game state in browser-local persistence. Do not commit `src/data/live-data.json`.

For `host-live-data` extensions, declare the mode in the client extension:

```ts
export const myLocalExtension: ClientExtension = {
  id: "my-local-extension",
  name: "My Local Extension",
  description: "Reads browser-safe host activity directly.",
  version: "0.1.0",
  defaultViewId: "overview",
  runtimeDataMode: "host-live-data",
  views: [{ id: "overview", label: "Overview", component: OverviewView }],
  nav: [{ label: "Overview", viewId: "overview", icon: Puzzle }],
  capabilities: [{ kind: "readGeneratedData", reason: "Reads host LiveData" }],
};
```

## 4. Add an Optional Collector

Create `scripts/extensions/my-extension/collector.ts` when the extension needs runtime data generation during `bun run aggregate`.

```ts
import { z } from "zod";
import type {
  CollectorExtension,
  ExtensionCollectResult,
  ExtensionCollectorContext,
} from "../../lib/extensions/types";

const MyExtensionDataSchema = z.object({
  items: z.array(z.object({ id: z.string(), label: z.string() })),
  lastUpdatedAt: z.string(),
});

type MyExtensionData = z.infer<typeof MyExtensionDataSchema>;

export const myExtensionCollector: CollectorExtension<MyExtensionData> = {
  id: "my-extension",
  name: "My Extension",
  version: "0.1.0",
  requiredEnvKeys: ["MY_EXTENSION_API_KEY"],
  capabilities: [
    {
      kind: "networkAccess",
      reason: "Fetches public data from the configured source",
    },
  ],
  timeoutMs: 30_000,
  maxPayloadBytes: 256_000,
  validateData(data: unknown) {
    const result = MyExtensionDataSchema.safeParse(data);
    if (result.success) return { ok: true, data: result.data };
    return { ok: false, reason: result.error.message };
  },

  async collect(ctx: ExtensionCollectorContext): Promise<ExtensionCollectResult<MyExtensionData>> {
    const apiKey = ctx.env("MY_EXTENSION_API_KEY");
    if (!apiKey) {
      throw new Error("MY_EXTENSION_API_KEY was required but not available");
    }

    ctx.log.info("Collecting My Extension data");

    return {
      data: {
        items: [{ id: "example", label: "Example item" }],
        lastUpdatedAt: ctx.now(),
      },
    };
  },
};
```

Register it in `scripts/extensions/registry.ts`:

```ts
import { myExtensionCollector } from "./my-extension/collector";
import { trendFinderCollector } from "./trend-finder/collector";

const collectors: CollectorExtension[] = [trendFinderCollector, myExtensionCollector];
```

The runner provides guardrails:

* Disabled extensions produce `status: "disabled"` and do not call `collect()`.
* Missing `requiredEnvKeys` produce `status: "error"` without exposing values.
* Timeouts, thrown errors, malformed results, and oversized payloads are contained to that extension.
* Payloads that fail `validateData()` produce `status: "error"` with `data: null`.
* The main aggregate can still complete when one extension fails.

Trend Finder builds on those runner guardrails with source-level degradation: one failed source or missing optional credential emits warnings while preserving valid evidence from other sources. The collector validates its generated payload before it reaches `live-data.json`.

## 5. Wire Env Vars and Settings Visibility

When a collector needs private config:

1. Add placeholder documentation to `.env.local.example`.
2. Add every key read through `ctx.env()` to `requiredEnvKeys` in the collector.
3. Keep the key out of `VITE_` variables.

`requiredEnvKeys` is both the allowlist for `ctx.env()` and the missing-key gate. If any listed key is absent, the runner marks the extension `error` and does not call `collect()`.

Settings show required extension env keys as present/missing booleans only. The browser must never receive secret values.

Use `VITE_CLAUDE_OS_ENABLED_EXTENSIONS` only for extension IDs because Vite exposes `VITE_` values to the browser bundle.

## 6. Add Tests

Use focused tests based on the blast radius:

| Change                   | Suggested Tests                                                                      |
| ------------------------ | ------------------------------------------------------------------------------------ |
| Client registry behavior | `src/lib/__tests__/extension-registry.test.ts` or a new nearby test                  |
| Runtime data parsing     | Schema/parser tests in `src/lib/__tests__/`                                          |
| View behavior            | Route/component tests in `src/routes/__tests__/` or `src/lib/__tests__/`             |
| Collector behavior       | `scripts/extensions/<id>/**/__tests__/**/*.test.ts`                                  |
| Public source adapter    | Adapter tests for rate limits, error states, PII exclusions, and malformed responses |
| Browser-visible routes   | `tests/e2e/` Playwright tests                                                        |

For Trend Finder runtime changes, include focused coverage for the affected layer:

| Layer                                                        | Suggested Tests                                 |
| ------------------------------------------------------------ | ----------------------------------------------- |
| Apify config/client/Actor/Dataset                            | `scripts/lib/apify/__tests__/`                  |
| AI runtime providers, analyst validation, scoring, snapshots | `scripts/lib/ai-runtime/__tests__/`             |
| Trend Finder collector and source adapters                   | `scripts/extensions/trend-finder/**/__tests__/` |
| Dashboard schema and views                                   | `src/lib/__tests__/trend-finder-*.test.ts*`     |
| Browser navigation and Brief view                            | `tests/e2e/trend-finder.spec.ts`                |

`vitest.config.ts` already includes `scripts/extensions/**/__tests__/**/*.test.ts`.

## 7. Validate Locally

For a UI-only extension:

```bash
bun run typecheck
bun run test
bun run format:check
```

For a collector extension, also run:

```bash
bun run typecheck:scripts
bun run aggregate
```

If a collector or runtime analysis path fails, inspect `logs/aggregate.latest.jsonl` before rerunning. The latest trace includes collector lifecycle events, runtime readiness, sanitized request/response metadata, validation issues, fallback reasons, and provider error causes.

For Trend Finder runtime certification, run the focused runtime/dashboard test set before the full suite:

```bash
bun run test -- scripts/lib/apify/__tests__ scripts/lib/ai-runtime/__tests__ scripts/extensions/trend-finder/__tests__ scripts/extensions/trend-finder/sources/__tests__ src/lib/__tests__/trend-finder-schema.test.ts src/lib/__tests__/trend-finder-dashboard.test.tsx src/lib/__tests__/trend-finder-collector.test.ts
```

For browser-visible behavior, run:

```bash
bun run test:e2e
```

For Trend Finder closeout and release certification, also run:

```bash
bun run trend-finder:export-brief -- --dry-run --json
bun run runtime:check-private
bun run build
bun run budget:check
bun run typecheck
bun run typecheck:scripts
bun run test
bun run format:check
bun run test:e2e
bun audit
```

Reference mode certification uses the committed manual registry, not a dynamic docs scan. Add or update Reference tests when release docs require a boundary phrase, internal Trend Finder Markdown link, deferred-candidate row, or planned-feature-drift assertion. Record any repo-wide formatting drift separately from scoped formatting proof for files touched by the closeout.

Manual smoke check for a new opt-in extension:

1. Set `VITE_CLAUDE_OS_ENABLED_EXTENSIONS=my-extension` in `.env.local`.
2. Restart Vite after changing `.env.local`; changing `.env.local.example` alone is not an active local configuration change.
3. Run `bun run aggregate` if the extension has a collector.
4. Inspect `logs/aggregate.latest.jsonl` if the generated status or runtime readiness is not what the UI shows.
5. Run `bun run dev`.
6. Open `http://127.0.0.1:5189/extensions/my-extension`.
7. Check `/settings` to confirm extension status, capabilities, and env-key presence display as expected.

For the built-in Trend Finder extension, open `http://127.0.0.1:5189/extensions/trend-finder/trends` and use the local view navigation to check Hidden Gems, Sources, Watchlist, Brief, and per-view visibility controls. Open `http://127.0.0.1:5189/extensions/trend-finder/engine` to check Engine Replay latest-run proof, Reference mode docs, mobile stacking, reduced-motion behavior, and explicit empty, error, or offline states.

For the built-in AI Rogue extension, no env value is required. Open `http://127.0.0.1:5189/extensions/ai-rogue/play` and check Play, Ledger, Loadout, Settings, local save/reset behavior, and browser-local runtime state.

## Common Pitfalls

* The extension ID in the client and collector must match.
* `defaultViewId` must match one of the `views[].id` values.
* `nav[].viewId` must point to a registered view.
* Registry/env enablement, collector output, and host LiveData availability are separate states. A sidebar entry proves registry/env enablement; it does not prove an `extension-item` collector payload exists.
* If an extension is visible but the route or Settings state looks wrong, check `runtimeDataMode`, `.env.local`, and whether Vite was restarted after the latest `VITE_CLAUDE_OS_ENABLED_EXTENSIONS` edit. For AI Rogue, also confirm the value is not explicitly set to `none`.
* Capabilities are declarations shown to users; they are not a replacement for code-level guardrails and tests.
* Collector data is `unknown` in the browser until your extension validates it.
* Do not add source adapters before source compliance, retention, rate-limit, and PII boundaries are documented.
* Do not commit `.env.local`, `src/data/live-data.json`, `logs/aggregate*.jsonl`, API keys, raw source dumps, or private runtime data.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://ai-os-and-trend-finder.gitbook.io/ai-os-and-trend-finder-docs/docs/extensions/readme_docs-extensions.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
