# 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.

The vocabulary is a **two-level tree**. The top level answers the question
that matters — how the software is consumed — and a repository can carry more
than one branch:

| Top level | What it makes reasonable to expect |
| --- | --- |
| **Library** — consumed as code | A stable interface, a changelog, versioning other software can depend on |
| **Application** — run as a program | Pinned dependencies, deployment and configuration hygiene |
| **Host extension** — installed into a host | The host supplies the trust model and the release cadence |
| **Notebook** | None of the above — it is read and run by a person, imported by nothing, installed into nothing |

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

## The subtypes

Subtypes exist only where the finer answer changes what a reader should
expect: an application's interface modality, or the host class an extension
inherits its trust surface from.

| Application | Host extension |
| --- | --- |
| Command-line tool | Plugin |
| Terminal interface | Browser extension |
| Desktop | Editor extension |
| Mobile | Theme |
| Web interface | |
| Network service | |
| Chat bot | |
| MCP server | |

**Library has no subtypes on purpose.** Earlier revisions distinguished
framework, SDK, API client, middleware and driver, and those boundaries do not
exist: axios is an API client *and* a library, flask is a framework *and* a
library — a question with no right answer. What a library connects to is
recorded elsewhere, as an integration fact, not here.

**The answer may stop at the top level.** A Cargo binary target and Go's
`cmd/` layout prove that the repository builds *an executable* — not whether
it is a command-line tool or a server. Such a repository is classified simply
**Application**, and gains the subtype only when evidence for it exists. A
generic answer supported by evidence beats a specific one that would be a
guess.

## 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 the classification.** 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

One check, under a standing rule. Since metrics 2.5.0 the
[security posture](/wiki/security-posture) fallback's **dependency-lockfile
expectation** reads the classification: a published package whose manifest
declares an executable counts as an application and keeps the expectation,
where publication alone used to waive it.

The standing rule is that **scoring reads manifest declarations only**. A
label inferred from file structure, topics or the description is displayed and
recorded, but never moves a score — only what a build manifest states outright
(a `bin` entry, a console-script entry point, an `OutputType`) can. Everything
else about the classification remains data: `metrics.classification` in every
report, alongside the evidence that produced it. Further connections will be
recorded in the [methodology versions](/wiki/methodology-versions) as they
land.

Related: [supported ecosystems](/wiki/supported-ecosystems) ·
[the health index](/wiki/health-index) ·
[security posture](/wiki/security-posture) ·
[scan configuration](/wiki/scan-configuration) ·
[signals, not warranties](/wiki/signals-not-warranties)
