Installed harnesses in the Threads picker
A provider package being available on a portal is not the same as a person installing it.
CLI packages such as Claude Code declare personalInstall: true; the Store copies their
Harness/{id} node into the installer's {user}/Harness namespace. The installed node is
the selection, while the provider module supplies the runtime IHarness with that id.
BuiltInHarnessProvider deliberately omits runtimes whose definition has RequiresInstall.
Publishing every loaded CLI runtime in the global Harness namespace would offer it to people
who had never installed it. The global catalog still supplies the native MeshWeaver harness.
Harnesses are options of the Threads app, not apps
The coding-assistant harnesses β Claude Code, GitHub Copilot, Codex, Cursor, Grok, OpenCode and
Antigravity β are options of the Threads app (AI/AiThreads), never apps of their own. Each
package declares hostedIn: AI/AiThreads (PluginContent.HostedIn), which means:
- No home tile.
Localizer.ShouldRegisterApprefuses a hosted package, whatever else it declares. A tile minted before the package declared its host is removed automatically once the package update is delivered: the Store root purges hosted packages' tiles mesh-wide on activation and whenever the catalog's hosted set changes, and every tile refresh removes them as well. NoPurgeOrphanAppTilesrun is needed (that task remains for tiles whose package is absent). - No Store listing for non-admins.
StoreCatalogLayoutAreas.IsVisibleTohides a hosted package from everyone but a global admin, who still installs and updates packages there. - One home for everything a person does with a harness. The Threads app lists each harness
package with whether this instance offers it (a registered
IHarnessruntime; an instance without the runtime says so and offers no install), its processing and origin marks, a link to the viewer's own harness node ({user}/Harness/{id}, its settings) and the package's own Get/Uninstall action (CoverCta). - Per thread,
/harnesspicks which one runs it β the built-in MeshWeaver agent or an installed harness. The picker rows carry the same marks.
Which harnesses an instance runs is deployment configuration: the loaded modules and
Features:Ai:Clis:*. On the fleet, that belongs in the Models app (/Hosting/Models, the AI
domain's fleet app). A switch there is owed; today the flags are set in the record's portal
configuration.
The marks
A harness that runs its own agent loop declares the same two marks a language model carries
(AI/ModelDataResidency): where prompts are processed (Harness.DataResidency: πͺπΊ EU only,
π global, β not established) and where the maker is based (Harness.ModelOrigin +
Harness.ModelMaker). The runtime declares them (IHarness.Definition) and HarnessProvenance
resolves them by harness id. That way a per-user copy installed before the marks existed shows
them too. The native MeshWeaver harness carries none, because it processes wherever the picked
model does and each model row shows its own. β is never treated as EU.
| Harness | Processing | Origin | Why |
|---|---|---|---|
| Claude Code | π Global | πΊπΈ Anthropic | Anthropic's first-party API (api.anthropic.com) is a global endpoint |
| OpenAI Codex | π Global | πΊπΈ OpenAI | api.openai.com is a global endpoint |
| Grok Build | π Global | πΊπΈ xAI | api.x.ai is a global endpoint |
| Google Antigravity | π Global | πΊπΈ Google | the Gemini API (generativelanguage.googleapis.com) is a global endpoint |
| GitHub Copilot | β not established | πΊπΈ GitHub (Microsoft) | routes to several makers' models; no residency evidence |
| Cursor | β not established | πΊπΈ Anysphere (Cursor) | routes to several makers' models from its own service |
| OpenCode | β not established | β unknown | runs whichever provider the user configures |
The endpoint rule is DataResidency.FromEndpoint, the same one the model marks use. None of these
harnesses is EU-only.
One query contract
AgentPickerProjection.BuildHarnessQuery owns the exact namespace union:
namespace:{user}/Harness|{space}/Harness|Harness nodeType:Harness
The builder omits absent or reserved route partitions, deduplicates repeated namespaces and
projects the content needed by registry consumers. Both ObserveDefaultComposer and the
interactive ThreadChatView picker use it. The picker resolves the current navigation context
before deriving the space partition and retains its circuit-user access context for the read.
No fan-out across all readable partitions is needed or appropriate.
The /harness skill's global-only literal remains a platform fallback, not the query a
context-aware Threads client should execute. The picker recognizes the harness composer field
and resolves the canonical registry, just as it does for the agent and model fields.
Diagnose the boundary that failed
On 2026-09-22 an active personal Claude Code harness existed and the Store reported installed,
but the picker showed only MeshWeaver. Installation had succeeded. OpenPicker grouped harnesses
with custom commands and ran the skill's literal namespace:Harness query, while composer
defaults already read the personal + space + global union. The personal copy could never match
the interactive picker's query.
Check these separately before changing installation, permissions or runtime registration:
- Does the installed node exist at the user's actual
{user}/Harness/{id}path and read Active? - Does the picker query include that namespace under the same browser user?
- Does the selected full path resolve to an installed node and a registered runtime?
- Only then diagnose that runtime's connection and provider-specific login.
HarnessPickerQueriesTest pins the namespace union and its no-user and reserved-context cases.
HarnessPickerQueryTest executes it against a mesh containing personal, space, global and
uninstalled package entries. PickerHarnessQueriesTest pins the Threads routing from the
skill request to that canonical builder. The separate HarnessInstallGateTest covers runtime
execution's active-install requirement; passing it alone never proved picker discoverability.