From 3064560c25dcd6d589fa17fa54e5c62c991458d1 Mon Sep 17 00:00:00 2001 From: xNet Test Date: Fri, 21 Aug 2026 17:38:39 -0700 Subject: [PATCH 01/16] docs(exploration): explore Cordis lessons for xNet plugin composition Co-Authored-By: Claude Fable 5 Signed-off-by: xNet Test --- ...DIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md | 580 ++++++++++++++++++ 1 file changed, 580 insertions(+) create mode 100644 docs/explorations/0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md diff --git a/docs/explorations/0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md b/docs/explorations/0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md new file mode 100644 index 000000000..166960016 --- /dev/null +++ b/docs/explorations/0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md @@ -0,0 +1,580 @@ +--- +title: Cordis lessons for xNet plugin composition +status: draft +last_updated: 2026-08-21 +review: 2026-12-16 # re-decide alongside 0452's registry review — the two docs share a fate: 0452 builds the doors, this doc builds the runtime behind them +decider: Chris Smothers +door: two-way # everything proposed is internal runtime mechanics behind existing public seams; nothing touches the wire or a public API +tags: [plugins, architecture, composability, prior-art, agent-tools] +--- + +# Cordis lessons for xNet plugin composition + +> [!TIP] +> **TL;DR** — Do **not** adopt Cordis as a dependency (bus factor 1, unstable +> API, in-process good-faith trust model that ADR-17 exists to refuse). Do +> import its three load-bearing ideas, which are exactly what xNet's plugin +> system is missing: effect scopes (nested, reverse-order, +> awaited disposal instead of today's flat `ctx.subscriptions` array), +> a service layer with inject semantics (plugins *provide* and +> *consume* named services; the container re-resolves on swap — the unwired +> `extraTools` merge point is the one-line proof we need this), and +> reactive reload (the already-built, already-tested, +> zero-caller `createWorkspacePluginHotReloader` is Cordis's HMR sitting on +> our shelf). Cordis answers "how do plugins compose"; ADR-17 answers "how +> much do we trust them." The two are orthogonal, and xNet only has the +> second. + +## Problem Statement + +[Cordis](https://github.com/cordiverse/cordis) — the "Meta-Framework of +Spatiotemporal Composability" (its README's phrase) extracted from the Koishi +chatbot framework — just became the most-watched plugin architecture in the +industry: DeepSeek Harness ("Everything is a Plugin.", ~181k stars, open-sourced +2026-08-13) vendors it as its plugin kernel, and a companion preprint +formalises its model. Koishi has grown **4,551 community plugins** on it +(registry.koishi.chat, measured 2026-08-21) with essentially one maintainer. + +xNet's stated ambition is the same shape: "everything is a plugin" governed by +a trust fabric (ADR-17), a lift-out ladder for first-party features +([0452](./0452_[_]_HOW_FAR_TO_PLUGINIZE_THE_KERNEL_THE_SHELL_AND_THE_LIFT_OUT_LADDER.md)), +and an agent that builds plugins from inside the workspace +([0331](./0331_[x]_DEVELOPING_XNET_FROM_INSIDE_XNET_SPEC_TO_PLUGIN_LOOP.md)). +Yet `registry/community.json` is `[]`, exactly one first-party feature ships +through the plugin door end-to-end, and every plugin-contributed agent tool is +stranded behind a merge point no host passes. What does Cordis know about +plugin composition that we don't — and which parts of it are poison for a +local-first, sandboxed, CRDT-backed system? + +## Executive Summary + +| Question | Answer | +| --- | --- | +| What is Cordis, in one line? | A context tree where plugins are `(ctx, config)` functions whose every side effect is collected on a disposable scope, and where services are reactively injected — unload on disappear, reload on swap. | +| Should xNet depend on it? | **No.** MIT-licensed but bus factor ≈ 1 (sole npm maintainer), README warns the API "may change without notice", v3→v4 renamed the entire scope layer, and its trust model is in-process good faith — the opposite of ADR-17. | +| What do we take? | Three mechanisms: (1) effect **scopes** replacing the flat `Disposable[]`; (2) a **service registry** with `provide`/`inject` and availability semantics; (3) **reactive reload** — wire the existing workspace-plugin hot reloader and give config edits partial-reload semantics. | +| What do we already have that Cordis doesn't? | The entire trust half: provenance→tier→sandbox mapping, capability guards (`guardStore`, `guardedFetch`), fail-closed paid licensing, consent dialogs, a registry pipeline with CI. Cordis plugins run with full process privileges on good faith. | +| Sharpest evidence we need the service idea? | `AiSurfaceService` has one `extraTools` merge point; all three hosts (`agent-mcp-server.ts`, `cli mcp.ts`, `AiChatPanel.tsx`) construct it without passing the argument, stranding `plugin_*`, `lab_*`, and every plugin-contributed agent tool. With resolution instead of hand-threading, all three sites are correct by construction. | +| Relationship to 0452 | Complementary, not competing. 0452 opens the missing contribution **doors** (node types, surfaces). This doc fixes the **runtime** behind all doors: scoped disposal, service edges between plugins, reload. Both walk through `packages/plugins`. | + +--- + +## Current State In The Repository + +The full survey is long; this section keeps only what the comparison needs. + +### What exists and is real + +- **Manifest + 21 contribution kinds** — `packages/plugins/src/manifest.ts` + (`XNetExtension`, `PluginContributions`); `ContributionRegistry` in + `packages/plugins/src/contributions.ts` holds 22 `TypedRegistry` fields + (`statusBar` and `frameRenderers` are runtime-only, no manifest path). +- **Lifecycle with trust gates** — `PluginRegistry` + (`packages/plugins/src/registry.ts`): `install()` runs 9 ordered gates + (validation → platform → duplicate → host-compat → dependencies → consent → + fail-closed license → persist as node → activate). This half is genuinely + ahead of Cordis, which has none of it. +- **Per-plugin context** — `createExtensionContext` + (`packages/plugins/src/context.ts`): 21 `register*` methods, each returning + a `Disposable`, all collected in a flat `ctx.subscriptions` array walked at + `deactivate()`. +- **One end-to-end dogfood** — `charts-extra-plugin.ts` registering donut and + horizontal-bar chart types into `chartTypeRegistry` and disposing cleanly. +- **The 0331 workspace-plugin runtime** — + `packages/plugins/src/workspace-plugins/` (~1,700 lines, 7 test files): + opaque-origin iframe host, per-file SWC build, typed postMessage protocol, + denylist-wins store RPC, **and a hot reloader** + (`watcher.ts: createWorkspacePluginHotReloader` — 250 ms debounce, rebuild, + hot-swap, crash → auto-disable with last-good hash pinned). **Zero non-test + callers.** + +### What is missing, and where it bites + +| Gap | Where | Consequence | +| --- | --- | --- | +| No scope tree | `context.ts` — flat `Disposable[]`, disposed in registration order, unawaited | A plugin cannot open a sub-scope for a feature it toggles; teardown order is accidental; async `deactivate` races the next mount (`packages/react/src/context.ts:427-457` fires deactivations without awaiting) | +| Three disposal conventions | `Disposable` in `plugins/src/types.ts`, a second copy in `views/src/types.ts`, bare `() => void` in `slot-registry.tsx` / `TypedRegistry.onChange` | Every consumer handles cleanup differently; composition helpers can't exist | +| No plugin→plugin service edge | `ecosystem/dependencies.ts` resolves **versions**, never objects | `dependencies` gates install order but grants no API access; a plugin cannot consume what another provides | +| No inject semantics | — | A plugin needing the AI surface, a connector, or another plugin's API has no way to say so, wait for it, or be unloaded when it disappears | +| `extraTools` never passed | `packages/plugins/src/ai-surface/service.ts:210` merge point; omitted by `apps/electron/src/main/agent-mcp-server.ts`, `packages/cli/src/commands/mcp.ts`, `packages/workbench/src/views/AiChatPanel.tsx:215` | `plugin_*` (9 tools, 0331), `lab_*`, all connector `agentTools`, and the auto-installed `WorkspaceAgentModule`'s tools reach **no model**. `ContributionRegistry.agentTools` is written by three files and read by nobody | +| Hot reload unwired | `workspace-plugins/watcher.ts` | The only code-as-data plugin path (source stored as `PluginSourceSchema` nodes) has no host mounting it | +| `registerSchema` stub | `context.ts:225` — empty `dispose()`, `// schemaRegistry.unregister would go here` | `contributes.schemas` is a no-op (0452 tracks the registry-side fix) | +| Config is static | `first-party-catalog.ts` config forms → `PluginConfigDialog` | A config edit has no partial-reload path; nothing like `scope.accept(keys)` exists | + +> [!NOTE] +> The gaps are all in one layer. Trust, gating, marketplace, contribution +> *collection* — solid. What happens *between* activation and deactivation — +> scopes, services, reload — is where xNet is a flat, static approximation of +> what Cordis makes dynamic. + +--- + +## External Research + +### The Cordis model, precisely + +Facts verified against the repo (`cordiverse/cordis`, MIT, 6,953★, created +2022-05-17), npm (latest `4.0.0-rc.8`, 2026-08-10, ~20k downloads/wk), the +v3-era README, and koishi.chat docs. + +**Context tree.** `new Context()` is the root; `ctx.extend()` creates children +via the JS prototype chain plus per-context metadata. Koishi builds filtered +contexts on top (`ctx.platform('discord').user('112233')`, plus +`intersect`/`union`/`exclude`); anything registered through a filtered context +— plugins, commands, listeners — is scoped to the filter. + +**Plugins as scopes.** A plugin is a function/class/`{ apply }` taking +`(ctx, config)`. `ctx.plugin(p, config)` returns a fork scope; +`fork.dispose()` reverts **every** collected effect. v4 renames the scope +machinery `Fiber` and makes effects explicit: `ctx.effect(runner)` registers a +revertible effect; disposers replay in **reverse order**; child fibers are +themselves effects on the parent, so disposing a context tears down its whole +subtree. A `dispose` event covers effects the framework can't auto-track +(the README's example: close the port you opened in `ready`). Plugins can be +**forked** — applied multiple times with per-fork config and per-fork +disposal (`export const reusable = true`). + +**Services with inject semantics.** A plugin declares +`export const inject = ['database']`. The contract (v3 README, verbatim +semantics): the plugin *"will not be loaded until the service becomes +truthy"*, is *"unloaded as soon as the service changes"*, and reloaded if the +new value is truthy. Services are provided by other plugins (v4: +`ctx.provide(name, value)` returns a disposer; a `Reflect` service throws +typed errors on undeclared access — `cannot get property "X" without +inject`). `ctx.isolate(name)` splits a service per subtree, so two instances +of the same service can coexist. Swapping a service implementation +automatically bounces every dependent plugin. + +**Reactive config.** `schemastery` (~89k downloads/wk — Cordis's most-adopted +piece) is a chainable schema that both validates config and auto-generates +config UIs. The loader calls `fork.update(config)`; a plugin can +`scope.accept(keys, cb)` to patch accepted keys in place instead of +restarting. Result: a config edit reloads only the plugins whose changed keys +demand it. + +**HMR.** `@cordisjs/plugin-hmr` watches files with chokidar, walks the module +dependency graph, disposes the affected plugins' fibers, and re-applies them +with fresh exports — the process never restarts for plugin code changes. +(It needs Node internals via `--expose-internals`; Koishi ships the same idea +as its "watcher".) + +
+The paper's framing: temporal and spatial composability + +The companion preprint (`cordiverse/paper`, 2,610★, draft 2026-08-13, PDF +only, **no named authors** — the "DeepSeek wrote it" framing in press coverage +is unverified) names the two halves: + +- **Temporal composability** — a removed component's side effects can be + fully reverted ("revertible effects"). This is the fiber/scope machinery. +- **Spatial composability** — dependencies between components are declared + and reactively managed ("reactive coeffects"). This is `inject`/`provide`. + +The mapping to xNet: we have a weak form of the first (flat disposables) and +none of the second. The vocabulary is useful even if the paper's provenance +is murky. + +
+ +### Ecosystem reality check + +| Signal | Value | Read | +| --- | --- | --- | +| Koishi community plugins | **4,551** (registry.koishi.chat, 2026-08-21) | The model scales to real ecosystems | +| `cordis` npm downloads | ~20k/wk | Small direct adoption outside Koishi/dsh | +| `schemastery` downloads | ~89k/wk | The config-schema piece travels furthest | +| DeepSeek Harness | vendors Cordis (219 `package.json` matches: `ui-cordis`, `tool-cordis`, `cordis-host-runner`…) | The star spike is dsh's, not organic Cordis growth | +| Maintainer | `shigma`, sole npm publisher across cordis/koishi/schemastery | Bus factor ≈ 1 | +| API stability | README: API "may change without notice"; v3→v4 renamed EffectScope→Fiber, changed `isolate` signature | Real churn, mid-rc | +| Docs | Standalone docs site dead; deep material zh-CN; best English API guide lives in a historical README commit | High adoption friction | + +### Criticisms that matter for us + +- **Trust model**: plugins run in-process with full reach — "good faith + rather than sandboxing" (Justin3go's dsh review, 2026-08-15). For a chatbot + framework that's tolerable; for a workspace holding a user's life it is + disqualifying. This is precisely the gap ADR-17 closes, and why "adopt + Cordis" and "keep our sandbox" cannot both be true for untrusted tiers. +- **Magic**: prototype-chain contexts, Proxy interception, + `this[Context.current]` caller tracking, TS declaration merging for typing. + Costs readability; pre-v4, an unavailable service silently read as + `undefined` (v4's typed reflect errors are the admission). +- **Over-engineering critique** (from dsh beta feedback): hot-reload + composability "benefits only edge cases"; agents that couldn't drive a + plugin correctly "just edit their own code instead". A useful caution for + 0331's agent-builds-plugins loop: the plugin API has to be *easier* than + forking the app, or agents will route around it. + +--- + +## Key Findings + +### 1. Contributions vs services — the two halves of a plugin system + +xNet's model is VS Code's, and says so +(`packages/workbench/src/contributions.tsx`: "Containers vs items, the VS +Code model"): plugins **declare contributions into fixed host registries**. +Cordis's model is a service container: plugins **provide and consume named +capabilities**, and the container re-resolves when providers change. These +are not rivals — VS Code itself has both (contribution points *and* an +exported-API/service layer). xNet has only the first. There is no way for +plugin B to use what plugin A provides; `dependencies` in the manifest +resolves version constraints, never objects. + +### 2. Disposables without scopes + +xNet has the leaf of Cordis's temporal model (everything returns a +`Disposable`; `ctx.subscriptions` auto-disposes on deactivate) and none of +the tree: no nested scopes, no reverse-order guarantee, no awaited teardown, +no fork (a plugin instantiated twice with different config), and three +inconsistent disposal conventions across packages. This is the smallest +change with the largest payoff, and it is invisible until you need it — hot +reload, per-feature toggles, and service bouncing all *require* scoped +disposal to be correct. + +### 3. The `extraTools` omission is the DI argument in one line + +```text + ┌──────────────────────────────┐ + plugin_* (9, built)──▶│ │ + lab_* (built)────────▶│ AiSurfaceService.extraTools │──▶ tools/list, dispatch + connector agentTools─▶│ (one merge point, service.ts│ + WorkspaceAgentModule─▶│ line 210) │ + └──────────────▲───────────────┘ + │ never passed by: + agent-mcp-server.ts ─┤ (Electron bridge) + cli mcp.ts ──────────┤ (xnet mcp serve) + AiChatPanel.tsx ─────┘ (in-app assistant) +``` + +One merge point, three construction sites, three independent omissions, and +every downstream tool family stranded — including the auto-installed +`WorkspaceAgentModule`, whose entire design is tools driving the shell. With +hand-threading, every new host must remember every provider. With a service +registry, `AiSurfaceService` *resolves* tool providers at construction and +re-resolves when a plugin activates or deactivates; all three sites become +correct by construction, and a newly activated plugin's tools appear in a +running session without restart — which is Cordis's `inject` reload semantics, +needed here for a concrete shipped feature. + +### 4. Hot reload exists here and is disconnected + +`createWorkspacePluginHotReloader` already does what Cordis HMR does — +rebuild on change, hot-swap the frame, crash → auto-disable with the +last-good hash pinned. The reason it's unwired is structural, not accidental: +Model A plugins (in-bundle, host realm) can't reload because their code isn't +data, and Model C (source-as-`PluginSourceSchema`-node, which can) has no UI +host. Wiring it is a 0452-ladder item (rung 4 prerequisite: "wire the +workspace-plugin tools before rung 4") — this doc adds the *why now*: it is +the temporal-composability half we already paid for. + +### 5. What Cordis validates about paths we already chose + +- **Registry-as-repo scales.** Koishi's 4,551 plugins ride an npm-scan + registry with marketplace metadata in `package.json` — structurally our + `registry/` + CI pipeline (0201, 0374) at larger scale. The pipeline shape + is right; our zero community plugins is a demand/capability problem, not an + infrastructure one. +- **Schema-driven config UIs.** schemastery's config forms are our + `first-party-catalog.ts` config specs + `PluginConfigDialog`. Same idea; + ours lacks the reload wire (a config save should `update(config)` the + plugin, not require toggle-off-on). +- **Everything-is-a-plugin needs a non-plugin referee.** Cordis's kernel + (Context/Fiber/Registry) is not itself a plugin. 0452's four exemptions + (kernel, shell, plugin system, protocol schemas) are the same line drawn + for the same reason. + +```mermaid +flowchart LR + subgraph Cordis["Cordis has"] + A[Effect scopes / fibers
reverse-order revert] + B[Service provide/inject
reactive rebind] + C[HMR without restart] + D[Reactive config
schemastery] + end + subgraph xNet["xNet has"] + E[Trust tiers + sandbox kinds
ADR-17] + F[Capability guards
guardStore / guardedFetch] + G[Fail-closed licensing,
consent, provenance] + H[Registry pipeline + CI
marketplace UI] + end + A -. missing in xNet .-> xNet + B -. missing in xNet .-> xNet + C -. built, unwired .-> xNet + D -. forms only, no reload .-> xNet + E -. absent in Cordis .-> Cordis + F -. absent in Cordis .-> Cordis + G -. absent in Cordis .-> Cordis +``` + +--- + +## Options And Tradeoffs + +### Option A — Adopt Cordis as a dependency + +Replace `PluginRegistry`/`ExtensionContext` internals with `cordis` contexts; +xNet plugins become Cordis plugins. + +- ✅ Battle-tested scope/service machinery for free; HMR for free. +- ❌ **Trust mismatch is fatal**: Cordis composes objects in one realm. + xNet's `user` and `marketplace` tiers run behind an iframe/SES boundary + where only JSON-pure RPC crosses (`workspace-plugins/protocol.ts`). A + Cordis service edge cannot cross that boundary; we'd be adopting the + framework precisely where it can't reach. +- ❌ Bus factor 1, API mid-rc and churning (EffectScope→Fiber), docs + effectively zh-CN only. +- ❌ Proxy/prototype/declaration-merging magic contradicts the repo's + fail-loud, grep-able style ("a silently absent host is indistinguishable + from a broken shell" — `workbench/src/host.ts`). + +### Option B — Import the mechanisms, not the framework ⭐ + +Build three small, typed, boring pieces inside `packages/plugins`, behind the +seams that already exist: an effect-scope primitive, a service registry with +inject semantics, and the reload wiring. Host-realm (first-party) plugins get +direct service objects; sandboxed tiers get the same contract tunneled over +the existing RPC — the service *names and availability semantics* are shared, +the transport differs by trust tier. This keeps ADR-17 as the outer law and +Cordis's composition as the inner mechanics. + +- ✅ Fixes the `extraTools` class of bug structurally; unblocks agent tools + (0331/0447), lab tools, connector tools in one move. +- ✅ Unifies three disposal conventions; makes hot reload and per-feature + toggles correct instead of racy. +- ✅ Zero new dependencies; every piece is ~100–300 lines with tests. +- ❌ Real design work (service availability across async activation; the + RPC-tunneled variant for sandboxed tiers can ship later). + +### Option C — Status quo (VS Code contributions are enough) + +- ✅ No work. +- ❌ The `extraTools` gap stays a whack-a-mole: every future host of every + future service repeats the omission. 0452's ladder lands on a runtime with + unordered teardown and no plugin→plugin edges, and 0331's loop stays + shelf-ware. + +> [!IMPORTANT] +> This proposes no revenue lane, so Charter §6's three tests are not in +> play. It changes no wire format and no public manifest field — `inject` +> and `provides` enter the manifest as *optional* additions, which is why the +> door is two-way. + +--- + +## Recommendation + +**Option B**, in three steps ordered so each one ships value alone, aligned +with 0452's ladder (its step 1 registry work and this doc's step 2 service +work both live in `packages/plugins/src/`): + +1. **Effect scopes** (`packages/plugins/src/scope.ts`). One `EffectScope` + class: `use(disposable)`, `child()`, `dispose()` — reverse-order, awaited, + idempotent, re-entrancy-safe. `ExtensionContext.subscriptions` becomes a + scope; `PluginRegistry.deactivate` awaits it; + `packages/react/src/context.ts` awaits teardown before remount. Adopt one + `Disposable` type repo-wide (`() => void | Promise` accepted at the + boundary, normalized inside). + +2. **Service registry with inject semantics** + (`packages/plugins/src/services.ts`). `provide(name, value): Disposable` + and `inject(names, (services) => scopeBody)` where the body runs when all + names are available, is disposed when any disappears, and re-runs on swap + — Cordis's contract, minus proxies: explicit registration, typed lookup, + loud `ServiceUnavailableError`. First consumer: `AiSurfaceService` + resolves `agent-tools` providers from the registry, and + `ContributionRegistry.agentTools` gets its first reader. Wire all three + hosts through it; delete the three hand-threaded omissions. Manifest gains + optional `provides?: string[]` / `inject?: string[]` (validated, unlike + most contribution kinds today). + +3. **Reload wiring.** Mount the 0331 workspace-plugin host + hot reloader in + the workbench dev surface (the 0452 gate: "the honesty test can actually + be run by an agent"); route `PluginConfigDialog` saves through a + `registry.update(pluginId, config)` that bounces only the plugin's scope + (partial-accept à la `scope.accept` can come later; full bounce is + correct-if-slower first). + +```mermaid +sequenceDiagram + participant P as Plugin (activate) + participant SR as ServiceRegistry + participant AI as AiSurfaceService + participant M as Model session + P->>SR: provide('agent-tools:my-plugin', tools) + SR-->>AI: availability change (inject re-run) + AI->>AI: merge into extraTools (dedupe, first wins) + M->>AI: tools/list + AI-->>M: built-ins + plugin tools + Note over P,SR: plugin deactivates → scope disposes → provide() reverted + SR-->>AI: availability change + AI->>AI: tools removed — no restart, no stale dispatch +``` + +Explicitly **not** recommended: forked/multi-instance plugins (no current +need; revisit if a connector wants two accounts of one service), context +filtering (Koishi's session selectors have no xNet analogue), schemastery +(our config specs already exist), and any Proxy-based context sugar. + +## Example Code + +```ts +// packages/plugins/src/scope.ts — the temporal half (sketch) +export type Effect = { dispose(): void | Promise } | (() => void | Promise) + +export class EffectScope { + private effects: Effect[] = [] + private children = new Set() + private state: 'active' | 'disposing' | 'disposed' = 'active' + + use(effect: T): T { + if (this.state !== 'active') throw new ScopeDisposedError() + this.effects.push(effect) + return effect + } + + child(): EffectScope { + const scope = new EffectScope() + this.children.add(scope) + this.use(() => scope.dispose()) + return scope + } + + async dispose(): Promise { + if (this.state !== 'active') return + this.state = 'disposing' + // reverse order — later effects may depend on earlier ones + for (const effect of this.effects.reverse()) { + try { + await (typeof effect === 'function' ? effect() : effect.dispose()) + } catch (error) { + // loud, but one failed disposer must not strand the rest + console.error('[plugins] effect dispose failed', error) + } + } + this.effects = [] + this.state = 'disposed' + } +} +``` + +```ts +// packages/plugins/src/services.ts — the spatial half (sketch) +export class ServiceRegistry { + provide(name: string, value: T): Disposable + get(name: string): T // throws ServiceUnavailableError — never undefined + /** body runs when all names resolve; its scope is disposed when any + * provider goes away; re-runs if a provider is swapped. */ + inject(names: string[], body: (scope: EffectScope) => void | Promise): Disposable +} + +// The first consumer — AiSurfaceService resolves instead of being handed: +const surface = createAiSurfaceService({ store, schemas, retrieveContext, services }) +// inside: services.inject(['agent-tools'], (scope) => this.mergeExtraTools(...)) +// — agent-mcp-server.ts, cli mcp.ts, AiChatPanel.tsx no longer each +// need to remember; a plugin activating mid-session adds its tools live. +``` + +## Risks And Open Questions + +- **Scope creep into a framework.** The failure mode is rebuilding Cordis. + Guard: each piece needs a named first consumer before it merges (scopes → + registry teardown; services → `extraTools`; reload → 0331 host). No + speculative features. +- **Service edges across the sandbox boundary.** A `user`-tier iframe plugin + cannot receive a live object. The contract: sandboxed plugins see services + only as RPC-tunneled, JSON-pure facades, and *providing* a service from a + sandboxed plugin is out of scope until a real case exists. The registry + must refuse (loudly) to hand a host-realm object across the boundary — + this is where ADR-17's line must hold against convenience. +- **Availability semantics vs async activation.** `PluginRegistry.activate` + is async; inject bodies must not observe half-activated providers. Cordis + gates non-immediate services on `ready`; we need an equivalent rule + (provide only at the end of `activate`, enforced or linted). +- **Unload semantics for live sessions.** When a provider disappears + mid-conversation, in-flight tool calls need a defined failure (typed error + to the model, not a hang). The scope model makes this expressible; it + still has to be decided. +- **Does the agent actually want plugins?** dsh's beta feedback (agents + editing their own code rather than driving plugins) is a live risk for + 0331's loop. Mitigation is DX, not architecture: the `plugin_*` tools must + be cheaper for an agent than a source edit, and the preview/feedback loop + (`preview.ts`) is the leverage. +- **Open question:** should `FeatureModule.capabilities` and + `PluginPermissions` unify while we're in the file? (Today they're bridged + by an untyped cast at `registry.ts:542`.) Probably yes, but it's severable + and shouldn't ride this change. + +## Implementation Checklist + +**Status:** ░░░░░░░░░░ 0/10 items + +- [ ] `EffectScope` in `packages/plugins/src/scope.ts` with reverse-order, + awaited, idempotent disposal + tests (incl. re-entrancy and a failing + disposer not stranding the rest) +- [ ] Unify the `Disposable` conventions: one exported type in + `@xnetjs/plugins`, `packages/views` re-exports it, `slot-registry` / + `TypedRegistry.onChange` return it +- [ ] `ExtensionContext.subscriptions` backed by an `EffectScope`; + `PluginRegistry.deactivate` awaits scope disposal; + `packages/react/src/context.ts` awaits teardown before remount +- [ ] `ServiceRegistry` in `packages/plugins/src/services.ts` — + `provide`/`get`/`inject`, loud `ServiceUnavailableError`, availability + re-resolution on provide/dispose, + tests +- [ ] Optional `provides` / `inject` manifest fields with real validation + (unlike the 14 unvalidated contribution kinds — don't add a 15th) +- [ ] `AiSurfaceService` resolves agent-tool providers from the registry; + `agentToolsAsExtraTools` bridge registered as a provider reading + `ContributionRegistry.agentTools` (its first reader) +- [ ] Wire all three hosts (`apps/electron/src/main/agent-mcp-server.ts`, + `packages/cli/src/commands/mcp.ts`, + `packages/workbench/src/views/AiChatPanel.tsx`) through the resolved + surface; verify `plugin_*` and `WorkspaceAgentModule` tools reach a + live session on each +- [ ] Register `createWorkspacePluginAgentTools()` output as an + `agent-tools` provider (closes the 0331/0447 "built but unwired" gap) +- [ ] Mount the workspace-plugin frame host + `createWorkspacePluginHotReloader` + behind a dev-surface entry point (coordinate with 0452 rung + prerequisites) +- [ ] `PluginRegistry.update(pluginId, config)`: full scope bounce on config + save from `PluginConfigDialog` + +## Validation Checklist + +- [ ] Unit: disposing a parent scope disposes children first-in-reverse and + awaits async disposers; a throwing disposer doesn't strand later ones +- [ ] Unit: `inject` body re-runs on provider swap and is disposed when a + provider goes away; `get` on a missing service throws typed +- [ ] Integration: activate a plugin contributing `agentTools` mid-session → + `tools/list` over the MCP server includes it without restart; + deactivate → it disappears and an in-flight call fails typed +- [ ] Integration: all three hosts pass the same test above (no + per-host omission possible — the test constructs each host) +- [ ] E2E-ish: edit a `PluginSource` node → hot reloader rebuilds and swaps + the frame; a crashing build auto-disables with last-good pinned + (existing `workspace-plugins-watcher.test.ts` promoted to a wired host) +- [ ] `pnpm build && pnpm typecheck && pnpm test` green; api-report updated + for `@xnetjs/plugins` new exports; changeset written + +## References + +- [cordiverse/cordis](https://github.com/cordiverse/cordis) — repo; v3 README + (historical commit `261ee6be`) has the best English API guide +- [cordiverse/paper](https://github.com/cordiverse/paper) — "A Programming + Paradigm for Spatiotemporal Composability" (draft 2026-08-13; no named + authors) +- [Koishi plugin docs](https://koishi.chat/en-US/guide/plugin/) — plugin, + [service](https://koishi.chat/en-US/guide/plugin/service.html), and + [filter](https://koishi.chat/en-US/guide/plugin/filter.html) guides +- [registry.koishi.chat/index.json](https://registry.koishi.chat/index.json) + — 4,551 plugins (2026-08-21) +- [DeepSeek Harness cordis primer](https://deepseek-harness.github.io/deepseek-harness/reference/cordis-primer) +- [Justin3go's dsh review](https://justin3go.com/en/posts/2026/08/15-deepseek-harness-review) +- Repo: [0452](./0452_[_]_HOW_FAR_TO_PLUGINIZE_THE_KERNEL_THE_SHELL_AND_THE_LIFT_OUT_LADDER.md) + (lift-out ladder), [0331](./0331_[x]_DEVELOPING_XNET_FROM_INSIDE_XNET_SPEC_TO_PLUGIN_LOOP.md) + (workspace-plugin runtime), [0206](./0206_[_]_WHY_SO_FEW_FIRST_PARTY_PLUGINS.md) + (lift-out test), [0205](./0205_[_]_DECOMPOSING_THE_APP_INTO_PLUGINS.md), + [0194](./0194_[_]_EXTENSIBILITY_FABRIC_PLUGINS_LABS_AI_EDITOR.md) (unify the + four extensibility systems), [0397](./0397_[_]_AGENT_NATIVE_FRAMEWORK_LESSONS.md) + (prior framework-comparison doc), ADR-17 in + `site/src/content/docs/docs/architecture/decisions.mdx` From 4ac0ae1688b5a653de7574ee9fbc63d8a547db1f Mon Sep 17 00:00:00 2001 From: xNet Test Date: Fri, 21 Aug 2026 17:59:08 -0700 Subject: [PATCH 02/16] =?UTF-8?q?docs(exploration):=20explore=20the=20entr?= =?UTF-8?q?y=20vector=20=E2=80=94=20the=20agent=20door=20first?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Signed-off-by: xNet Test --- ...6_[_]_ENTRY_VECTOR_THE_AGENT_DOOR_FIRST.md | 501 ++++++++++++++++++ 1 file changed, 501 insertions(+) create mode 100644 docs/explorations/0456_[_]_ENTRY_VECTOR_THE_AGENT_DOOR_FIRST.md diff --git a/docs/explorations/0456_[_]_ENTRY_VECTOR_THE_AGENT_DOOR_FIRST.md b/docs/explorations/0456_[_]_ENTRY_VECTOR_THE_AGENT_DOOR_FIRST.md new file mode 100644 index 000000000..d79890578 --- /dev/null +++ b/docs/explorations/0456_[_]_ENTRY_VECTOR_THE_AGENT_DOOR_FIRST.md @@ -0,0 +1,501 @@ +--- +title: Entry vector — the agent door first +status: draft +last_updated: 2026-08-21 +review: 2026-11-19 # one quarter of focused execution, then re-score against the dogfood gate and the Buzz/Notion lane movement. 90-day default genuinely fits: this is a focus decision, not research. +decider: Chris Smothers +door: two-way # a focus and sequencing decision — no wire format, no public API, no revenue lane changes; every deferred item stays in the repo with its exploration intact +tags: [strategy, focus, agents, plugins, go-to-market, roadmap] +--- + +# Entry vector — the agent door first + +> [!TIP] +> **TL;DR** — Pick one door: **the agent door**. The entry +> vector is `xnet connect claude-code` — "give your coding agent a workspace +> you own" — because it is the only surface in the repo that is already a +> single command, already on npm, already differentiated (0.11x MCP token +> benchmark), and sits in the one quadrant of the agent-workspace lane +> (local-first + agents extend the workspace *from within*) that Notion, +> Cowork, Buzz, and DeepSeek Harness have not taken. "Self-improving xNet" +> is not pie in the sky — 0331 already built the runtime and 0455 showed the +> loop is roughly three wiring PRs from closed. The open-source-Notion lane +> (~1% star→user conversion, Logseq dead of a rewrite) and the local-first +> SDK lane (zero breakouts) are not entry vectors; cloud becomes one only +> after something pulls people toward it. This doc adds **no new program** +> — it sequences four existing checklists (0335 → 0455 → 0447 → positioning) +> into one focus stack and names what is explicitly parked. + +## Problem Statement + +The founder's own words, condensed: *I want xNet to be self-improving — +agents integrate seamlessly and extend it from within — but that feels far +away. I want to ship something people actually use, but I don't know if +that's an open-source Notion, the framework/React hooks, xNet Cloud and easy +self-hosting, or the plugin substrate. I want Lego bricks: build once, every +developer and user after that is more productive. What's the first entry +vector?* + +This is not a new question for the repo. `docs/ROADMAP.md` (July 2026) +already bet on three pillars in dependency order — AI daily driver, then +effortless cloud, then the commons — gated on the founder's own daily use. +The overwhelm is real anyway, for three reasons this doc addresses head-on: + +1. **The site still offers every door at once.** The hero renders App / SDK / + Protocol as three equal doors (`site/src/components/sections/Hero.astro`); + `GetStarted.astro` lists three unranked paths. A stranger cannot tell what + xNet is *first*. +2. **The "self-improving" goal feels distant** — but the feeling is + miscalibrated. The audit in + [0455](./0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md) found the + agent-builds-plugins runtime (0331) fully built and tested with zero + callers, stranded behind one unpassed constructor argument. +3. **Intent accumulates without closing.** + [0421](./0421_[-]_FAST_WHAT_COLLISONS_LIST_MEASURES_AND_WHAT_XNET_LACKS.md) + measured it: +85 unstarted explorations/month, 524 docs, and "xNet's + problem is not that work moves slowly, it is that intent accumulates + without ever being closed" (0421's own words). The cure for overwhelm is + not another program; it is sequencing what exists. + +## Executive Summary + +| Question | Answer | +| --- | --- | +| What is the entry vector? | **The agent door**: `xnet connect claude-code` — one command that gives a coding agent a workspace the user owns. Everything else (SDK, cloud, app) becomes a *second* step people take after that door works for them. | +| Is "self-improving xNet" pie in the sky? | No — it's mislabeled near-done work. The 0331 iframe plugin runtime + hot reloader exist with 7 test files and zero callers; 0455's checklist closes the loop in ~3 PRs (service registry → `extraTools` wired → `plugin_*` tools live → host mounted). | +| Why not open-source Notion? | The lane converts stars to daily users at roughly 1% (AppFlowy ~70k★ / ~46k MAU), its most famous member (Logseq) stalled fatally mid-rewrite, and winners there won by *narrowing* (Outline = team wiki). Head-on Notion marketing is the weakest use of a solo founder's quarter. | +| Why not the SDK/framework? | No local-first framework has broken out (Electric pivoted to Postgres reads, Liveblocks open-sourced defensively, Jazz still pre-traction); even sympathetic engineers warn "local-first… is not a default." Keep the SDK shipped and honest; don't lead with it. | +| Why not cloud first? | Supabase/n8n prove self-host wedges work — but each rode a one-sentence job people already wanted. Cloud amplifies demand; it doesn't create it. xNet Cloud's deploy workflow is literally inert today (`deploy-cloud.yml`: "INERT BY DEFAULT"). Turn it on when the agent door creates pull. | +| Is the agent lane still open? | The lane is crowding (Notion agent hub May 2026, Anthropic Cowork Feb 2026, Block's Buzz July 2026, DeepSeek Harness Aug 2026) — but the specific quadrant **local-first, user-owned substrate where agents extend the workspace from within** is unclaimed. Buzz is the nearest neighbor (0416's thesis competitor, confirmed) and it is Nostr-relay-centric, not local-first, with a harness-out rather than workspace-in plugin story. | +| What about the Lego bricks? | The bricks the founder wants to build ARE pillar 1 — but the cold-start evidence (VS Code, Obsidian, Raycast vs ChatGPT plugins) says ecosystems thrive only on an existing devoted user base. At n≈1, that user base is **the founder plus their agents**. Build the bricks your own agents snap together this quarter; the community comes after the demo is undeniable. | +| What closes this doc? | The focus stack shipped (checklist below) and the roadmap's own gate: consecutive weeks of the founder's real work done inside xNet. | + +--- + +## Current State In The Repository + +The full shippability audit is summarized here; the load-bearing facts each +carry a path. + +### What is genuinely shippable today + +| Surface | Evidence | Verdict | +| --- | --- | --- | +| `xnet connect claude-code\|codex` | `packages/cli/src/commands/connect.ts` — idempotent, fenced CLAUDE.md edits, MCP registration, `xnet doctor --agent-access` self-check; read-only by default, `--writes` opt-in; `@xnetjs/cli@0.4.0` on npm | ✅ **The one single-command entry vector that already exists** | +| Agent lanes | `site/src/content/docs/docs/guides/agent-interfaces.mdx` — CLI verbs → vault checkout → MCP fallback; benchmark: **0.11x the tokens of an MCP toolset at equal success on 15 tasks** | ✅ Differentiated and measured | +| Web demo | `apps/web/src/boot/` + `/app?demo=1` — 2 steps, ~10–20 s cold start (SQLite WASM + OPFS), auto-seeded, never overwrites user content (`demo-seed.ts`) | ✅ Good | +| npm data layer | 18 public packages at 3.0.0 (`core, data, react, sync, sqlite, …`), OIDC + provenance releases (`.github/workflows/npm-release.yml`) | ✅ Real | +| React SDK standalone | `examples/minimal-app/` — outside the workspace, works against published npm, syncs via `wss://hub.xnet.fyi` | ✅ Under-marketed | +| Self-host hub | `packages/hub/Dockerfile`, `docker-compose.hub.yml`, root `railway.toml`, multi-arch ghcr image | ✅ Strongest distribution story | + +### What is not, despite appearances + +| Surface | Evidence | Verdict | +| --- | --- | --- | +| xNet Cloud | `apps/cloud/src/server.ts` is a real Hono control plane with ~30 test files — but `.github/workflows/deploy-cloud.yml` is **"INERT BY DEFAULT"**, defaults are in-memory providers, billing gateway 503s unset, and `site/src/data/status.json` is a two-month-stale snapshot while `site/src/data/pricing.ts` deep-links every CTA to `cloud.xnet.fyi/auth/start` | ❌ Largest claim/state gap in the repo | +| UI layer on npm | `ui, editor, views, canvas, workbench, dashboard, charts` all private/changeset-ignored | ❌ Devs can't install the components in the screenshots | +| Mobile | Expo Go demo only; `site/src/pages/mobile.astro` says so honestly | ❌ Deliberately deferred (roadmap) | +| Traction signal | `site/src/data/metrics.json` has `"sample": true`; `cloud-metrics.yml` inert; no waitlist, no testimonials, no user count anywhere; telemetry charter-banned | — Zero external signal exists, by design and by stage | +| Agent tools loop | `AiSurfaceService.extraTools` never passed by any of the three hosts; `plugin_*` (9 tools), `lab_*`, `WorkspaceAgentModule` tools all stranded (0455) | 🚧 Built, unwired | +| npm-facing story | `packages/cli/README.md` still describes schema-migration tooling; **does not mention `connect`, `checkout`, `commit`, or `mcp`** | 🚧 The best feature is unmarketed | + +> [!WARNING] +> One real hazard rides the current download page: +> `apps/electron/src/renderer/main.tsx:181` still defines `makeTestKey` +> (deterministic, source-derivable signing key; "DO NOT use in production!") +> and line 887 still calls it, while `secure-seed.ts` sits uncalled. This is +> release-blocker #1 from the +> [0335 audit](./0335_[_]_RELEASE_READINESS_AUDIT_WHAT_STANDS_BETWEEN_XNET_AND_A_WELL_RECEIVED_LAUNCH.md), +> unfixed, on a binary `site/src/pages/download.astro` distributes today. A +> product whose pitch is *you own your keys* cannot lead with a key any +> reader of the repo can reconstruct. It is first in the focus stack for +> that reason. + +### What the roadmap already decided + +`docs/ROADMAP.md` (July 2026): the bet is *"deep AI integration with total +visibility, on top of a malleable, sandboxed workspace"* — three assets +nobody else has (signed change log per node, workspace-as-Lego, plugins as +sandboxed xNet artifacts). Pillar order: AI daily driver → effortless cloud +→ commons. Principle 5: **"Dogfood is the metric"** — the gate is +consecutive weeks of the author's real work inside xNet. Deliberately +ignored: verticals, mobile parity, OS-level, marketplace-scale distribution. + +This exploration's job is to test that bet against August 2026 evidence and +convert it into a single entry vector with a closable checklist. Spoiler: +the bet survives, strengthened. + +--- + +## External Research + +Full sourcing in the research notes; the decision-relevant findings: + +### Lane 1 — "Open-source Notion": stars ≠ users + +- AppFlowy: ~70k stars, **~46k MAU** per third-party trackers (~0.66 MAU per + star). AFFiNE: ~60k stars, and its growth team publishes literal + how-to-get-stars playbooks — stars are a marketing KPI there, not a user + metric. Anytype: ~$13.5M raised, niche. +- **Logseq is the cautionary tale**: a database rewrite begun in 2022 + consumed the project; last stable release April 2024; by 2026 the + community writes migration guides away from it. +- The lane's survivors narrowed: Outline won "team wiki with Docker deploy," + not "all of Notion." + +### Lane 2 — Local-first frameworks: no breakout, ever + +- npm reality (2026): Convex ~100k weekly downloads (and it sells a + *backend*, not local-first), InstantDB ~20k, ElectricSQL ~15k after + pivoting to narrow Postgres read-path sync. Liveblocks open-sourced its + engine in Feb 2026 — a defensive move. Jazz remains pre-traction. +- The movement's own engineers say it: local-first "is not a default" + (Supabase engineer critique of teams adopting sync engines for 50-user + apps). Linear sold the *pattern*, not any framework — and built in-house. + +### Lane 3 — Self-host wedges: naming is the multiplier + +- Supabase went **8 → 800 hosted databases in three days** by renaming + itself "the open-source Firebase alternative" — same product. Now $170M + ARR / $10.5B (June 2026), lifted by AI-coding demand. n8n: $5.2B after SAP + (May 2026) as the default self-hosted agent-workflow layer. Cal.com rode + "open-source Calendly" — then went closed-source April 2026. +- The pattern: a one-sentence wedge naming a job people already want, plus a + one-click deploy. The deploy xNet has (`railway.toml`, ghcr image); the + sentence it doesn't. + +### Lane 4 — The agent-workspace lane: crowding, with one quadrant open + +- **Notion** turned its workspace into "a hub for AI agents" (developer + platform, May 13 2026) — top-down, cloud, 100M users. **Anthropic Cowork** + (Feb 2026): enterprise agent plugins/marketplaces. **Block's Buzz** + (July 21 2026, Apache 2.0, ~15–25k stars in weeks): self-hostable + workspace where agents are full members with cryptographic identity — + xNet's quadrant neighbor and 0416's predicted thesis competitor, **but** + Nostr-relay-centric (not local-first CRDT) and harness-out (ACP drives + Goose/Codex/Claude Code at the workspace) rather than workspace-in + (agents building sandboxed plugins *inside* it). **DeepSeek Harness** + (Aug 2026): "everything is a plugin," commoditizing plugin architecture + for harnesses — a harness, not a workspace (and per ADR-29 / 0416, xNet + is deliberately not a harness). +- Open as of today: **local-first, user-owned data as the agent substrate** + and **the agent extending the workspace by writing sandboxed plugins into + it**. Nobody has shipped that loop as a product. + +### The plugin cold-start table + +| Platform | Users first? | Plugin launch gap | Outcome | +| --- | --- | --- | --- | +| VS Code | Preview Apr 2015 | +7 mo | 500k MAU + 1,000 extensions at 1.0 | +| Obsidian | May 2020, small rabid base | +~6 mo | 6.8k plugins, 120M downloads, ~1.5M MAU | +| Raycast | Oct 2020 | +13 mo | 100+ community extensions in a month | +| Figma | 2016, $25M revenue first | +3 yr | Plugins became the PLG flywheel | +| ChatGPT plugins | **Plugins were the launch** | 0 | **Killed Apr 2024** — "most users never enabled plugins" | + +> [!IMPORTANT] +> The ecosystem the founder wants ("once I build it, every developer after +> me is more productive") historically only ignites on top of an existing +> devoted user base — even a tiny one. The corollary is not "abandon the +> plugin model"; it is **build the plugin loop for the user base you already +> have: yourself and your agents.** That is also exactly what the roadmap's +> dogfood gate demands. DeepSeek Harness is the apparent counterexample +> (plugins-first, exploding) — but its users *are* developers and the plugin +> *is* the product, which is precisely the agent-door framing, not the +> marketplace framing. + +### Solo-founder focus wisdom, the two load-bearing points + +- Paul Graham, *Do Things That Don't Scale*: recruit users manually; build + for one user at a time; growth rate on a tiny base beats a launch. +- Nadia Eghbal, *Working in Public*: for a solo maintainer, a big undirected + community is a **cost**; the failure mode is attention-consuming + low-value participation, not obscurity. Optimize for users, not + contributors — which cuts against investing in marketplace/community + mechanics before demand exists. + +--- + +## Key Findings + +1. **The decision was already made; the evidence now confirms it.** July's + pillar order (daily driver → cloud → commons) matches what the lane + research independently concludes: product-with-devoted-users before + ecosystem, demand before cloud, content before commons. The overwhelm is + a positioning and sequencing problem, not a strategy vacuum. + +2. **The entry vector already exists and is unmarketed.** `xnet connect + claude-code` is one command, on npm, benchmarked, differentiated, safe by + default (read-only until `--writes`), and aligned with every current + trend (agents everywhere, MCP fatigue, token cost pressure). Its own + README doesn't mention it. The Supabase lesson says fixing *that* — the + sentence and the door — is the highest-leverage cheap work in the repo. + +3. **"Self-improving xNet" is three PRs away, not a moonshot.** The + pie-in-the-sky feeling comes from mislabeling: 0331 built the sandboxed + plugin runtime (iframe host, builder, store RPC, hot reloader — 7 test + files); 0455 diagnosed the single missing wire (`extraTools` never + passed) and wrote the checklist (effect scopes → service registry → + wire three hosts → mount the dev surface). Hot module reloading isn't + the far future — it's the shelf. + +4. **The Lego-brick instinct is right; the audience is wrong-sized.** Bricks + compound only when someone is building. This quarter the builders are the + founder and their agents. Every brick should be judged by one test: *does + it make my own agent measurably better at doing my real work inside xNet + this week?* That test kills marketplace mechanics, community + infrastructure, and SDK marketing for now — and green-lights exactly the + 0455/0447 wiring. + +5. **Buzz's existence is clarifying, not threatening.** It validates the + quadrant (someone at Block believed enough to ship it) and leaves xNet's + two differentiators intact: local-first CRDT ownership (Buzz is + relay-centric) and the in-workspace plugin loop with a signed audit + trail (Buzz drives external harnesses). But it moves faster than a solo + founder on breadth — which argues for depth on the loop no one else has, + not breadth-matching. + +6. **One honest-to-goodness blocker gates all marketing:** the deterministic + Electron signing key (0335 #1). "Own your keys" cannot be the pitch while + the shipped desktop key is derivable from source. + +```mermaid +flowchart TD + subgraph LANES["The four lanes, scored"] + A["Open-source Notion
~1% star→user, Logseq ☠"] + B["Local-first SDK
zero breakouts"] + C["Cloud / self-host
works only WITH a wedge"] + D["Agent substrate
crowding, one quadrant open"] + end + D -->|"the open quadrant"| Q["local-first + user-owned +
agents extend from WITHIN"] + Q --> V["Entry vector:
xnet connect claude-code"] + V --> LOOP["The loop: agent builds sandboxed
plugins inside your workspace,
every change signed & visible"] + LOOP -->|"creates pull"| C + LOOP -->|"creates content"| COMMONS["Commons / Index (last)"] + A -.->|"not the entry"| V + B -.->|"supporting surface"| V +``` + +--- + +## Options And Tradeoffs + +### Option A — Open-source Notion: market the app to end users + +Polish the web/desktop app, launch on HN/Product Hunt as the local-first +Notion alternative. + +- ✅ The demo is genuinely good (2 steps, auto-seeded); the lane has proven + *star* demand. +- ❌ ~1% star→daily-user conversion; crowded (AppFlowy, AFFiNE, Anytype all + better-funded); a solo founder competing on end-user polish against teams + loses on breadth; and stars would flood a solo maintainer with exactly the + low-value participation Eghbal warns about. +- ❌ Desktop can't be marketed at all until the 0335 key blocker is fixed. + +### Option B — The framework: market the React SDK + +Lead with `@xnetjs/react`, publish the UI packages, build `create-xnet`. + +- ✅ `examples/minimal-app` is real; hooks are stable at 3.0.0. +- ❌ The lane has produced zero breakouts in five years; the UI layer is + private so the differentiating half isn't installable; "local-first + framework" is an architecture pitch, and architecture pitches don't + convert (Electric's pivot is the proof). + +### Option C — Cloud first: finish and launch xNet Cloud + +Turn on `deploy-cloud.yml`, swap in real providers, launch pricing. + +- ✅ The code is closer to done than it looks; Supabase/n8n prove the + self-host+cloud model. +- ❌ Wrong order: every self-host winner rode existing demand for a named + job. Standing up billing/provisioning/support for zero pulled users is + pure operational drag on a solo founder. Cloud is pillar 2 for a reason — + *"cloud = amplifier not landlord"* and amplifiers need a signal. + +### Option D — The agent door ⭐ + +One entry vector: **connect your coding agent to a workspace you own.** +Close the self-improving loop for an audience of one (founder + agents), +fix the key blocker, then say one sentence loudly. + +- ✅ Ships this quarter from existing checklists (0335, 0455, 0447); the + only lane quadrant still open; the only surface already reduced to one + command; matches the dogfood gate exactly; produces the demo no one else + can record (agent builds a sandboxed plugin inside the workspace, every + change signed, hot-reloaded live). +- ✅ Pulls the other lanes behind it: agent users need sync → cloud demand; + agent-built plugins need publishing → commons content; devs who see the + loop want the SDK. +- ❌ n=1 risk: the gate is subjective (the founder's own weeks-of-use) and + there is deliberately no telemetry to contradict self-report. Mitigation: + the validation checklist requires at least one outside person completing + the flow, recruited manually, PG-style. +- ❌ Lane risk: Notion/Buzz could ship the in-workspace loop. Watch, don't + match — depth on ownership + visibility is the moat they'd have to + rebuild their foundations to copy. + +### Option E — Status quo: keep all three doors open + +- ✅ No decision required. +- ❌ This *is* the overwhelm. Three unranked doors on the hero, a stale CLI + README, an inert cloud, and +85 unstarted docs/month is what "no entry + vector" looks like from the inside. + +> [!NOTE] +> No new revenue lane is proposed (existing cloud pricing stands), so the +> Charter §6 ground-rent tests are not triggered. The focus choice is +> two-way: if the quarter disproves the agent door, options A–C remain +> exactly where they are, minus nothing. + +--- + +## Recommendation + +**Option D — the agent door, as a strict sequence.** The quarter's rule: +every week's work must serve the sentence *"point your coding agent at a +workspace you own, and watch it build you tools inside it."* + +**The focus stack** (order matters; each unblocks the next): + +1. **Safety before speech** — fix 0335 blocker #1: Electron boots on + `secure-seed.ts` (safeStorage-backed), `makeTestKey` dies or becomes + test-only. Nothing gets marketed while the key story is false. +2. **Close the loop** — execute the 0455 checklist (effect scopes → service + registry → `extraTools` wired in all three hosts → `plugin_*` + + `WorkspaceAgentModule` tools live → workspace-plugin host and hot + reloader mounted behind a dev surface). This is 0447's "wire the loop," + now with a mechanism-level plan. Exit criterion: *from a Claude Code + session, an agent scaffolds, previews, and installs a sandboxed plugin + into the founder's real workspace, and the change log shows every step.* +3. **Dogfood ruthlessly** — the roadmap gate, made legible: a running + dogfood ledger (a page *in xNet*) logging each week the founder's real + work happened inside it, and what forced a fallback to other tools. Each + fallback is the next week's highest-priority fix. This is the + self-improvement flywheel at n=1 — the system improving because its user + lives in it, before any HMR mysticism. +4. **Say one sentence, everywhere** — reposition around the door: + `packages/cli/README.md` rewritten around `connect`/`checkout`/`mcp` + (it currently sells 2024's schema tooling); the hero's primary CTA + becomes the agent quickstart with app/SDK demoted to secondary doors; + one launch-register blog post (the corpus has 24 essays and zero + launches) with the recorded loop demo; the 0.11x-tokens benchmark made + reproducible (script + methodology in-repo) since it will be challenged + the moment it's quoted. +5. **Recruit manually** — PG-style: personally onboard 3–10 people who + already live in Claude Code/Codex, watch them run `xnet connect`, fix + what snags them. No waitlist, no community infrastructure, no Discord + moderation surface. (Eghbal: contributors are a cost; users are the + asset.) + +**Explicitly parked this quarter** (each keeps its exploration; none is +withdrawn): cloud GTM (tripwire to un-park: an outside user asks for hosted +sync twice), UI packages on npm, `create-xnet`, marketplace/community +mechanics, Index/commons build-out, mobile, matching Buzz features, +open-source-Notion positioning. + +```mermaid +sequenceDiagram + participant U as Founder (n=1 user) + participant CC as Claude Code + participant X as xNet workspace + participant P as Sandboxed plugin + U->>CC: xnet connect claude-code + CC->>X: read/query/edit via CLI lanes (0.11x tokens) + U->>CC: "build me a tool for this workflow" + CC->>X: plugin_scaffold / plugin_build / plugin_preview (0331 tools, wired by 0455) + X->>P: iframe host mounts, hot reloader watches + P-->>U: new capability inside the workspace + X-->>U: signed change log — every step visible + Note over U,P: the loop = the demo = the pitch.
Cloud, SDK, commons all inherit demand from it. +``` + +## Risks And Open Questions + +- **The n=1 gate can self-deceive.** With telemetry charter-banned, "I used + it all week" is unauditable. Mitigation: the dogfood ledger lives in the + workspace itself (its change log is signed and dated), and validation + requires outside humans completing the flow. +- **Incumbent speed.** Notion's agent hub or Buzz could ship an + in-workspace plugin loop. The response is written into the choice: depth + on the two things structurally hard for them (local-first ownership; + signed total visibility), not breadth. Revisit at review if either ships + the loop. +- **The agent door might onboard users into a thin room.** Someone arrives + via `xnet connect` with no existing xNet content — what does their agent + act *on*? The demo-seed path (`demo-seed.ts`) and vault checkout of + existing files partially answer this; the manual-onboarding step (5) is + where the real answer gets discovered. Open question to resolve during + the quarter. +- **Benchmark fragility.** The 0.11x claim rides 15 tasks; once public it + invites adversarial replication. Publishing methodology (step 4) converts + the risk into credibility. +- **Focus decay.** The repo's measured failure mode is accumulation + (0421). This doc itself must not become doc #525-that-nothing-closes: it + has a 90-day review, one decider, and a checklist whose items are + pointers into four existing checklists rather than new scope. + +## Implementation Checklist + +**Status:** ░░░░░░░░░░ 0/9 items + +- [ ] **0335 #1**: Electron uses `secure-seed.ts`; `makeTestKey` removed + from the production boot path (`apps/electron/src/renderer/main.tsx:887`) +- [ ] **0455 items 1–7**: effect scopes + service registry landed; + `extraTools` resolved (not hand-threaded) in all three hosts +- [ ] **0455 items 8–9 / 0447**: `plugin_*` tools + `WorkspaceAgentModule` + tools reach live sessions; workspace-plugin host + hot reloader + mounted behind a dev surface +- [ ] **Loop demo recorded**: one take, unedited — agent scaffolds → builds + → previews → installs a plugin in the founder's real workspace; + change-log view shown +- [ ] **Dogfood ledger** created *as an xNet page*; weekly entries; every + fallback-to-other-tools logged with a cause +- [ ] **`packages/cli/README.md` rewritten** around + `connect`/`checkout`/`commit`/`mcp` (npm-facing) +- [ ] **Hero repositioned**: one primary CTA (agent quickstart); + App/SDK demoted to secondary (`site/src/components/sections/Hero.astro`, + `GetStarted.astro`) +- [ ] **Launch post** published (first launch-register post in the blog) + + benchmark methodology committed and reproducible +- [ ] **3+ manual onboardings** of outside Claude Code/Codex users + completed, snags filed as issues/explorations + +## Validation Checklist + +- [ ] The recorded loop demo exists and required no code outside `main` +- [ ] Roadmap gate: ≥4 consecutive ledger weeks of the founder's real work + in xNet, with fallback count trending down +- [ ] At least one outside person completes `xnet connect` → agent reads + and writes their workspace, without founder intervention mid-flow +- [ ] `npm view @xnetjs/cli` README mentions `connect`; site hero has + exactly one primary door +- [ ] At review (2026-11-19): re-score the four lanes; if the agent door + produced zero outside pull after honest execution, re-open Options + A–C with this doc's evidence tables as the baseline + +## References + +- Repo: `docs/ROADMAP.md` (the three pillars, dogfood gate), + `docs/CHARTER.md`, + [0455](./0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md) (the + loop's mechanism-level plan), + [0447](./0447_[_]_LEARNING_FROM_MACRO_WIRE_THE_LOOP_BEFORE_WIDENING_THE_SUITE.md), + [0452](./0452_[_]_HOW_FAR_TO_PLUGINIZE_THE_KERNEL_THE_SHELL_AND_THE_LIFT_OUT_LADDER.md), + [0331](./0331_[x]_DEVELOPING_XNET_FROM_INSIDE_XNET_SPEC_TO_PLUGIN_LOOP.md), + [0416](./0416_[-]_AGENT_HARNESS_OR_AGENT_SUBSTRATE.md) (ADR-29; + Buzz prediction), + [0421](./0421_[-]_FAST_WHAT_COLLISONS_LIST_MEASURES_AND_WHAT_XNET_LACKS.md) + (accumulation metrics), + [0335](./0335_[_]_RELEASE_READINESS_AUDIT_WHAT_STANDS_BETWEEN_XNET_AND_A_WELL_RECEIVED_LAUNCH.md) + (key blocker), [0391](./0391_[x]_XNET_AS_THE_DAILY_DRIVER_AI_INTERFACE.md), + [0393](./0393_[_]_XNET_FROM_INSIDE_THE_CODING_AGENT.md) (`xnet connect`) +- External (as-of dates in text): Supabase origin story + (stacksync.com) + Series F (CNBC, 2026-06); n8n/SAP (Bloomberg, 2026-05); + Obsidian stats (obsidianstats.com); AppFlowy/AFFiNE/Anytype trackers + (third-party, soft numbers, flagged in research); Logseq stall coverage; + Notion agent hub (TechCrunch, 2026-05-13); Anthropic Cowork (Forbes, + 2026-02-25); Block's Buzz (opensourceforu.com, digitalapplied.com, + 2026-07); DeepSeek Harness (The Register, 2026-08-14); ChatGPT plugins + shutdown retrospectives; Paul Graham, *Do Things That Don't Scale*; + Nadia Eghbal, *Working in Public* From 8c730e62469d76d4be6506fe05b64ce83853600e Mon Sep 17 00:00:00 2001 From: xNet Test Date: Fri, 21 Aug 2026 18:15:44 -0700 Subject: [PATCH 03/16] docs(exploration): explore agent-first site re-architecture Co-Authored-By: Claude Fable 5 Signed-off-by: xNet Test --- ...457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md | 525 ++++++++++++++++++ 1 file changed, 525 insertions(+) create mode 100644 docs/explorations/0457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md diff --git a/docs/explorations/0457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md b/docs/explorations/0457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md new file mode 100644 index 000000000..28e740343 --- /dev/null +++ b/docs/explorations/0457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md @@ -0,0 +1,525 @@ +--- +title: Agent-first site re-architecture — every page converts one door +status: draft +last_updated: 2026-08-21 +review: 2026-11-19 # same date as 0456, deliberately — this is 0456 step 4 specified; if the entry-vector bet is re-scored, this doc re-scores with it +decider: Chris Smothers +door: two-way # copy, information architecture, and one new route; no wire format, no public API, no pricing change. Every demoted page keeps its URL. +tags: [site, marketing, agents, positioning, conversion, docs-ia] +--- + +# Agent-first site re-architecture — every page converts one door + +> [!TIP] +> **TL;DR** — Rebuild the site's conversion spine around one action: +> `xnet connect claude-code`. Hero becomes a copyable command +> with per-agent tabs (the Bun / Claude Code pattern), a new `/agents` page +> becomes the conversion hub (per-client installs, safety model, the 0.11x +> benchmark), "Connect your agent" becomes the header button, the +> coding-agents guide moves from *item 9 of a collapsed accordion* into +> **Start Here**, and llms.txt finally mentions `xnet connect` — today the +> string appears in exactly **one** file on the whole site +> (`coding-agents.mdx`) and in none of: nav, footer, hero, GetStarted, +> README, llms.txt. Nothing is deleted: App, SDK, Cloud, Why all keep their +> URLs and their depth pages, demoted one rank. Ship in two phases — +> **Phase A now** (repositioning what already works: connect, checkout, +> MCP, the benchmark), **Phase B when 0456's loop demo exists** (the +> agent-builds-plugins section). Everything a human reads gets a twin the +> *agent* reads, because for this product the agent is present at the +> moment of conversion. + +## Problem Statement + +[0456](./0456_[_]_ENTRY_VECTOR_THE_AGENT_DOOR_FIRST.md) chose the entry +vector: the agent door, `xnet connect claude-code|codex`. Its step 4 said +"say one sentence, everywhere" and reserved one checklist line for the +hero. This exploration is that step, fully specified: what does the landing +page, the site IA, the docs, the README, and the agent-readable layer look +like when **everything converts toward the agent connection** — while the +app, the React SDK, xNet Cloud, and the movement pages all remain, one rank +down? + +The gap is stark. The site survey found: + +- `xnet connect` appears in **one** source file + (`site/src/content/docs/docs/guides/coding-agents.mdx`) — nowhere in + `Nav.astro`, `Footer.astro`, `Hero.astro`, `GetStarted.astro`, or the + root `README.md`. +- `public/llms.txt` — the file coding agents actually fetch — **omits the + coding-agents guide entirely** while listing 40+ other docs. +- The landing's agent section (`BuiltForAgents.astro`) is 4th of 7, + ~6 viewports down, and demos `xnet checkout` / `xnet query` — not the + one command we want typed. +- The docs landing (`docs/index.mdx`) opens "xNet is a local-first React + framework" and never links either agent guide. +- There is no `/agents` route, no agent data file, no OG images, no + sitemap, no site-wide robots.txt. + +The site is not wrong — it is even-handed. Even-handed is the problem +(0456, Option E). + +## Executive Summary + +| Question | Answer | +| --- | --- | +| The one sentence | **"Give your coding agent a workspace you own."** Sub-sentence: docs, databases, and canvases your agent can read, query, and build in — every change signed, synced, and yours. | +| The one action | A copy-button command in the hero, per-agent tabs: Claude Code · Codex · Cursor · VS Code · anything (MCP). Primary everywhere: header button, hero, GetStarted path 1, README section 1, docs Start Here. | +| What happens to the app/SDK/cloud? | Kept, demoted one rank. App = "the workspace behind the agent" (section + `/app` untouched); SDK = the developer depth pages (`/react`, `/build-with`); Cloud = a whisper ("Free to start · Pricing") per the Ollama pattern. No URL dies. | +| What's new? | One route: **`/agents`** (conversion hub + `src/data/agents.ts`); an agent-readable layer (llms.txt fix, `/agents.md`, install snippets/deeplinks); OG meta while we're in `Base.astro`. | +| What's honest to ship *today*? | Phase A: connect, three lanes, read-only default, agent passports, the 0.11x benchmark (with methodology published — a 0456 item). All shipped and true now. | +| What waits? | Phase B: the "agent builds tools inside your workspace" section and demo — gated on 0456's loop wiring (0455 checklist). The site must not market the loop before it's recordable. | +| Biggest execution risk | Build gates: `build-llms-full.ts` fails if a docs page isn't in `sidebar.mjs`; `validate-dist.ts` asserts route outputs (read before renaming anything); `pricing-claims.test.ts` regex-reads `pricing.ts` as text — don't reformat it. | + +--- + +## Current State In The Repository + +### The conversion spine today + +```text +Nav: [xNet] App Developers Open | Why Build Demos Blog [Docs] [Try the App] + │ +Hero: "Your data. Your devices. Your rules." ▼ + [Try the app — free, no account] [Read the docs] /app + doors: App(emerald) · SDK(indigo) · Protocol(purple) + "The app is built on the SDK. The SDK implements the protocol. + Start anywhere." ← three equal doors + +Sections: Hero → TheApp → ForDevelopers → BuiltForAgents → NoBlackBoxes + → HumaneByDesign → GetStarted (App / SDK / Movement — no agent path) +``` + +Everything routes to `/app?demo=1` or `/docs/quickstart/` (SDK). The agent +story is mid-scroll (`BuiltForAgents.astro`, showing `checkout`/`query`), +and its docs are behind a collapsed accordion: `coding-agents` is item 9 of +15 in **Guides**, three levels below Start Here. + +### Assets the restructure can reuse (no new machinery needed) + +| Asset | Path | Why it matters | +| --- | --- | --- | +| Terminal chrome component | `site/src/components/ui/CodeBlock.astro` — macOS traffic lights, `filename="terminal"`, hover copy button | The hero command block already exists as a component | +| Tab strip component | `site/src/components/ui/CodeTabs.astro` — dependency-free, group-synced, localStorage-persisted, no-JS fallback | Per-agent tabs (Claude Code/Codex/Cursor/VS Code) for free | +| The content itself | `docs/guides/coding-agents.mdx` (the `xnet connect` guide), `docs/guides/agent-interfaces.mdx` (three lanes + 0.11x benchmark), `docs/ai/understanding-xnet.mdx` | The `/agents` page is 80% assembly of existing prose | +| Changelog receipts | 12+ agent fragments in `site/src/data/changelog/` (e.g. `2026-07-24-use-xnet-from-claude-code-and-codex.json`, `2026-08-01-verify-what-your-agent-did.json`) | Social-proof strip: real dated receipts, no invented testimonials | +| llms-full pipeline | `site/scripts/build-llms-full.ts` + `site/src/sidebar.mjs` (single source of truth for docs order **and** llms-full order) | Reordering the sidebar reorders the agent-readable corpus too | +| Data-file pattern | `site/src/data/*.ts` driving every page | `agents.ts` slots in beside `pricing.ts`/`compare.ts` | + +### Constraints that will bite (from the survey) + +> [!WARNING] +> Four build gates constrain this work. (1) `build-llms-full.ts` **fails +> the build** if a docs content file is in neither `sidebar.mjs` nor its +> exclusion list — every new docs page needs a sidebar entry in the same +> PR. (2) `scripts/validate-dist.ts` was added after a half-built deploy +> wiped the homepage for ~30 min (2026-07-18); **read it before renaming +> or deleting any route** — it asserts route outputs exist. (3) +> `apps/cloud/src/pricing-claims.test.ts` reads `site/src/data/pricing.ts` +> as **text with a whitespace-sensitive regex** — do not reformat that file +> while touching Cloud copy. (4) `validate-metrics.ts` fails on overstated +> or >25%-stale figures — any new stat on the hero must come from +> `siteMetrics.ts`'s conservative-floor pattern. + +Also: `site/` installs `--ignore-workspace` and cannot import `@xnetjs/*` +(root `AGENTS.md`); the established workarounds are repo-root JSON imports +(`plugins.ts` → `registry/registry.json`) and committed snapshots — an +`agents.ts` data file follows the same pattern. Deploys ride +`deploy-site.yml` (site + `/app` + `/play` assembled onto `gh-pages`; the +"~9 min to live" figure from memory is not written anywhere in-repo — +verify empirically before quoting it in launch-day plans). + +--- + +## External Research + +### The command-first hero is a solved pattern + +From the 2026 survey of dev-tool landers: + +- **Bun**: headline + copyable `curl … | bash` with OS tabs + versioned + install button + a *replayable* benchmark race above the fold. Its trophy + logos are agent products (Claude Code, Cursor, Midjourney, Railway). +- **Claude Code itself**: name + "Work with Claude directly in your + codebase…" + download button **and** install one-liner. MCP integrations + are a late section — the mirror image of xNet, which *is* the + integration and should lead with the connect command. +- **Homebrew**: the page essentially is the command. **Ollama**: one + action; cloud reduced to "Free to start. See pricing." +- **Aider**: proof by quantified usage ("88% of new code in the latest + release written by Aider itself") — the dogfood-metric pattern 0456's + ledger can eventually feed. +- **Evil Martians' 100-lander study**: exactly **two** CTAs (one dominant, + one subordinate); specific verb copy over "Get started"; for + libraries/infra a code snippet *is* the right hero visual; pricing on + its own page. + +### The "add to your agent" affordance stack (mid-2026 table stakes) + +- Per-client tab strip with copy buttons: `claude mcp add …` (Claude Code + convention — the snippet is the affordance), `~/.codex/config.toml` TOML + block (Codex), **Cursor deeplink** (`cursor://anysphere.cursor-deeplink/mcp/install?name=…&config=`, + official button assets; pair with a JSON fallback — deeplinks are + reported flaky), **VS Code badge** (`vscode:mcp/install?`). +- Directory distribution (Smithery ~16.8k MCPs listed; mcp.so ~20k + secondhand) is marketing reach, not a substitute for the page. +- Docs sites are now expected to *be* agent-consumable: Mintlify ships + `/llms.txt`, `/llms-full.txt`, per-page "copy as Markdown"; GitBook + auto-exposes an MCP endpoint per docs site. + +### Marketing to the agent, not just the human + +Netlify named the category — "Agent Experience (AX)" — and in April 2026 +launched **netlify.ai, a site built for agents rather than humans** +(onboarding and build context for the agent itself). The nuance from the +llms.txt adoption data: no major AI vendor commits to llms.txt for +*search/training*, but **coding agents do fetch `/llms.txt` when pointed +at a docs site** — which is precisely xNet's conversion moment: an agent +is *running* `xnet connect` while its human watches. xNet's llms.txt +currently forgets to mention the connect flow at all. + +One more external fact that shapes copy: **Continue.dev's lander is now an +acquisition notice and Goose's is a redirect stub.** Harness brands churn. +The site should anchor on "your coding agent" generically, with named +clients as tabs — never as the headline. + +--- + +## Key Findings + +1. **This is a repositioning, not a rebuild.** The components (terminal + chrome, tab strip), the content (two mature agent guides), and the + receipts (12 dated changelog fragments) all exist. What's missing is + rank: the agent story is mid-scroll on the homepage, item 9 of a + collapsed accordion in docs, and absent from nav, README, and llms.txt. + +2. **The conversion moment is a two-reader moment.** Uniquely for this + product, at the instant of conversion there are two readers: the human + deciding, and the agent about to execute `xnet connect` (and likely + fetching `/llms.txt` mid-run). Every conversion surface therefore needs + a human face and an agent twin. No competitor in the workspace lane + does this; Netlify proved the pattern in the deploy lane. + +3. **Honesty gates the section order.** The three lanes, read-only + default, passports, signed log, and the benchmark are shipped and true + — Phase A can say them loudly today. The loop ("your agent builds tools + *inside* the workspace") is 0455/0456 wiring away; the site must not + promise it before the demo records. The Charter's own rule (every + promise ships with a receipt or is labeled not-yet) applies to + marketing exactly as to docs. + +4. **The benchmark is the single most quotable asset and the most + fragile.** "~9x cheaper than MCP tools" already appears on the landing + page; the hero will amplify it. 0456 already requires the methodology + to be published and reproducible — that item becomes a *prerequisite* + of Phase A launch, not a follow-up. + +5. **Demotion must be visible-but-cheap.** The lesson from Ollama ("Free + to start. See pricing") and Evil Martians (pricing on its own page): + the app and cloud don't vanish — they compress to one honest line each + with a route. That satisfies "keep all the other features" without + re-splitting the funnel. + +```mermaid +flowchart TD + subgraph HUMAN["Human reader"] + H1[Hero: one sentence + command] --> H2["/agents — what your agent gets,
safety model, benchmark"] + H2 --> H3[Copies command / clicks deeplink] + end + subgraph AGENT["Agent reader"] + A1["/llms.txt — names connect flow"] --> A2["/agents.md — what am I
connecting to, which tools"] + A2 --> A3["SKILL.md installed by connect
(~500 tokens, exists today)"] + end + H3 --> C[xnet connect claude-code] + A3 --> C + C --> W[Agent reads/writes the workspace
three lanes, signed log] + W -->|"wants sync"| CLOUD["/cloud (demoted, intact)"] + W -->|"wants the UI"| APP["/app (demoted, intact)"] + W -->|"Phase B"| LOOP[Agent builds plugins inside
— gated on 0456 loop demo] +``` + +--- + +## Options And Tradeoffs + +### Option A — Copy-only touch-up + +Rewrite `Hero.astro` copy and promote `BuiltForAgents` to section 2; change +nothing else. + +- ✅ One PR, zero risk to build gates. +- ❌ Leaves the funnel broken where it actually converts: no `/agents` + page to send traffic to, docs still SDK-first, llms.txt still silent on + connect, README untouched. The header button still says "Try the App." + Half a repositioning reads as indecision — the current site's disease. + +### Option B — Full-stack repositioning in two phases ⭐ + +Phase A (now): hero + nav + `/agents` route + GetStarted + docs IA + +llms.txt/agents.md + README, all around what ships today. Phase B (gated +on 0456's loop demo): the plugin-loop section, the recorded demo, and the +dogfood-metric proof strip. + +- ✅ Converts the whole spine while every claim stays true; the two-phase + gate keeps marketing behind reality; touches no revenue mechanics. +- ✅ Each Phase A item is small and independently shippable (see + checklist) — no big-bang redesign, `--delete` rsync deploys stay safe. +- ❌ ~8–10 PRs across site, docs, README; sidebar/llms-full/validate-dist + gates need care; OG/meta work tempts scope creep (kept optional). + +### Option C — Separate agent microsite (agents.xnet.fyi or netlify.ai-style twin) + +- ✅ Maximum focus; the main site stays even-handed. +- ❌ Splits authority and maintenance for a solo founder; the survey shows + the main site's traffic surfaces (README, llms.txt, docs) are exactly + where the fix is needed; a microsite duplicates the Starlight/llms + pipeline. The agent-twin *pages* (Option B) capture the netlify.ai idea + without a second property. + +### Option D — Docs-as-landing (Tailwind posture) + +Make `/docs` the homepage; kill the marketing site's hero. + +- ❌ Throws away the `/why`/Charter/blog narrative layer that is xNet's + actual differentiation vs Buzz/Notion, and the 25-essay corpus that + earns trust. Rejected without much agony. + +> [!NOTE] +> No revenue lane changes: Cloud pricing, plans, and CTAs are untouched +> except in rank. Charter §6 tests not triggered. + +--- + +## Recommendation + +**Option B.** The spec, surface by surface: + +### 1. Hero (`site/src/components/sections/Hero.astro`) + +```text +┌────────────────────────────────────────────────────────────────────┐ +│ [Alpha — shipping, and still moving fast] │ +│ │ +│ Give your coding agent a workspace you own. │ +│ │ +│ Documents, databases, and canvases your agent can read, query, │ +│ and build in — local-first, synced, every change signed. │ +│ │ +│ ┌ Claude Code ┊ Codex ┊ Cursor ┊ VS Code ┊ Any agent ─────────┐ │ +│ │ ● ● ● terminal [copy] │ │ +│ │ $ npx @xnetjs/cli connect claude-code │ │ +│ │ ✓ skill installed · mcp registered · read-only by default │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +│ │ +│ [What your agent gets → /agents] [Try the app] (2nd CTA) │ +│ │ +│ ~9x cheaper than MCP toolsets* · read-only until you say so · │ +│ works offline · MIT *methodology → /agents │ +└────────────────────────────────────────────────────────────────────┘ +``` + +- Tabs via existing `CodeTabs.astro`; terminal via existing + `CodeBlock.astro` (`filename="terminal"`). Claude Code/Codex tabs show + `xnet connect …`; Cursor/VS Code tabs show the MCP deeplink button + + copyable JSON fallback; "Any agent" shows `xnet mcp serve`. +- Exactly two CTAs (Evil Martians): primary → `/agents`, secondary → + `/app?demo=1`. The three equal doors **go away**; the closing line + becomes "There's a full workspace app behind this — and an SDK under + both. [App] · [SDK] · [Protocol]" as small links. +- Verify the exact zero-install one-liner before shipping (`npx + @xnetjs/cli …` vs `npm i -g` — whichever `packages/cli` actually + supports; the checklist carries this). +- Static command block first; a typed-replay animation is a Phase B + nicety, not a blocker (Deno converts with no animation at all). + +### 2. New route: `/agents` (+ `site/src/data/agents.ts`) + +The conversion hub, assembled from existing content: + +1. Per-client install (the hero tabs, expanded — including + `claude mcp add` and Codex TOML for people who prefer raw MCP). +2. **What your agent gets**: the three lanes from `agent-interfaces.mdx` + (CLI verbs → vault checkout → MCP fallback), with the token benchmark + and a link to the published methodology. +3. **The safety model**: read-only by default, `--writes` opt-in, agent + passports, every change signed into the log — "verify what your agent + did" (reuse the changelog fragment's framing). +4. **Receipts strip**: the dated agent changelog fragments as cards (real + receipts instead of invented testimonials). +5. One-line demotions: "Prefer a UI? [Try the app]. Building your own? + [React SDK]. Want managed sync? [Cloud — free to start]." +6. Phase B slot: the recorded loop demo replaces a "what's next" teaser. + +`agents.ts` holds the per-client commands/deeplinks/labels so the hero +tabs, `/agents`, README snippets, and docs quickstart all render from one +source (same pattern as `pricing.ts`). + +### 3. Nav + footer (`Nav.astro`, `Footer.astro`) + +- Header: add **Agents** as the first page link; the filled conversion + button becomes **"Connect your agent" → `/agents`**; "Try the App" + moves to a plain link. Everything else stays. +- Footer: new first column **Agents** (Connect guide, Agent interfaces, + /agents, llms.txt, MCP/registry listings), then Product/Cloud/Develop/ + Resources/Community as today. + +### 4. Docs IA (`site/src/sidebar.mjs`, `docs/index.mdx`) + +- **Start Here** becomes: Introduction → **Connect your agent** + (`coding-agents.mdx`, retitled) → Quickstart (SDK) → Core Concepts. + `agent-interfaces` moves up alongside it or into a new "Agents" group + right under Start Here — either way, out of the collapsed accordion. +- `docs/index.mdx` opens with two cards — "Connect your coding agent" and + "Build with React" — replacing the SDK-only lede. +- Sidebar reorder automatically reorders `llms-full.txt` (same source of + truth); regenerate and commit in the same PR (`pnpm check:llms-full`). + +### 5. The agent-readable layer + +- `public/llms.txt`: add the connect flow at the **top** ("If you are a + coding agent: your human can connect you with `xnet connect `; + after connect you get these tools/lanes…"), plus the missing + coding-agents entry. +- New `public/agents.md` (the netlify.ai move, one page not a microsite): + what xNet is *to an agent*, the three lanes, tool list, safety + contract, where the SKILL.md comes from. Linked from llms.txt and + `/agents`. +- Optional same-PR cheap wins while in `Base.astro`: `og:title`/ + `og:description`/`twitter:card` (site has **zero** OG meta today), + `@astrojs/sitemap`, site-wide `robots.txt`. + +### 6. README (root) + +Mirror the site's new order: after the one-liner and screenshot, **Try it** +gains "Connect your coding agent" as the *first* bullet (`npx @xnetjs/cli +connect claude-code`), before demo/download/hub; a short "Your agent, +your workspace" section (three lanes + benchmark + safety line) lands +above "Build with it". Zero agent mentions today → the second landing +surface gets the same spine. + +### 7. Explicitly unchanged + +`/why`, `/commitments`, `/blog`, `/compare`, `/open`, `/status`, +`/roadmap`, all legal pages, `/cloud` + pricing (rank only), `/plugins`, +`/download`, `/mobile`, `/demos`, `/react`, `/build-with`, `/devtool` — +URLs, content, and validators untouched. + +### Phase gate + +> [!IMPORTANT] +> **Phase A ships now** — every claim above is true of today's shipped +> `@xnetjs/cli@0.4.0`. **Phase B** (the loop section: "ask your agent for +> a tool; it builds a sandboxed plugin inside your workspace; watch every +> change in the signed log" + recorded demo + Aider-style dogfood metric) +> **is gated on 0456's checklist item "loop demo recorded"** — the site +> never gets ahead of the repo. The 0.11x benchmark methodology +> publication (a 0456 item) is a **Phase A prerequisite**, because the +> hero quotes it. + +## Risks And Open Questions + +- **The command must work flawlessly for strangers.** The hero promotes a + path so far run mostly by its author. 0456's manual-onboarding item is + the mitigation; sequence at least one outside run before the hero + flips. Also confirm `npx @xnetjs/cli connect` works without global + install (and without a pre-existing workspace — the "thin room" question + from 0456: what does a fresh agent connect *to*? The `/agents` page + should answer with the demo-seed or `xnet vault init` story). +- **Cursor/VS Code deeplinks are flaky** (documented forum failures) — + always render the copyable JSON beside the button; treat the deeplink as + progressive enhancement. +- **Benchmark exposure.** Quoting 0.11x in the hero invites replication + attempts; methodology must be in-repo and reproducible first + (prerequisite above). +- **validate-dist and route assembly.** Read `scripts/validate-dist.ts` + before the nav/route PR; add `/agents` to whatever it asserts. The + gh-pages rsync `--delete` means a bad build can blank pages — the + validator exists because it already happened once; keep it updated rather + than bypassed. +- **Alpha honesty vs conversion.** The alpha badge stays in the hero. The + 0335 key blocker (0456 item 1) must land before any launch push drives + desktop downloads. +- **Open question — the name of the door.** "Agents" vs "AI" vs "Connect" + in nav copy; "Agents" is assumed here (matches `/agents`, + survives harness churn), but test on the manual onboardings. +- **Open question — Plausible goals.** Cookieless Plausible is already + gated in; whether to define custom events (copy-click, tab-select) or + keep zero-measurement is a Charter-flavored decision left to the + decider. + +## Implementation Checklist + +**Status:** ░░░░░░░░░░ 0/12 items (Phase A: 1–10; Phase B: 11–12) + +- [ ] Verify + document the canonical zero-install command (`npx + @xnetjs/cli connect claude-code` or equivalent) against + `packages/cli` as published; fix `packages/cli` if npx flow has + gaps +- [ ] Publish the 0.11x benchmark methodology in-repo (0456 item, now a + Phase A prerequisite) and link target for the hero footnote +- [ ] `site/src/data/agents.ts` — per-client commands, deeplinks, labels + (single source for hero tabs, `/agents`, README, docs) +- [ ] `Hero.astro` rewrite: new headline/sub, `CodeTabs` + `CodeBlock` + command block, two CTAs, doors → small links +- [ ] New `site/src/pages/agents.astro` per the section spec; update + `scripts/validate-dist.ts` expectations if route-asserting +- [ ] `Nav.astro` (+Agents link; button → "Connect your agent") and + `Footer.astro` (+Agents column) +- [ ] `GetStarted.astro`: path 1 becomes "Connect your agent" (command + block), App and SDK follow +- [ ] Docs IA: `sidebar.mjs` — coding-agents into Start Here (retitled + "Connect your agent"), agent-interfaces promoted; `docs/index.mdx` + two-card lede; regenerate `llms-full.txt` (`pnpm check:llms-full`) +- [ ] Agent-readable layer: `public/llms.txt` top section + coding-agents + entry; new `public/agents.md`; (optional, same PR: OG meta in + `Base.astro`, `@astrojs/sitemap`, `robots.txt`) +- [ ] Root `README.md`: connect-first Try-it bullet + "Your agent, your + workspace" section above Build-with +- [ ] **Phase B**: loop demo section on `/` and `/agents` once 0456's + "loop demo recorded" item is checked; typed-replay animation of the + connect+session terminal +- [ ] **Phase B**: dogfood proof strip (ledger-derived metric, Aider + pattern) once the 0456 ledger has ≥4 weeks of data + +## Validation Checklist + +- [ ] `cd site && pnpm build` green (all validators incl. llms-full check + and validate-dist) with the new route and reordered sidebar +- [ ] A fresh machine + `npx` run of the hero command succeeds verbatim + as printed, against the published npm package (not the repo) +- [ ] An agent given only `https://xnet.fyi` (via llms.txt/agents.md) can + explain what `xnet connect` will do and which tools it gets — + tested by actually asking Claude Code with a clean context +- [ ] Cursor deeplink and VS Code badge each install the MCP server on a + clean profile; JSON fallback verified when the deeplink fails +- [ ] Every demoted page still reachable within two clicks of `/` + (nav or footer); no URL removed (`check:exploration-links`-style + manual sweep of site nav) +- [ ] At least one 0456 manual onboarding completed **through the new + site** without founder intervention — the site was the only guide +- [ ] Phase B additions appear only after the referenced 0456 items are + verifiably checked + +## References + +- Repo: [0456](./0456_[_]_ENTRY_VECTOR_THE_AGENT_DOOR_FIRST.md) (the + strategy this specifies), + [0455](./0455_[_]_CORDIS_LESSONS_FOR_XNET_PLUGIN_COMPOSITION.md) (loop + wiring behind Phase B), + [0384](./0384_[x]_TIGHTENING_THE_LANDING_PAGE_FROM_28_VIEWPORTS_TO_A_FOCUSED_FUNNEL.md) (the + teaser→route rule this doc obeys: teasers link depth pages, never + re-argue them), `site/src/components/sections/Hero.astro`, + `site/src/sidebar.mjs`, `site/scripts/build-llms-full.ts`, + `scripts/validate-dist.ts` (via `site/package.json` build), + `apps/cloud/src/pricing-claims.test.ts`, + `site/src/content/docs/docs/guides/coding-agents.mdx`, + `public/llms.txt` +- External: Bun (bun.sh) hero pattern; Claude Code product page + (claude.com/product/claude-code); Ollama (ollama.com) one-action page; + Aider (aider.chat) dogfood metric; Evil Martians "We studied 100 dev + tool landing pages" (2025) + LaunchKit; Cursor MCP install-links docs + (deeplink + button assets); VS Code "Agent mode meets MCP" (May 2025, + `vscode:mcp/install` badges); modelcontextprotocol/mcpb bundles; + Mintlify contextual menu / llms.txt tooling; GitBook docs-MCP + endpoints; Netlify Agent Experience + netlify.ai (Apr 2026); Smithery + (smithery.ai); llms.txt adoption surveys (secondary sources, directional + only); Continue.dev acquisition page + Goose redirect (harness churn) From 14cc9e0d8955ac5be96163e527878d66eb5767d6 Mon Sep 17 00:00:00 2001 From: xNet Test Date: Fri, 21 Aug 2026 18:29:36 -0700 Subject: [PATCH 04/16] feat(cli): register an npx launcher when xnet is not on PATH The zero-install on-ramp (npx @xnetjs/cli connect claude-code) used to register an MCP server entry with command 'xnet', which dies once the npx cache is gone. connect now resolves the launcher from PATH and falls back to 'npx -y @xnetjs/cli'; the connect guide documents the npx one-liner as the canonical zero-install command. Signed-off-by: xNet Test --- .changeset/connect-npx-launcher.md | 5 ++ ...457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md | 66 +++++++++---------- .../cli/src/__tests__/connect-command.test.ts | 31 +++++++-- packages/cli/src/commands/connect.ts | 44 +++++++++++-- .../docs/docs/guides/coding-agents.mdx | 14 +++- 5 files changed, 113 insertions(+), 47 deletions(-) create mode 100644 .changeset/connect-npx-launcher.md diff --git a/.changeset/connect-npx-launcher.md b/.changeset/connect-npx-launcher.md new file mode 100644 index 000000000..5d4f56863 --- /dev/null +++ b/.changeset/connect-npx-launcher.md @@ -0,0 +1,5 @@ +--- +'@xnetjs/cli': patch +--- + +`xnet connect` now registers an `npx -y @xnetjs/cli` MCP server launcher when the `xnet` bin is not on PATH, so the zero-install `npx @xnetjs/cli connect claude-code` on-ramp produces a registration that still works after the npx cache is gone. diff --git a/docs/explorations/0457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md b/docs/explorations/0457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md index 28e740343..cc9efdd7f 100644 --- a/docs/explorations/0457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md +++ b/docs/explorations/0457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md @@ -16,7 +16,7 @@ tags: [site, marketing, agents, positioning, conversion, docs-ia] > with per-agent tabs (the Bun / Claude Code pattern), a new `/agents` page > becomes the conversion hub (per-client installs, safety model, the 0.11x > benchmark), "Connect your agent" becomes the header button, the -> coding-agents guide moves from *item 9 of a collapsed accordion* into +> coding-agents guide moves from _item 9 of a collapsed accordion_ into > **Start Here**, and llms.txt finally mentions `xnet connect` — today the > string appears in exactly **one** file on the whole site > (`coding-agents.mdx`) and in none of: nav, footer, hero, GetStarted, @@ -25,7 +25,7 @@ tags: [site, marketing, agents, positioning, conversion, docs-ia] > **Phase A now** (repositioning what already works: connect, checkout, > MCP, the benchmark), **Phase B when 0456's loop demo exists** (the > agent-builds-plugins section). Everything a human reads gets a twin the -> *agent* reads, because for this product the agent is present at the +> _agent_ reads, because for this product the agent is present at the > moment of conversion. ## Problem Statement @@ -60,15 +60,15 @@ The site is not wrong — it is even-handed. Even-handed is the problem ## Executive Summary -| Question | Answer | -| --- | --- | -| The one sentence | **"Give your coding agent a workspace you own."** Sub-sentence: docs, databases, and canvases your agent can read, query, and build in — every change signed, synced, and yours. | -| The one action | A copy-button command in the hero, per-agent tabs: Claude Code · Codex · Cursor · VS Code · anything (MCP). Primary everywhere: header button, hero, GetStarted path 1, README section 1, docs Start Here. | +| Question | Answer | +| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| The one sentence | **"Give your coding agent a workspace you own."** Sub-sentence: docs, databases, and canvases your agent can read, query, and build in — every change signed, synced, and yours. | +| The one action | A copy-button command in the hero, per-agent tabs: Claude Code · Codex · Cursor · VS Code · anything (MCP). Primary everywhere: header button, hero, GetStarted path 1, README section 1, docs Start Here. | | What happens to the app/SDK/cloud? | Kept, demoted one rank. App = "the workspace behind the agent" (section + `/app` untouched); SDK = the developer depth pages (`/react`, `/build-with`); Cloud = a whisper ("Free to start · Pricing") per the Ollama pattern. No URL dies. | -| What's new? | One route: **`/agents`** (conversion hub + `src/data/agents.ts`); an agent-readable layer (llms.txt fix, `/agents.md`, install snippets/deeplinks); OG meta while we're in `Base.astro`. | -| What's honest to ship *today*? | Phase A: connect, three lanes, read-only default, agent passports, the 0.11x benchmark (with methodology published — a 0456 item). All shipped and true now. | -| What waits? | Phase B: the "agent builds tools inside your workspace" section and demo — gated on 0456's loop wiring (0455 checklist). The site must not market the loop before it's recordable. | -| Biggest execution risk | Build gates: `build-llms-full.ts` fails if a docs page isn't in `sidebar.mjs`; `validate-dist.ts` asserts route outputs (read before renaming anything); `pricing-claims.test.ts` regex-reads `pricing.ts` as text — don't reformat it. | +| What's new? | One route: **`/agents`** (conversion hub + `src/data/agents.ts`); an agent-readable layer (llms.txt fix, `/agents.md`, install snippets/deeplinks); OG meta while we're in `Base.astro`. | +| What's honest to ship _today_? | Phase A: connect, three lanes, read-only default, agent passports, the 0.11x benchmark (with methodology published — a 0456 item). All shipped and true now. | +| What waits? | Phase B: the "agent builds tools inside your workspace" section and demo — gated on 0456's loop wiring (0455 checklist). The site must not market the loop before it's recordable. | +| Biggest execution risk | Build gates: `build-llms-full.ts` fails if a docs page isn't in `sidebar.mjs`; `validate-dist.ts` asserts route outputs (read before renaming anything); `pricing-claims.test.ts` regex-reads `pricing.ts` as text — don't reformat it. | --- @@ -96,14 +96,14 @@ and its docs are behind a collapsed accordion: `coding-agents` is item 9 of ### Assets the restructure can reuse (no new machinery needed) -| Asset | Path | Why it matters | -| --- | --- | --- | -| Terminal chrome component | `site/src/components/ui/CodeBlock.astro` — macOS traffic lights, `filename="terminal"`, hover copy button | The hero command block already exists as a component | -| Tab strip component | `site/src/components/ui/CodeTabs.astro` — dependency-free, group-synced, localStorage-persisted, no-JS fallback | Per-agent tabs (Claude Code/Codex/Cursor/VS Code) for free | -| The content itself | `docs/guides/coding-agents.mdx` (the `xnet connect` guide), `docs/guides/agent-interfaces.mdx` (three lanes + 0.11x benchmark), `docs/ai/understanding-xnet.mdx` | The `/agents` page is 80% assembly of existing prose | -| Changelog receipts | 12+ agent fragments in `site/src/data/changelog/` (e.g. `2026-07-24-use-xnet-from-claude-code-and-codex.json`, `2026-08-01-verify-what-your-agent-did.json`) | Social-proof strip: real dated receipts, no invented testimonials | -| llms-full pipeline | `site/scripts/build-llms-full.ts` + `site/src/sidebar.mjs` (single source of truth for docs order **and** llms-full order) | Reordering the sidebar reorders the agent-readable corpus too | -| Data-file pattern | `site/src/data/*.ts` driving every page | `agents.ts` slots in beside `pricing.ts`/`compare.ts` | +| Asset | Path | Why it matters | +| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| Terminal chrome component | `site/src/components/ui/CodeBlock.astro` — macOS traffic lights, `filename="terminal"`, hover copy button | The hero command block already exists as a component | +| Tab strip component | `site/src/components/ui/CodeTabs.astro` — dependency-free, group-synced, localStorage-persisted, no-JS fallback | Per-agent tabs (Claude Code/Codex/Cursor/VS Code) for free | +| The content itself | `docs/guides/coding-agents.mdx` (the `xnet connect` guide), `docs/guides/agent-interfaces.mdx` (three lanes + 0.11x benchmark), `docs/ai/understanding-xnet.mdx` | The `/agents` page is 80% assembly of existing prose | +| Changelog receipts | 12+ agent fragments in `site/src/data/changelog/` (e.g. `2026-07-24-use-xnet-from-claude-code-and-codex.json`, `2026-08-01-verify-what-your-agent-did.json`) | Social-proof strip: real dated receipts, no invented testimonials | +| llms-full pipeline | `site/scripts/build-llms-full.ts` + `site/src/sidebar.mjs` (single source of truth for docs order **and** llms-full order) | Reordering the sidebar reorders the agent-readable corpus too | +| Data-file pattern | `site/src/data/*.ts` driving every page | `agents.ts` slots in beside `pricing.ts`/`compare.ts` | ### Constraints that will bite (from the survey) @@ -137,11 +137,11 @@ verify empirically before quoting it in launch-day plans). From the 2026 survey of dev-tool landers: - **Bun**: headline + copyable `curl … | bash` with OS tabs + versioned - install button + a *replayable* benchmark race above the fold. Its trophy + install button + a _replayable_ benchmark race above the fold. Its trophy logos are agent products (Claude Code, Cursor, Midjourney, Railway). - **Claude Code itself**: name + "Work with Claude directly in your codebase…" + download button **and** install one-liner. MCP integrations - are a late section — the mirror image of xNet, which *is* the + are a late section — the mirror image of xNet, which _is_ the integration and should lead with the connect command. - **Homebrew**: the page essentially is the command. **Ollama**: one action; cloud reduced to "Free to start. See pricing." @@ -150,7 +150,7 @@ From the 2026 survey of dev-tool landers: ledger can eventually feed. - **Evil Martians' 100-lander study**: exactly **two** CTAs (one dominant, one subordinate); specific verb copy over "Get started"; for - libraries/infra a code snippet *is* the right hero visual; pricing on + libraries/infra a code snippet _is_ the right hero visual; pricing on its own page. ### The "add to your agent" affordance stack (mid-2026 table stakes) @@ -162,7 +162,7 @@ From the 2026 survey of dev-tool landers: reported flaky), **VS Code badge** (`vscode:mcp/install?`). - Directory distribution (Smithery ~16.8k MCPs listed; mcp.so ~20k secondhand) is marketing reach, not a substitute for the page. -- Docs sites are now expected to *be* agent-consumable: Mintlify ships +- Docs sites are now expected to _be_ agent-consumable: Mintlify ships `/llms.txt`, `/llms-full.txt`, per-page "copy as Markdown"; GitBook auto-exposes an MCP endpoint per docs site. @@ -172,9 +172,9 @@ Netlify named the category — "Agent Experience (AX)" — and in April 2026 launched **netlify.ai, a site built for agents rather than humans** (onboarding and build context for the agent itself). The nuance from the llms.txt adoption data: no major AI vendor commits to llms.txt for -*search/training*, but **coding agents do fetch `/llms.txt` when pointed +_search/training_, but **coding agents do fetch `/llms.txt` when pointed at a docs site** — which is precisely xNet's conversion moment: an agent -is *running* `xnet connect` while its human watches. xNet's llms.txt +is _running_ `xnet connect` while its human watches. xNet's llms.txt currently forgets to mention the connect flow at all. One more external fact that shapes copy: **Continue.dev's lander is now an @@ -202,7 +202,7 @@ clients as tabs — never as the headline. 3. **Honesty gates the section order.** The three lanes, read-only default, passports, signed log, and the benchmark are shipped and true — Phase A can say them loudly today. The loop ("your agent builds tools - *inside* the workspace") is 0455/0456 wiring away; the site must not + _inside_ the workspace") is 0455/0456 wiring away; the site must not promise it before the demo records. The Charter's own rule (every promise ships with a receipt or is labeled not-yet) applies to marketing exactly as to docs. @@ -210,7 +210,7 @@ clients as tabs — never as the headline. 4. **The benchmark is the single most quotable asset and the most fragile.** "~9x cheaper than MCP tools" already appears on the landing page; the hero will amplify it. 0456 already requires the methodology - to be published and reproducible — that item becomes a *prerequisite* + to be published and reproducible — that item becomes a _prerequisite_ of Phase A launch, not a follow-up. 5. **Demotion must be visible-but-cheap.** The lesson from Ollama ("Free @@ -272,7 +272,7 @@ dogfood-metric proof strip. - ❌ Splits authority and maintenance for a solo founder; the survey shows the main site's traffic surfaces (README, llms.txt, docs) are exactly where the fix is needed; a microsite duplicates the Starlight/llms - pipeline. The agent-twin *pages* (Option B) capture the netlify.ai idea + pipeline. The agent-twin _pages_ (Option B) capture the netlify.ai idea without a second property. ### Option D — Docs-as-landing (Tailwind posture) @@ -326,7 +326,7 @@ Make `/docs` the homepage; kill the marketing site's hero. becomes "There's a full workspace app behind this — and an SDK under both. [App] · [SDK] · [Protocol]" as small links. - Verify the exact zero-install one-liner before shipping (`npx - @xnetjs/cli …` vs `npm i -g` — whichever `packages/cli` actually +@xnetjs/cli …` vs `npm i -g` — whichever `packages/cli` actually supports; the checklist carries this). - Static command block first; a typed-replay animation is a Phase B nicety, not a blocker (Deno converts with no animation at all). @@ -380,7 +380,7 @@ source (same pattern as `pricing.ts`). after connect you get these tools/lanes…"), plus the missing coding-agents entry. - New `public/agents.md` (the netlify.ai move, one page not a microsite): - what xNet is *to an agent*, the three lanes, tool list, safety + what xNet is _to an agent_, the three lanes, tool list, safety contract, where the SKILL.md comes from. Linked from llms.txt and `/agents`. - Optional same-PR cheap wins while in `Base.astro`: `og:title`/ @@ -390,7 +390,7 @@ source (same pattern as `pricing.ts`). ### 6. README (root) Mirror the site's new order: after the one-liner and screenshot, **Try it** -gains "Connect your coding agent" as the *first* bullet (`npx @xnetjs/cli +gains "Connect your coding agent" as the _first_ bullet (`npx @xnetjs/cli connect claude-code`), before demo/download/hub; a short "Your agent, your workspace" section (three lanes + benchmark + safety line) lands above "Build with it". Zero agent mentions today → the second landing @@ -422,7 +422,7 @@ URLs, content, and validators untouched. the mitigation; sequence at least one outside run before the hero flips. Also confirm `npx @xnetjs/cli connect` works without global install (and without a pre-existing workspace — the "thin room" question - from 0456: what does a fresh agent connect *to*? The `/agents` page + from 0456: what does a fresh agent connect _to_? The `/agents` page should answer with the demo-seed or `xnet vault init` story). - **Cursor/VS Code deeplinks are flaky** (documented forum failures) — always render the copyable JSON beside the button; treat the deeplink as @@ -450,8 +450,8 @@ URLs, content, and validators untouched. **Status:** ░░░░░░░░░░ 0/12 items (Phase A: 1–10; Phase B: 11–12) -- [ ] Verify + document the canonical zero-install command (`npx - @xnetjs/cli connect claude-code` or equivalent) against +- [x] Verify + document the canonical zero-install command (`npx + @xnetjs/cli connect claude-code` or equivalent) against `packages/cli` as published; fix `packages/cli` if npx flow has gaps - [ ] Publish the 0.11x benchmark methodology in-repo (0456 item, now a diff --git a/packages/cli/src/__tests__/connect-command.test.ts b/packages/cli/src/__tests__/connect-command.test.ts index 177e393f1..ba86c1672 100644 --- a/packages/cli/src/__tests__/connect-command.test.ts +++ b/packages/cli/src/__tests__/connect-command.test.ts @@ -13,9 +13,12 @@ import { MANAGED_BEGIN, MANAGED_END, mergeManagedBlock, + NPX_LAUNCHER, + resolveServerLauncher, runConnect, writeCodexConfig, writeMcpJson, + XNET_PATH_LAUNCHER, type ConnectOptions } from '../commands/connect.js' @@ -36,8 +39,22 @@ describe('xnet connect', () => { expect(buildServerEntry({ dir, db: '/d.db' }).args).toEqual(['mcp', 'serve', '--db', '/d.db']) }) + it('registers an npx launcher when xnet is not on PATH (zero-install connect)', async () => { + // A PATH with no xnet bin anywhere → the npx fallback, so the registered + // server survives after the `npx @xnetjs/cli connect …` cache is gone. + expect(resolveServerLauncher({ PATH: dir })).toEqual(NPX_LAUNCHER) + + // A PATH dir that does hold an xnet bin → register the real thing. + await writeFile(join(dir, 'xnet'), '#!/bin/sh\n') + expect(resolveServerLauncher({ PATH: `${dir}` })).toEqual(XNET_PATH_LAUNCHER) + + const entry = buildServerEntry({ dir, db: '/d.db' }, NPX_LAUNCHER) + expect(entry.command).toBe('npx') + expect(entry.args).toEqual(['-y', '@xnetjs/cli', 'mcp', 'serve', '--db', '/d.db']) + }) + it('claude-code writes skill, .mcp.json, and CLAUDE.md; is idempotent', async () => { - const changes = await runConnect('claude-code', { ...base, dir }) + const changes = await runConnect('claude-code', { ...base, dir }, XNET_PATH_LAUNCHER) const byPath = Object.fromEntries(changes.map((c) => [c.path.replace(dir, ''), c.status])) expect(byPath['/.claude/skills/xnet/SKILL.md']).toBe('created') expect(byPath['/.mcp.json']).toBe('created') @@ -48,12 +65,12 @@ describe('xnet connect', () => { expect(mcp.mcpServers.xnet.env).toEqual({ XNET_READONLY: '1' }) // Re-run: everything unchanged. - const again = await runConnect('claude-code', { ...base, dir }) + const again = await runConnect('claude-code', { ...base, dir }, XNET_PATH_LAUNCHER) expect(again.every((c) => c.status === 'unchanged')).toBe(true) }) it('codex writes AGENTS.md and .codex/config.toml with a valid server block', async () => { - const changes = await runConnect('codex', { ...base, dir, writes: true }) + const changes = await runConnect('codex', { ...base, dir, writes: true }, XNET_PATH_LAUNCHER) const byPath = Object.fromEntries(changes.map((c) => [c.path.replace(dir, ''), c.status])) expect(byPath['/AGENTS.md']).toBe('created') expect(byPath['/.codex/config.toml']).toBe('created') @@ -84,7 +101,7 @@ describe('xnet connect', () => { const original = '# My project\n\n@AGENTS.md\n\nHouse rules that took months.\n' await writeFile(join(dir, 'CLAUDE.md'), original) - await runConnect('claude-code', { ...base, dir }) + await runConnect('claude-code', { ...base, dir }, XNET_PATH_LAUNCHER) const merged = await readFile(join(dir, 'CLAUDE.md'), 'utf8') expect(merged).toContain('# My project') expect(merged).toContain('House rules that took months.') @@ -94,11 +111,11 @@ describe('xnet connect', () => { }) it('rewrites only the managed block on a re-run, leaving edits outside it', async () => { - await runConnect('claude-code', { ...base, dir }) + await runConnect('claude-code', { ...base, dir }, XNET_PATH_LAUNCHER) const first = await readFile(join(dir, 'CLAUDE.md'), 'utf8') await writeFile(join(dir, 'CLAUDE.md'), `${first}\n## My own section\n\nKeep me.\n`) - const again = await runConnect('claude-code', { ...base, dir }) + const again = await runConnect('claude-code', { ...base, dir }, XNET_PATH_LAUNCHER) const merged = await readFile(join(dir, 'CLAUDE.md'), 'utf8') expect(merged).toContain('## My own section') expect(merged).toContain('Keep me.') @@ -109,7 +126,7 @@ describe('xnet connect', () => { it('preserves an existing AGENTS.md on the codex path', async () => { await writeFile(join(dir, 'AGENTS.md'), '# Existing agent rules\n') - await runConnect('codex', { ...base, dir }) + await runConnect('codex', { ...base, dir }, XNET_PATH_LAUNCHER) const merged = await readFile(join(dir, 'AGENTS.md'), 'utf8') expect(merged).toContain('# Existing agent rules') expect(merged).toContain(MANAGED_BEGIN) diff --git a/packages/cli/src/commands/connect.ts b/packages/cli/src/commands/connect.ts index 8215f462b..aa737456f 100644 --- a/packages/cli/src/commands/connect.ts +++ b/packages/cli/src/commands/connect.ts @@ -18,9 +18,10 @@ * shell-less clients. */ +import { existsSync } from 'node:fs' import { mkdir, readFile, writeFile } from 'node:fs/promises' import { homedir } from 'node:os' -import { dirname, join, resolve } from 'node:path' +import { delimiter, dirname, join, resolve } from 'node:path' import { XNET_AGENT_SKILL_MD } from '@xnetjs/plugins/node' import { Command } from 'commander' import { parse as parseToml, stringify as stringifyToml } from 'smol-toml' @@ -52,14 +53,44 @@ export type McpServerEntry = { command: string; args: string[]; env?: Record { const dir = resolve(options.dir) const changes: ConnectChange[] = [] - const entry = buildServerEntry(options) + const entry = buildServerEntry(options, launcher) if (harness === 'claude-code') { changes.push( diff --git a/site/src/content/docs/docs/guides/coding-agents.mdx b/site/src/content/docs/docs/guides/coding-agents.mdx index b1bc89383..fc1197e8d 100644 --- a/site/src/content/docs/docs/guides/coding-agents.mdx +++ b/site/src/content/docs/docs/guides/coding-agents.mdx @@ -19,6 +19,14 @@ depth; this one is the quick on-ramp. ## One-step connect +No install needed — `npx` runs the published CLI directly: + +```bash +npx @xnetjs/cli connect claude-code # zero-install, project scope, read-only +``` + +Or, with the CLI installed (`npm install -g @xnetjs/cli`): + ```bash xnet connect claude-code # project scope, read-only xnet connect claude-code --user # also install the skill for all projects @@ -27,12 +35,16 @@ xnet connect codex # Codex: AGENTS.md + .codex/config.toml ``` `xnet connect` is **idempotent** — it installs the `SKILL.md`, registers the -`xnet` MCP server, writes a `CLAUDE.md`/`AGENTS.md` contract, and (with +xNet MCP server, writes a `CLAUDE.md`/`AGENTS.md` contract, and (with `--vault `) bootstraps a scoped checkout with an index. Re-running it reports what is already in place and changes nothing. It finishes by running `xnet doctor --agent-access`, which confirms the backend, full-text search, and signing identity are all reachable. +The registered server launches however the CLI is actually available: as +`xnet` when the bin is on your PATH, or as `npx -y @xnetjs/cli` after a +zero-install connect — so the registration keeps working either way. + ## Works whether or not the app is running The agent verbs resolve a backend automatically: From 2080d7a1592e5580397be8a5df37ad9d85dc334e Mon Sep 17 00:00:00 2001 From: xNet Test Date: Fri, 21 Aug 2026 18:31:18 -0700 Subject: [PATCH 05/16] docs(site): publish the agent-surface benchmark methodology Signed-off-by: xNet Test --- .../docs/docs/guides/agent-interfaces.mdx | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/site/src/content/docs/docs/guides/agent-interfaces.mdx b/site/src/content/docs/docs/guides/agent-interfaces.mdx index 1f2f68fbb..630855371 100644 --- a/site/src/content/docs/docs/guides/agent-interfaces.mdx +++ b/site/src/content/docs/docs/guides/agent-interfaces.mdx @@ -23,7 +23,8 @@ workspace with a malformed write. Measured on a 15-task benchmark, the files+CLI surface completes the same work at **~0.11x the tokens** of a traditional MCP toolset (0.05x on synthesis -tasks), with equal task success. +tasks), with equal task success. The +[methodology and how to reproduce it](#benchmark-methodology) are below. ## The checkout @@ -96,6 +97,40 @@ tokens). Responses are compact JSON by default with The doctrine, in order: **files for reading and editing, the CLI for what files can't express, MCP only when there's no shell.** +## Benchmark methodology + +The "~0.11x the tokens" claim comes from an in-repo, reproducible benchmark — +not a one-off measurement. What it is, precisely: + +- **The suite**: 15 tasks (read a page, edit a page, query a database, bulk + update, cross-node synthesis) run against a seeded workspace fixture. Every + task really executes against the same plan → validate → apply core the + product uses; **success is measured, not assumed**. +- **Three surfaces**: `files-cli` (vault checkout + file tools + `xnet` CLI), + `mcp-legacy` (all tool definitions standing, pretty-printed JSON), and + `mcp-slim` (core tools standing, compact JSON — today's MCP fallback). +- **What's counted**: a token cost model (~4 chars/token) over the *actual + bytes* each surface moves through model context — standing tool/skill + definitions, request arguments, tool responses, file contents read, edit + diffs, CLI commands and their outputs. +- **What's not counted**: model reasoning tokens. This is an **interface cost + model** — it measures what each surface forces into context, not how a + specific model thinks. Live harness runs with pinned versions are tracked + separately (exploration 0161). +- **Result**: files+CLI at 0.111x the total tokens of the legacy MCP surface + overall, 0.050x on synthesis tasks, 15/15 task success on every surface. + +Reproduce it from a repo checkout: + +```bash +pnpm bench:agent-surfaces +``` + +Source: `packages/plugins/src/benchmarks/agent-surface-benchmark.ts`. The +ratios are **regression-guarded in CI** by +`agent-surface-benchmark.test.ts`, so the numbers quoted here fail the build +if the surfaces drift. + ## Further reading - [For AI Assistants](/docs/ai/understanding-xnet/) — the mental model for From 1015966e1e91e4d076ea8c3210063b9111db7c11 Mon Sep 17 00:00:00 2001 From: xNet Test Date: Fri, 21 Aug 2026 18:33:26 -0700 Subject: [PATCH 06/16] feat(site): add /agents conversion page and agents.ts data source Per-client connect tabs (Claude Code, Codex, Cursor deeplink, VS Code badge, generic MCP), the three lanes with the benchmark methodology link, the structural safety model, and dated changelog receipts. validate-dist derives expected routes from src/pages, so the new page is covered automatically. Signed-off-by: xNet Test --- ...457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md | 8 +- site/src/data/agents.ts | 88 +++++++ site/src/pages/agents.astro | 214 ++++++++++++++++++ 3 files changed, 306 insertions(+), 4 deletions(-) create mode 100644 site/src/data/agents.ts create mode 100644 site/src/pages/agents.astro diff --git a/docs/explorations/0457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md b/docs/explorations/0457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md index cc9efdd7f..a230bad32 100644 --- a/docs/explorations/0457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md +++ b/docs/explorations/0457_[_]_AGENT_FIRST_SITE_REARCHITECTURE.md @@ -451,16 +451,16 @@ URLs, content, and validators untouched. **Status:** ░░░░░░░░░░ 0/12 items (Phase A: 1–10; Phase B: 11–12) - [x] Verify + document the canonical zero-install command (`npx - @xnetjs/cli connect claude-code` or equivalent) against + @xnetjs/cli connect claude-code` or equivalent) against `packages/cli` as published; fix `packages/cli` if npx flow has gaps -- [ ] Publish the 0.11x benchmark methodology in-repo (0456 item, now a +- [x] Publish the 0.11x benchmark methodology in-repo (0456 item, now a Phase A prerequisite) and link target for the hero footnote -- [ ] `site/src/data/agents.ts` — per-client commands, deeplinks, labels +- [x] `site/src/data/agents.ts` — per-client commands, deeplinks, labels (single source for hero tabs, `/agents`, README, docs) - [ ] `Hero.astro` rewrite: new headline/sub, `CodeTabs` + `CodeBlock` command block, two CTAs, doors → small links -- [ ] New `site/src/pages/agents.astro` per the section spec; update +- [x] New `site/src/pages/agents.astro` per the section spec; update `scripts/validate-dist.ts` expectations if route-asserting - [ ] `Nav.astro` (+Agents link; button → "Connect your agent") and `Footer.astro` (+Agents column) diff --git a/site/src/data/agents.ts b/site/src/data/agents.ts new file mode 100644 index 000000000..697845616 --- /dev/null +++ b/site/src/data/agents.ts @@ -0,0 +1,88 @@ +/** + * Single source of truth for the agent-door connect commands (exploration + * 0457). The hero tabs, /agents, and GetStarted all render from this file so + * the command a visitor copies is the same everywhere — and there is exactly + * one place to change when a client's install convention moves. + * + * Two affordances per GUI client, deliberately: the one-click deeplink is + * progressive enhancement (Cursor deeplinks have documented failure modes), + * and the copyable config is the fallback that always works. + */ + +/** MCP server config shared by the Cursor/VS Code registrations. The npx + * launcher matches what `xnet connect` itself registers when the `xnet` bin + * is not on PATH — the registration works with zero prior install. */ +const MCP_COMMAND = { + command: 'npx', + args: ['-y', '@xnetjs/cli', 'mcp', 'serve'], + env: { XNET_READONLY: '1' } +} as const + +/** `.cursor/mcp.json` / `.mcp.json`-shaped config for the copyable fallback. */ +export const MCP_CONFIG_JSON = JSON.stringify({ mcpServers: { xnet: MCP_COMMAND } }, null, 2) + +/** Cursor one-click install deeplink (config is base64 of the server entry). */ +const cursorDeeplink = (() => { + const config = Buffer.from(JSON.stringify(MCP_COMMAND)).toString('base64') + return `cursor://anysphere.cursor-deeplink/mcp/install?name=xnet&config=${encodeURIComponent(config)}` +})() + +/** VS Code one-click install URL (`vscode:mcp/install?`). */ +const vscodeDeeplink = (() => { + const payload = JSON.stringify({ name: 'xnet', ...MCP_COMMAND }) + return `vscode:mcp/install?${encodeURIComponent(payload)}` +})() + +export interface AgentClient { + id: 'claude-code' | 'codex' | 'cursor' | 'vscode' | 'any' + /** Tab / button label. */ + label: string + /** Shell lines for the copyable terminal block (plain text; pages highlight). */ + command: string[] + /** One-line note rendered under the block. */ + note: string + /** Optional one-click install link — always paired with the copyable form. */ + deeplink?: { href: string; label: string } + /** Copyable JSON config fallback for GUI clients (no shell involved). */ + configJson?: string +} + +export const AGENT_CLIENTS: AgentClient[] = [ + { + id: 'claude-code', + label: 'Claude Code', + command: ['npx @xnetjs/cli connect claude-code'], + note: 'Installs the ~500-token skill, registers the MCP fallback, self-checks. Read-only until --writes.' + }, + { + id: 'codex', + label: 'Codex', + command: ['npx @xnetjs/cli connect codex'], + note: 'Writes the AGENTS.md contract and registers the server in .codex/config.toml. Read-only until --writes.' + }, + { + id: 'cursor', + label: 'Cursor', + command: [MCP_CONFIG_JSON], + note: 'Paste into .cursor/mcp.json — or use the one-click install button.', + deeplink: { href: cursorDeeplink, label: 'Add xnet to Cursor' }, + configJson: MCP_CONFIG_JSON + }, + { + id: 'vscode', + label: 'VS Code', + command: [MCP_CONFIG_JSON], + note: 'Paste into your MCP config — or use the one-click install button.', + deeplink: { href: vscodeDeeplink, label: 'Install in VS Code' }, + configJson: MCP_CONFIG_JSON + }, + { + id: 'any', + label: 'Any agent', + command: ['npx -y @xnetjs/cli mcp serve'], + note: 'A standard MCP server over stdio — point any MCP-capable client at it. Set XNET_READONLY=1 for read-only.' + } +] + +/** The one canonical command quoted in prose (hero, README, docs). */ +export const CANONICAL_CONNECT = 'npx @xnetjs/cli connect claude-code' diff --git a/site/src/pages/agents.astro b/site/src/pages/agents.astro new file mode 100644 index 000000000..bd3b42162 --- /dev/null +++ b/site/src/pages/agents.astro @@ -0,0 +1,214 @@ +--- +import Base from '../layouts/Base.astro' +import Nav from '../components/sections/Nav.astro' +import Footer from '../components/sections/Footer.astro' +import SectionHeader from '../components/ui/SectionHeader.astro' +import CodeBlock from '../components/ui/CodeBlock.astro' +import CodeTabs from '../components/ui/CodeTabs.astro' +import { AGENT_CLIENTS } from '../data/agents' +import { entries } from '../data/changelog' + +// The conversion hub for the agent door (exploration 0457): per-client +// install, what the agent gets, the safety model, and dated receipts. Depth +// stays in the docs — this page routes, it never re-argues (0384). + +const cm = (s: string) => `${s}` +const escapeHtml = (s: string) => + s.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>') + +/** Render a client's command lines as a highlighted terminal block. */ +const commandHtml = (lines: string[]) => + lines + .map((line) => + line.startsWith('{') ? escapeHtml(line) : `${cm('$')} ${escapeHtml(line)}` + ) + .join('\n') + +const installTabs = AGENT_CLIENTS.map((client) => ({ + label: client.label, + filename: client.configJson ? 'mcp config' : 'terminal', + code: commandHtml(client.command) +})) + +const lanes = [ + { + step: '1', + title: 'CLI — the default lane', + description: + 'xnet search, query, and db get print plain stdout an agent greps and pipes. No tool schemas standing in context.' + }, + { + step: '2', + title: 'Vault — files the agent already understands', + description: + 'xnet checkout materializes a scoped slice as Markdown and JSONL. Edits become schema-validated mutation plans on xnet commit — a malformed write can’t corrupt anything.' + }, + { + step: '3', + title: 'MCP — the no-shell fallback', + description: + 'A slim server (five core tools standing, everything else deferred) for clients that can’t run a CLI. Read-only unless connected with --writes.' + } +] + +const safety = [ + { + title: 'Read-only by default', + description: + 'xnet connect registers the server read-only. Writes are an explicit opt-in (--writes), and the CLI refuses to persist a write under a throwaway identity.' + }, + { + title: 'Its own key, its own passport', + description: + 'An agent signs with an enrolled agent passport or a key you provide — never silently as you. Scope its grant as narrowly as you like.' + }, + { + title: 'Every change in a signed log', + description: + 'Each write lands in the workspace’s signed, hash-chained change log. Verify what your agent did — without trusting us, or it.' + } +] + +// Dated receipts: real shipped changelog entries from the agent lane, newest +// first — instead of invented testimonials. +const receipts = entries.filter((entry) => entry.tags.includes('ai')).slice(0, 4) +--- + + +