6 minutes read
Capability manifest and auto-discovery contract
Offing publishes one deterministic capability manifest for humans, agents, and integrations:
offing capabilities --json
The output uses the offing.capabilities.v1 contract. Its JSON Schema is
schemas/capabilities.v1.schema.json,
and a complete committed example is
examples/capabilities.json.
What the manifest describes
- the Offing package and product version;
- supported, experimental, and unsupported capabilities;
- supported detector ecosystems and their maturity;
- CLI, MCP, Skill, plugin, and CI surfaces and their entry points;
- the shared capability identifiers available through each surface; and
- compatibility rules for consumers reading newer manifests.
Detector maturity
Each ecosystem carries a maturity field next to its existing status:
maturity |
Shown in the docs as | What it promises users |
|---|---|---|
experimental |
Experimental | The detector runs and is usable. Findings, rule ids, and unclassified output may change between releases without notice. |
beta |
Beta | Findings were measured against independent ground truth. Rule and finding ids are listed in the changelog when they change. |
stable |
Supported | The same promise as beta on a larger and more varied corpus, as part of the documented product contract. |
maturity is the detector's level in
benchmarks/thresholds.json, which is the single
source of truth for decision 0013. It is not
a second, independent judgment.
An ecosystem's maturity is the lowest level among the detectors listed in its
detectorIds. When several detectors serve one ecosystem, the ecosystem is only as
mature as its least mature detector. Today nodejs and npm both list the npm
detector, so both take its level. A contract test fails when the manifest disagrees
with benchmarks/thresholds.json.
The existing status field keeps its meaning and is not replaced: it says whether
the ecosystem is a supported product surface, while maturity records the evidence
level of the detectors behind it. A reader that ignores unknown fields can keep
using status exactly as before; maturity is an additive optional field in the
v1 manifest.
Capability identifiers describe behavior, not transport. For example,
repository.scan is the same capability when reached through the CLI, local MCP
server, Agent Skill, experimental host plugin, or CI. Integrations should compare
identifiers rather than infer behavior from command or tool names.
Forward compatibility
Consumers of a v1 manifest must follow the compatibility object included in the document:
- Ignore fields they do not understand within schema major version 1.
- Ignore unknown capability and surface identifiers rather than failing the whole manifest.
- Treat any unlisted capability or ecosystem as unsupported. Absence never means experimental or supported.
- Reject an unknown schema major version until the consumer explicitly supports it.
- Use each entry's
status; do not promoteexperimentalorunsupportedbehavior based on a matching entry-point name.
New optional fields, identifiers, and surfaces using the defined statuses can be added in a compatible v1 minor manifest update. Removing or redefining an existing identifier, changing required field meaning or status vocabulary, or weakening the rules above requires a new schema major version.
JSON output schemas and policy for adding fields
Every command supporting --json (or --format json) produces output that strictly conforms
to its published JSON Schema in schemas/*.v1.schema.json (such as scan.v1.schema.json,
check.v1.schema.json, finding.v1.schema.json, context.v1.schema.json,
doctor.v1.schema.json, and data-status.v1.schema.json).
Because scripts, agents, and CI automations depend on exact JSON structures, Offing locks all published schemas with strict contract tests:
- Strict property validation: Output schemas enforce
additionalProperties: falseacross all object definitions. CLI output must not contain properties undeclared in the schema. - Deliberate schema updates: Adding a field to any
--jsonoutput is never done implicitly. It requires an explicit, reviewed update to the relevant JSON Schema underschemas/, defining the property's type, constraints, and intent. - Additive changes in v1: New optional fields can be introduced in the current
v1major schema release without breaking backward-compatible consumers. - Breaking changes require a schema bump: Removing existing properties, renaming
fields, or altering requiredness / types requires a new schema major version (such as
v2). - Contract test enforcement: Snapshot tests in
tests/schema-snapshots.test.tsvalidate real execution output for every command against its schema and assert that any undeclared property triggers schema validation failure.
Stability and privacy
The command reads a packaged constant and performs no repository scan, network request, telemetry, or configuration write. Identical Offing versions produce byte-for-byte identical JSON output. The package's contract tests validate both the CLI output and committed example against the public JSON Schema and ensure every surface references registered capability identifiers.
Exit codes and error codes
Every CLI failure belongs to one class. The class decides the exit code, and with
--json (or --format json) the same class is written to stdout as an
offing.error.v1 document, so an agent can branch without parsing messages.
Class (error.code) |
Exit code | Meaning |
|---|---|---|
| (none) | 0 | The command succeeded. |
policy |
1 | A gate you configured failed: check found an affected dependency at or above --fail-on, doctor reported a failed check, or --on-expired fail met an expired suppression. |
usage |
2 | The command line is wrong: unknown command, flag, or value, or a missing argument. |
data |
3 | The input is well formed but the data is not there: a finding ID that was not found, a pinned data version that is not available, or a data update that was refused. |
parse |
4 | An input could not be parsed: an invalid Package URL, an unreadable or unsupported baseline, or an AGENTS.md section that is damaged. |
internal |
5 | Offing failed unexpectedly. This is a bug; please report it. |
{
"schemaVersion": "offing.error.v1",
"error": {
"code": "usage",
"reason": "unknown-command",
"exitCode": 2,
"message": "Unknown command: frobnicate"
}
}
Each failure also carries one stable reason code, written as error.reason in the error document.
Reason (error.reason) |
Class | Raised when |
|---|---|---|
policy-failed |
policy |
A configured gate failed. No error document is written. |
invalid-argument |
usage |
A flag, value, or operand is wrong or missing. |
invalid-config |
usage |
The repository configuration file cannot be read or is invalid. |
invalid-data-version |
usage |
--data-version is not a data version. |
unknown-command |
usage |
The command name is not one Offing has. |
scaffold-failed |
usage |
new-rule or new-detector could not create its files: an invalid id, a taken name, an existing file, or no contributor checkout. |
finding-not-found |
data |
A finding ID matches no finding in the scan. |
finding-not-observed |
data |
A finding ID was not observed earlier in this MCP session. |
data-unavailable |
data |
A pinned data version or bundle is not available or does not verify. |
data-update-failed |
data |
offing data update could not finish. |
invalid-purl |
parse |
A Package URL is invalid. |
invalid-agent-file |
parse |
An AGENTS.md section is damaged. |
invalid-sbom |
parse |
An SBOM is not valid CycloneDX or SPDX. |
invalid-input |
parse |
Another input could not be parsed. |
scan-memory-exceeded |
internal |
The scan reached its heap limit. |
internal |
internal |
Offing failed unexpectedly. |
Reason codes are stable: one is never renamed or moved to another class. The source is ERROR_CODES in src/offing-error.ts.
The message is also written to stderr in every mode. A policy failure is a
verdict on a result that was already printed, so stdout stays that single result
document and no error document is added. The set of classes and their exit codes
is stable; new classes would be added with new numbers, never by renumbering.