ADR 0018: aidokit verify facet umbrella

Field Value
Status Accepted
Date 2026-05-26
Deciders Project maintainers
Supersedes
Superseded by
Related ADRs ADR-0011 (Schema library / Zod), ADR-0017 (Strict-tier capability artifact)

Summary

aidokit verify is extended from a narrow install-integrity check into a pluggable facet runner. Facets implement the VerifyFacet interface in @aidokit/core/verify. Five facets are reserved at v1.x: secrets, budget, deps, license, loop-cap. Only secrets ships fully implemented; the other four are reserved stubs that emit FACET_NOT_IMPLEMENTED warnings when enabled.

The umbrella gives the user one discoverable surface (aidokit verify --secrets, --budget, --deps, --license, --loop-cap, --all-facets), one shared policy file (.aidokit/policy.json), one output formatter set (human, --json, --sarif), and one aggregated exit code.


Context

The 25-pain-point analysis surfaces several independent failure modes that a structured AI workflow needs CI-side gates for:

Each is a static or quasi-static scan operating on the working tree (or staged diff) and emitting findings with a severity. They share the same shape: configurable rules, allowlist, severity, output formatters, exit-code semantics.

The existing aidokit verify [--capabilities] already owned the "verify install integrity" surface. Two design pressures shape this ADR:

  1. Surface multiplication risk. Shipping aidokit secrets, aidokit budget, aidokit deps-check, aidokit license, aidokit loop-cap as five top-level commands grows the CLI surface 5× for what is semantically one capability ("scan and report"). Discoverability suffers; documentation duplicates.
  2. Policy sharing. Each facet needs project-local configuration (allowlists, severity overrides, paths to skip). Five separate config files is worse than one.

Decision

1. One umbrella command, N facets

aidokit verify keeps its existing --capabilities mode and grows five new facet flags:

aidokit verify --secrets [--all] [--sarif] [--json]
aidokit verify --budget        # stub, emits FACET_NOT_IMPLEMENTED
aidokit verify --deps          # stub
aidokit verify --license       # stub
aidokit verify --loop-cap      # stub
aidokit verify --all-facets    # run every enabled facet

--all selects scan scope (full tree vs staged diff; default staged). --sarif emits SARIF 2.1.0 for GitHub code-scanning ingestion. --json emits the standard CLI envelope.

2. Facets implement VerifyFacet in @aidokit/core/verify

export interface VerifyFacet {
  id: 'secrets' | 'budget' | 'deps' | 'license' | 'loop-cap';
  run(ctx: VerifyContext, policy: VerifyPolicy): Promise<VerifyFinding[]>;
}

A facet returns findings with { severity: 'error' | 'warn', code, file, line?, message, rule? }. The umbrella aggregates findings across enabled facets, formats them, and computes exit code:

3. Single policy file: .aidokit/policy.json

Emitted at init across all three adapters, byte-identical fixture. Carries per-facet enable/severity/allowlist settings:

{
  "specVersion": "2.0",
  "facets": {
    "secrets": { "enabled": true, "severity": "error", "allowPaths": [] },
    "budget":  { "enabled": false, "severity": "warn", "limits": {} },
    "deps":    { "enabled": false, "severity": "warn" },
    "license": { "enabled": false, "severity": "warn", "allowed": ["MIT","Apache-2.0","BSD-*"] },
    "loop-cap":{ "enabled": false, "severity": "warn", "maxIterations": 50 }
  }
}

4. Cross-adapter, CLI-owned

All facet logic lives in @aidokit/core/verify and the aidokit CLI. No adapter package is touched. .aidokit/policy.json is part of the cross-adapter .aidokit/ directory (alongside state.json, capabilities.json, model.lock) — byte-identical across the three adapter fixtures.

5. Stub facets are real entries, not silent no-ops

budget, deps, license, and loop-cap are registered in the facet runner. Invoking them emits a FACET_NOT_IMPLEMENTED warn finding with a link to the tracking issue, so users get an explicit signal rather than a silent success. The reserved CLI flags document forward intent and let early-adopters pin their policy.json against the eventual schema without breakage.


Consequences

Positive

Negative

Neutral


Alternatives considered

Alternative 1: Five top-level commands

aidokit secrets, aidokit budget, aidokit deps, aidokit license, aidokit loop-cap as siblings of aidokit verify.

Pros

Cons

Reason rejected: the surface-multiplication cost outweighs the discoverability win, especially given the shared infrastructure.

Alternative 2: Two commands split by failure-class

aidokit scan for static checks (secrets, license, deps) and aidokit limit for budget / loop-cap (runtime-quasi-static).

Pros

Cons

Reason rejected: the boundary is not crisp enough to justify the extra command.

Alternative 3: Push facets into aidokit doctor

Re-use the existing doctor command as the scan surface.

Pros

Cons

Reason rejected: ownership semantics differ — keep doctor for install health, verify for project gates.


Implementation notes


Validation

This decision is validated when:


References

Internal

External