ADR 0017: Strict-tier capability declarations as a CLI-emitted artifact

Field Value
Status Accepted
Date 2026-05-25
Deciders Project maintainers
Supersedes
Superseded by
Related ADRs ADR-0005 (Adapter contract shape), ADR-0012 (Spec↔core type reconciliation)

Summary

At Strict tier (strict conformance), aidokit init and aidokit sync emit a .aidokit/capabilities.json artifact derived from every declared adapter's manifest.capabilities. The artifact is the canonical machine-readable surface that downstream tooling — the upcoming capability verifier (workstream D4), aidokit verify, audit exports (C5/C6) — reads to assert what the install promises.

The artifact is emitted by the CLI, not by adapter methods. The Adapter interface gains no new methods; existing manifest fields are the source of truth.


Context

Workstream A's tier model promises Strict as the audit-ready tier (wiki/tiers/strict.md): capability declarations, signed manifest, audit export. The Capabilities and CapabilityDeclarations types are already exported from @aidokit/core and every adapter declares them in manifest.capabilities. What's missing is the artifact — a file in the user's project that records the declarations, that:

Today the declarations live only inside the npm package; the user has no in-repo artifact to point an auditor at. That gap blocks Strict from being a real product offering.

A second pressure: any extension to the Adapter interface ripples across three first-party adapters (claude-code, codex, copilot) and any future third-party adapter. Adding a new method (emitCapabilities) to satisfy Strict has high blast radius for what is, semantically, a read-only projection of data the manifest already carries.


Decision

1. The artifact is .aidokit/capabilities.json

A single JSON file under the project's .aidokit/ directory, written during init and re-written by sync whenever the install changes. Multi-adapter projects emit one entry per declared adapter.

{
  "specVersion": "2.0",
  "aidokitCliVersion": "...",
  "tier": "strict",
  "adapters": [
    {
      "name": "claude-code",
      "conformance": "strict",
      "capabilities": {
        "shellCommands": ["claude mcp add"],
        "writePaths": ["CLAUDE.md", ".claude/", "agent-artifacts/"],
        "networkHosts": []
      }
    }
  ]
}

The shape is the same CapabilityDeclarations type already exported by @aidokit/core, wrapped in a thin envelope with versioning metadata.

2. The artifact is emitted by the CLI, not by the adapter

packages/cli/src/init/capabilities-artifact.ts reads the state.json manifest plus each adapter's manifest.capabilities and produces the EmittedFile. The CLI's file-plan computation includes it at Strict tier only.

The Adapter interface (ADR-0005) gains no method. Adapters remain the source of truth for what they're capable of (via manifest); the CLI is the source of truth for how that surfaces in the user's project.

3. The artifact is Strict-only

Starter and Standard projects do not emit it. The semantics of the artifact — a stable, auditable contract surface — only matter at the tier that promises audit-readiness. Emitting it at lower tiers would suggest a guarantee the lower tiers don't make.

4. The artifact is included in the byte-compare gate

The Strict-tier reference fixture (when one exists — currently the reference is at Standard) will include the expected capabilities.json. Until then, the artifact is asserted only by the unit tests for the emitter.


Consequences

Positive

Negative

Neutral


Alternatives considered

Alternative 1: New emitCapabilities(ctx) method on Adapter

Add emitCapabilities(ctx: ProjectContext): readonly EmittedFile[] to the Adapter interface. Each adapter implements it (typically by serialising its own manifest.capabilities).

Pros

Cons

Reason rejected: the artifact is a pure projection of manifest.capabilities; pushing rendering into adapters adds boilerplate without flexibility we actually use. The contract surface should stay minimal.

Alternative 2: Tuck the artifact into emitEngineConfig

Have each adapter's emitEngineConfig include the capabilities file in its returned EmittedFile[].

Pros

Cons

Reason rejected: the artifact is cross-adapter and project-wide; putting it under a per-adapter engine directory breaks the multi-adapter case.

Alternative 3: Emit only at aidokit doctor --strict runtime, never to disk

Compute the capability declarations on demand from state.json + adapter manifests; never persist them.

Pros

Cons

Reason rejected: compliance evidence requires durable artifacts. The whole point of Strict tier is making the declarations auditable outside the running aidokit process.

Alternative 4: YAML instead of JSON

Use .aidokit/capabilities.yaml.

Pros

Cons

Reason rejected: marginal readability win does not justify a new dependency or hand-rolled YAML emission.


Implementation notes

The CLI module:

Schema versioning:


Validation

This decision is validated when:


References

Internal

External