# 0004 — A capability is a job; a target space is a facet

- **Status:** accepted
- **Date:** 2026-08-03
- **Extends:** [`0002`](0002-the-plug-in-test.md), which governs what belongs in a Core baseline. This governs what may be a class at all.

## Context

An attempt to cluster the discovery cohort mechanically ([`FINDINGS.md` F8](../research/discovery/FINDINGS.md)) searched for browser-control vocabulary — `click`, `browser`, `page`, `element`, `selector`, `dom`, `locator` — and found 2 of 300 listings.

The search was wrong. Those tokens name a **place**, not a **job**. Re-run against the job shape instead — a service that both perceives and actuates over the same addressed handle — the same corpus yields 31 candidates. A 15× swing, from identical data, purely from what was looked for.

The error is one this project had already named. An early independent review of the proposal warned against "target types masquerading as capabilities" and offered the test *"does the proposed category name a noun? Nouns are target spaces. Capabilities are jobs."* The clustering attempt then committed exactly that error.

## Decision

> **A capability class names a job someone wants done. It never names where the job happens, or what kind of data it happens to.**
>
> Where and what-kind are **facets** of the job, or target-space characterizations. They are not classes.

### The job test

Name the thing a person wants done. If the candidate names the venue or the data type rather than the outcome, it is not a class.

| Candidate | Names | Verdict |
|---|---|---|
| "browser automation" | a venue | **facet** — `operator` + `browser` |
| "vector database" | a storage technique | **facet** of retrieval or record-store |
| "document store" | a data shape | **facet** |
| **operator** | *drive a surface on my behalf* | **class** |
| **retrieval** | *find things in a corpus* | **class** |

Nobody's job is "browser." Their job is *do this thing for me, using the browser.*

### Worked: operator

`operator` is the class. Its Core is surface-agnostic — **address a surface → perceive its state → act on a target → settle** — and its Core says nothing about browsers.

The surface type is the facet:

| Facet | Brings |
|---|---|
| `browser` | tabs, DOM, selectors, console, network |
| `desktop` | windows, OS-level input, screen regions |
| `terminal` | sessions, streams, exit codes |

This is why the layering matters practically. A model that has learned `operator` can drive a browser, a desktop, and a terminal, and only needs facet vocabulary for the surface-specific parts. Under "browser automation" as a class, it learns browsers and learns nothing transferable.

It is also the display analogy applied correctly. A display's class is geometry. OLED versus LCD versus e-ink is not a separate class of display — it is how this one is built.

## Why mechanical clustering cannot produce the map

The clustering attempt's headline result was that **133 of 300 judgeable listings had no dominant signature.** That was recorded as evidence against clean clustering. It is better read the other way.

**Names do not carry intent. That is the premise of this entire proposal.**

If tool names carried function, no semantic contract would be needed — an agent would read the names and know. Mechanical analysis failed to recover function for 61% of judgeable listings for precisely the reason an agent struggles with those same surfaces: the surface does not say what it means.

So that number is not a defect in the method. **It is a measurement of the gap the project exists to address.** It should be reported as such.

The corollary is stronger and constrains what the discovery study can deliver:

> A capability map cannot be derived from surfaces. It can only be **declared** by authors, using vocabulary the study helps establish.

The study's job is therefore not "find the categories." It is *find out what vocabulary the categories must cover, what shapes recur, and where the language has collisions and gaps.* Classification is the wrong frame; the right one is intent introspection — and the only party who can introspect a service's intent reliably is the party that built it.

This is also why the declaration model beats a classification model. When Ghostlight says *"I am an `operator` with a `browser` facet,"* that is the author declaring intent, not an outside party inferring it from tool names. Inference is what agents are stuck doing today, expensively and unreliably. A declaration replaces the inference rather than automating it.

## Consequences

1. **A proposed class must survive the job test before anything else.** If it names a venue or a data type, it is a facet and the class is somewhere above it.
2. **Target space is characterized, not categorized.** Two implementations of a class may operate over entirely disjoint target spaces and still share the contract — while remaining non-substitutable for a given job. Shared vocabulary is not shared suitability.
3. **The clustering attempt is corrected, not discarded.** Its retrieval finding stands ([`FINDINGS.md` F7](../research/discovery/FINDINGS.md)); its operator search was looking for the wrong thing, and its negative result is reframed above.
4. **Mechanical signals stay diagnostic, never determinative.** They can show that a vocabulary is fragmented — three retrieval concepts under eleven spellings — which is evidence about the *language*. They cannot establish what a service is.

## Open tension: the flagship class is named after a noun

`calendar` is a noun. Under the job test that is a smell, and honesty requires recording it rather than exempting the one worked example.

The defence is that `calendar` carries irreducible job semantics — availability, recurrence, attendees, timezone handling — that do not follow from "records that happen to have times." On that reading it is a job-bearing domain rather than a bare target space, and the job is closer to *manage my schedule* than to *store dated rows*.

That defence is plausible and untested. If it fails, the class is really `scheduling` with `calendar` as its target space. Recorded as open; it does not block anything here, and [`0003`](0003-calendar-core-audit.md)'s audit is unaffected either way.
