# Software classification

> How inspect.software determines what a repository builds — the multi-label reading of whether software is consumed as code, run as a program, or installed into a host.


A repository is not simply "a project". It builds something, and what it builds
decides which practices are reasonable to expect from it. A published library
is expected **not** to commit a dependency lockfile — the versions it pins
would be ignored by everyone installing it. The application beside it in the
catalogue is expected to commit one, because the versions it pins are exactly
what gets deployed. Judging both by one rule misreads one of them.

Until metrics 2.3.0 a single proxy stood in for the question: does the
repository publish a package to a registry. That reads every command-line tool
on PyPI as a library, and every application that happens to ship one helper
package as one too.

## Every reading the evidence supports

The classification is **multi-label**. Software that is both a published
library and a runnable tool is the ordinary case, not a contradiction to be
resolved: ripgrep is a crate and a binary, esbuild is an npm package and an
executable. A repository therefore keeps every reading its evidence supports.

Three questions are answered independently, and a repository can answer yes to
more than one:

| Question | True for | What it makes reasonable to expect |
| --- | --- | --- |
| **Consumed as code** | library, framework, SDK, API client, middleware, driver | A stable interface, a changelog, versioning other software can depend on |
| **Run as a program** | command-line tool, terminal interface, desktop and mobile application, web interface, network service, chat bot, MCP server | Pinned dependencies, deployment and configuration hygiene |
| **Installed into a host** | plugin, extension, theme, editor tooling | The host supplies the trust model and the release cadence |

Where both apply, both apply — a hybrid owes the obligations of every reading
it carries, never the most lenient one.

## The labels

| Consumed as code | Run as a program | Installed into a host |
| --- | --- | --- |
| Library | Command-line tool | Plugin |
| Framework | Terminal interface | Extension |
| SDK | Desktop application | Theme |
| API client | Mobile application | Editor tooling |
| Middleware | Web interface | |
| Driver | Network service | |
| | Chat bot | |
| | MCP server | |

*Notebook* is a label of its own and belongs to none of the three: it is read
and run by a person, imported by nothing, and installed into nothing.

## How the answer is reached

Evidence is ranked by how far it can be trusted, and **no single weak signal
produces a label**.

| Evidence | Examples | Weight |
| --- | --- | --- |
| **Declared in a build manifest** | an npm `bin` entry, a `console_scripts` entry point, `<OutputType>Exe</OutputType>`, Composer's `type`, Maven's `<packaging>`, a Cargo `[lib]` target | Decisive |
| **Declared by the registry** | a Packagist type, a NuGet package type, a crates.io category | Strong |
| **Declared dependencies** | a web framework, a CLI argument parser, a chat-platform client | Moderate |
| **File-tree structure** | `cmd/…/main.go`, `src-tauri/`, a Helm chart, a browser-extension manifest | Supporting |
| **Repository topics and registry keywords** | `cli`, `wordpress-plugin`, `self-hosted` | Weak — self-assigned |
| **The repository description** | "a CLI for…", "a library for…" | Weak — self-assigned |

The first two are the strongest because they are not opinions: a maintainer who
writes `<OutputType>Exe</OutputType>` is not describing the software, the build
would not work otherwise. A topic is an opinion, and two of them agreeing is
the minimum that carries anything at all.

Evidence also runs the other way. A Cargo package with `publish = false`, a
Composer `type` of `project`, a `.NET` tool, a Maven `war` — each **rules out**
being something other software can depend on, whatever else the repository
looks like.

## Where the answer is absent

Three situations produce no classification, and all three are ordinary:

- **The report predates metrics 2.3.0.** Every stored report was
  reclassified from the facts it already carried, but the manifest evidence is
  collected during a scan — so reports gathered earlier carry the weaker tiers
  only, and gain the full reading when the repository is next inspected.
- **The evidence does not answer the question.** Many repositories publish no
  manifest, carry no informative topics, and describe themselves in prose that
  states nothing structural.
- **The answer would be a guess.** Where signals are present but too weak to
  cross the threshold, nothing is claimed.

In all three the report simply shows no classification. **Absence is never
presented as a finding**, nothing is scored on it, and a repository without one
is not marked, ranked, or penalized in any way.

## Repositories that build several things

A monorepo holding a deployable service beside three published libraries is not
one artifact, and rolling it up to a single answer would lose the fact that
both readings are true. Each manifest is therefore classified on its own
declarations, and the repository carries the union.

One consequence is worth stating plainly: the single **primary** label shown
for such a repository is the best-supported one, which in a large monorepo can
be a published build tool rather than the product the project is known for. The
primary label exists for display and for grouping comparable repositories — it
is never the basis of a scoring decision.

## What it affects today

Nothing. As of metrics 2.3.0 the classification is published as data —
`metrics.classification` in every report, alongside the evidence that produced
it — and no metric reads it. Connecting it to the metrics that depend on it,
beginning with the dependency-lockfile expectation, is a separate change and
will be recorded in the [methodology versions](/wiki/methodology-versions) when
it lands.
