Gaps produces a field-level coverage report. For each
(asset_type, property_path) the catalog declares — i.e., paths
the predicate AST walker discovers from controls that carry
applicable_asset_types — it reports:
- missing_count / total_count how many observed assets of
the type lack the property
- controls_blocked which controls would fire if
the property were populated
- chains_blocked chain count via member-control
inheritance (deduplicated)
- max_severity highest severity among readers
- is_intent_property tags + role-type label paths
sort to the top of the plan
- remediation.type "tag" (seconds-per-asset) or
"collector" (code change)
- remediation.command CLI template (tag) or doc
pointer (collector)
Distinct from stave readiness, which reports asset-type-level
coverage ("did the collector emit any IAM roles at all?"). Gaps
goes one level deeper ("of the 22 buckets you emitted, 19 lack
data_classification, and that's blocking 98 chains").
Inputs:
--observations DIR Observation snapshot directory
--controls DIR Control catalog (default: controls)
--chains DIR Chain catalog (default: chains)
--format FORMAT Output: text (default) | json
--top N Action plan entries (default: 5)
Outputs:
stdout The gap report
stderr Loader diagnostics
Exit Codes:
0 Report produced
2 Input error
4 Internal error
130 SIGINT
Caveats:
- Controls without applicable_asset_types declarations cannot
surface gaps here. Their count appears in the summary so the
operator knows the report is partial; run 'stave readiness'
to see asset-type-level coverage.
- Intent properties are currently a hardcoded canonical set
(data_classification, role-type, environment for storage and
compute). A future commit reads them from
internal/controldata/taxonomy/intent_properties.yaml.
- Per-asset-ID listings are not emitted by default. The
aggregate counts ("19 of 22 buckets") are the scannable
format; a future --verbose flag will expand them.
Usage:
stave gaps [flags]
Examples:
# Top 5 field-level gaps against an observation directory
stave gaps --observations ./my-snapshot
# Machine-readable for CI or tooling
stave gaps --observations ./my-snapshot --format json
# Widen the surfaced gap list to the top 10 by unlock value
stave gaps --observations ./my-snapshot --top 10
Flags:
--chains string chain catalog directory (default "chains")
-i, --controls string control catalog directory (default "controls")
-f, --format string output format: text | json (default "text")
-h, --help help for gaps
-o, --observations string observation snapshot directory (required)
--quiet suppress output (exit code only)
--top int number of top gaps to emphasise in the summary (default 5)
Global Flags:
--allow-symlink-output Allow writing output through symlinks (default: refuse)
--force Allow overwriting existing output files
--log-file string Write logs to file (default: stderr)
--log-format string Log format: text|json (default "text")
--log-level string Log level: debug|info|warn|error (overrides -v)
--log-timestamps Include timestamps in logs (breaks determinism)
--log-timings Include timing information (breaks determinism)
--no-color Disable ANSI colors in output
--path-mode string Path rendering in errors/logs: base (basename only) or full (absolute paths) Resolved default may come from STAVE_* env vars, stave.yaml, user config, or built-in.
--require-offline Assert offline operation: fail if proxy env vars (HTTP_PROXY, HTTPS_PROXY, ALL_PROXY) are set
--sanitize Sanitize infrastructure identifiers (bucket names, ARNs, policies) from output Resolved default may come from STAVE_* env vars, stave.yaml, user config, or built-in.
--strict Enable strict integrity checks for embedded registries and references
-v, --verbose count Increase verbosity (-v=INFO, -vv=DEBUG)
-y, --yes Auto-confirm all interactive prompts (distinct from --force which controls file overwriting)