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:

  1. Ignore fields they do not understand within schema major version 1.
  2. Ignore unknown capability and surface identifiers rather than failing the whole manifest.
  3. Treat any unlisted capability or ecosystem as unsupported. Absence never means experimental or supported.
  4. Reject an unknown schema major version until the consumer explicitly supports it.
  5. Use each entry's status; do not promote experimental or unsupported behavior 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:

  1. Strict property validation: Output schemas enforce additionalProperties: false across all object definitions. CLI output must not contain properties undeclared in the schema.
  2. Deliberate schema updates: Adding a field to any --json output is never done implicitly. It requires an explicit, reviewed update to the relevant JSON Schema under schemas/, defining the property's type, constraints, and intent.
  3. Additive changes in v1: New optional fields can be introduced in the current v1 major schema release without breaking backward-compatible consumers.
  4. Breaking changes require a schema bump: Removing existing properties, renaming fields, or altering requiredness / types requires a new schema major version (such as v2).
  5. Contract test enforcement: Snapshot tests in tests/schema-snapshots.test.ts validate 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.

View Markdown

Use with an agent
View Markdown

Open in