Skip to content

opencomplai gaps

Print a gap report for every target framework (the EU AI Act by default): for every tracked requirement, whether it is Met, Partial, Missing, or Unverified, and which subsystem produced that status (rule, obligation, scan, evaluator, or artifact probe).

What: a readable coverage board for humans and CI dashboards.

When: after you have a manifest (and optionally a scan report / sample set).

Don't: treat heuristic/partial rows as legal certification. Statuses include honesty labels (heuristic_estimate, not_assessed, measured).

gaps is informational only and never gates CI; the exit-code contract stays with opencomplai check. A framework that opencomplai.yaml gates (see check) says so under its table.

Usage

Bash
opencomplai gaps --manifest system-manifest.json
PowerShell
opencomplai gaps --manifest system-manifest.json

Options

Flag Default Description
--manifest / -m system-manifest.json System manifest path
--commit-ref HEAD Commit reference for provenance
--scan-report (none) Path to a CorroborationReport JSON from a prior opencomplai scan --output json — resolves scan-sourced article rows
--sample-set (none) Path to an EvalSampleSet JSON — resolves evaluator-sourced article rows (safety, bias, data-leakage)
--repo-root . Repo root for artifact path probes (Arts. 9/13/14/16/24/43)
--target manifest Framework to assess (EU_AI_ACT, NIST_AI_RMF, ISO_IEC_42001); repeat for several. Replaces the manifest's compliance_targets, else its compliance_target. More than one target prints a table per framework and adds a frameworks block to the JSON. ISO_IEC_42001 is the native pack, attestation-led (partial, unreviewed): most rows read Unverified until an attestation is recorded under framework_inputs. See Frameworks
--map-to (none) DORA or EBA; repeat for both. Adds a mapped-only citation column per regime to the EU AI Act table and a mapped_regimes block to the JSON. A citation, not a verdict; low confidence, needs founder review. See Mapped-only regimes
--strict off Exit 2 on unknown top-level keys in the manifest instead of warning and ignoring them
--sort article Row order of the human tables: article, or priority (rows that are not Met first, then earliest known deadline, worst status, smallest effort). With --output json, priority also adds a backlog list of the open rows, ranked; articles stay in article order. A NIST-only run keeps subcategory order. The effort is an engineering estimate, not a compliance claim
--output / -o human human or json (JSON is wrapped in a versioned envelope — not a signed artifact)

Understanding gap statuses

Status Meaning
MET The mapped rule passed, or a mapped scan finding falls inside the declared Annex III areas, or the mapped evaluator passed. A detection of agent frameworks, MCP servers or PII dataflow is never MET on its own.
PARTIAL A mapped evaluator returned a warn outcome.
MISSING A mapped rule failed, a scan finding in a mapped signal category maps to an Annex III area the declared purpose does not cover (a scan discrepancy), or a mapped evaluator failed.
UNVERIFIED No automated source was run for this article in this invocation — not the same as "failing." An obligation-only article (e.g. Art. 11, Art. 12) is always UNVERIFIED, since Opencomplai has no automated check for it; a rule/scan/evaluator-backed article is UNVERIFIED only when you didn't supply the input needed to resolve it (--scan-report and/or --sample-set). A scan-backed row with a detection but no verdict is also UNVERIFIED ("detected, no compliance verdict"): the scan found the signal, but a detection is not a compliance verdict.

This distinction is deliberate, not a limitation to work around silently: the rule engine alone cannot see everything. An article whose only mapped source is a pipeline evaluator (e.g. Art. 15's EVAL_SAFETY_LEXICAL_V1) will read UNVERIFIED — not MISSING — until you pass --sample-set. Passing more inputs turns more rows from UNVERIFIED into a real MET/PARTIAL/MISSING verdict; it never turns a real MISSING back into UNVERIFIED.

Example: rule-sourced and evaluator-sourced rows together

Given a manifest with intended_purpose: "recruitment screening of candidates" (an Annex III high-risk use case) and a --sample-set whose outputs contain an adversarial-prompt fixture that fails EVAL_SAFETY_LEXICAL_V1:

Bash
opencomplai gaps --manifest system-manifest.json --sample-set eval-set.json --output json
PowerShell
opencomplai gaps --manifest system-manifest.json --sample-set eval-set.json --output json
JSON
{
  "system_id": "hiring",
  "commit_ref": "HEAD",
  "generated_at": "2026-07-12T10:00:00+00:00",
  "articles": [
    {
      "article": "Art. 6",
      "status": "missing",
      "source": "rule",
      "evidence_ref": "EU_AIA_ART6_HIGH_RISK",
      "rationale": "Intended purpose matches Annex III employment category."
    },
    {
      "article": "Art. 15",
      "status": "missing",
      "source": "evaluator",
      "evidence_ref": "sha256:...",
      "rationale": "EVAL_SAFETY_LEXICAL_V1 (NIST AI RMF MEASURE 2.6 / EU AI Act Art. 15 (robustness)): fail."
    },
    {
      "article": "Art. 11",
      "status": "unverified",
      "source": "obligation",
      "evidence_ref": "none",
      "rationale": "No source data supplied for this article in this run."
    }
  ],
  "evidence_hashes": [],
  "principle_summary": { "principles": [ "..." ] }
}

The Art. 6 row is rule-sourced — it comes from the static rule engine and needs no extra input. The Art. 15 row is evaluator-sourced — it only resolves to a real verdict (here, MISSING) because --sample-set was supplied; run gaps without --sample-set against the same manifest and Art. 15 reads UNVERIFIED instead. This is the mechanism behind [1.5 in the roadmap]: --sample-set affects gaps output the same way it affects check --sample-set.

Principle Summary

Every gaps invocation (human or JSON) also rolls the per-article statuses up into the 6 EU Trustworthy AI principles (Technical Robustness & Safety, Privacy & Data Governance, Transparency, Diversity/Non-discrimination/Fairness, Societal & Environmental Wellbeing, Accountability). Each principle shows the worst-case status across its mapped articles — the same "MISSING beats PARTIAL beats UNVERIFIED beats MET" convention used for individual article rows.

Human output:

Text Only
Principle Summary

Principle                                  Status       Articles
Technical Robustness and Safety            MISSING      Art. 15, Art. 25
Privacy and Data Governance                UNVERIFIED   Art. 10
Transparency                               UNVERIFIED   Art. 12, Art. 50
Diversity, Non-discrimination and Fairness MISSING      Art. 5, Art. 6, Art. 10
Societal and Environmental Wellbeing       UNVERIFIED   Art. 5
Accountability                             UNVERIFIED   Art. 4, Art. 11, Art. 53, Art. 55

See EU AI Act Principles for the full article-to-principle mapping and which rule/obligation/scan/evaluator backs each article.

principle_summary is an additive, optional field on GapReport — existing gaps --output json consumers that don't read it are unaffected by its presence.

Several frameworks

With exactly one target, EU_AI_ACT or NIST_AI_RMF, gaps prints and returns what it always has: the EU AI Act table above, or the NIST AI RMF subcategory table (see NIST AI RMF). With any other set, from the manifest's compliance_targets or from repeated --target, it prints one table per framework in target order. A framework other than the EU AI Act gets a table of its own, with prefixed requirement ids (NIST_AI_RMF:GOVERN 1.1), the requirements excluded in the manifest's framework_inputs and their reasons, a note when opencomplai.yaml gates it, and the framework-neutral disclaimer.

Bash
opencomplai gaps --target EU_AI_ACT --target NIST_AI_RMF --output json
PowerShell
opencomplai gaps --target EU_AI_ACT --target NIST_AI_RMF --output json

The JSON payload keeps the EU AI Act report at its top level (and nist_rmf_report when NIST_AI_RMF is a target) and adds a frameworks block, one framework report per target:

JSON
{
  "system_id": "loan-decision-model",
  "articles": ["... the EU AI Act rows, as above ..."],
  "nist_rmf_report": {"subcategories": ["..."]},
  "frameworks": {
    "EU_AI_ACT": {"framework": "EU_AI_ACT", "derived_from": null, "report": {"...": "..."}},
    "NIST_AI_RMF": {
      "framework": "NIST_AI_RMF",
      "label": "NIST AI RMF 1.0 (derived from EU AI Act evidence)",
      "data_version": "9513768b2196",
      "derived_from": "EU_AI_ACT",
      "disclaimer_ref": "DISCLAIMER_V2",
      "gated": false,
      "excluded": {"NIST_AI_RMF:MAP 1.1": "Internal tool with no external users"},
      "report": {
        "articles": [
          {
            "article": "NIST_AI_RMF:GOVERN 1.1",
            "status": "missing",
            "source": "crosswalk",
            "evidence_ref": "Art. 5, Art. 9, Art. 11, Art. 16, Art. 17",
            "confidence_label": "heuristic_estimate",
            "disclaimer_ref": "DISCLAIMER_V2"
          }
        ]
      }
    }
  }
}

The envelope carries the framework-neutral disclaimer for such a run. An unknown target, a framework_inputs key that is not a known framework, or a framework_inputs entry that names an id the framework does not have, exits 2. See Frameworks for exclusions and attestations.

Next step

Run opencomplai recommend to generate copy-paste remediation templates for every MISSING/PARTIAL row in the gap report — see recommend.