# Roadmap to 1.x parity
Direction, not a guarantee
This page describes the intended direction for reaching parity between the 0.x
checks: format and the 1.x pipeline. Scope and sequencing may change as
priorities shift and contributors engage.
- 1.x is already recommended for production. See Config versions.
- 0.x is fully supported while this work progresses. Nothing will break.
- The current recipe catalogue is on the gaps matrix.
# The direction: recipes, not plugin ports
1.x reaches parity by documenting composition recipes, not by building a
dedicated plugin for each 0.x check. Almost every 0.x check is already
reproducible today by wiring together general-purpose collect and analyse
plugins — see the gaps matrix for the plugin chain per check.
So most of the remaining work is writing docs and examples, not writing code. Building a new plugin is the exception, reserved for the few checks where no existing building block can collect the required data.
# What "parity" means
Parity is reached when every 0.x check has a documented, tested recipe composed
from general-purpose plugins — and, where the 0.x check supported it, a
remediation path. A recipe counts as complete when it is
documented in the reference and backed by a working file in examples/.
The gaps matrix is the authoritative record of current status.
# Current status: no capability gaps remain
All 19 registered 0.x checks have a documented recipe and a working example in
examples/. The last gap, yamllint, closed with the yaml:lint fact
plugin: it treats a YAML parse failure as analysable data rather than a collect
error, so the pipeline reaches the analyse stage instead of aborting at
log.Fatal("failed to collect facts") (pkg/shipshape/shipshape.go:171-174).
See Recipe: yamllint.
The capabilities that were previously deferred here have all landed:
| 0.x check | Capability added |
|---|---|
json | json:key fact plugin (RFC 9535 JSONPath) |
filediff | file:drift fact + drift analyser — placeholder-masking instead of template rendering |
crawler | http:crawl fact plugin |
sca:application_type | file:fingerprint fact + detected analyser |
yamllint | yaml:lint fact plugin — parse failures as data, not fatal errors |
The Drush-based Drupal checks similarly all have worked examples now. They share
the collect → assert composition pattern shown in the
Drush composition recipe: a
command fact emits data one item per line, and allowed:list / equals
makes the assertion.
Remaining work is therefore reference documentation quality, not new
plugins. Ten pages are still title-only stubs: database:search,
docker:command, docker:images, file:fingerprint, file:lookup,
file:read, file:read:multiple and yaml:key under reference/collect/,
plus docker:exec and mysql under reference/connection/.
file:lookup is the highest-value of these, since several recipes now depend on
its file-names-only field to select between emitting file names and file
contents.
# Known plugin limitations to investigate
These are defects found while validating the bundled examples against 1.x. They are tracked here so the affected examples can be simplified once the underlying plugin work lands.
Both are panics, not errors
Each item below crashes the process on operator-supplied config rather than reporting a collection error. A malformed or merely unusual config file should never panic — these are correctness bugs, not cosmetic ones.
| Area | Symptom | Direction |
|---|---|---|
yaml:key — empty sequence | A config file with an empty YAML list (e.g. permissions: []) panics with index out of range. The SequenceNode branch indexes Content[0] without a length check in four places: YamlLookup.ProcessNodes (pkg/fact/yaml/yaml.go:106, :122) and AliasNodeToData (:238, :254). | Guard every SequenceNode case against empty Content, and emit an empty list rather than panicking. All four sites need the guard, not just the one reached first. |
allowed:list — multi-file input | yaml:key output over a file:lookup (map of file → list) panics in AsMapListString (pkg/data/data.go:94) with interface conversion: map[string]interface{}, not map[string][]string, via allowedlist.go:178. When any file has an empty map the format instead becomes map-nested-string, which allowed:list does not handle. | Make allowed:list accept the actual multi-file yaml:key data shape (and map-nested-string), so a "disallowed permission across all roles" assertion can be expressed without per-role single-file reads. This is why examples/drupal-config.yml asserts permissions per-role rather than across all roles at once. |
# Candidate composability improvements (unscheduled)
These came out of a review of every bundled example. Unlike the defects above, none is a bug — each is an ergonomics improvement where the current composition works but reads more verbosely than it should. They are recorded for prioritisation and are not scheduled.
| Candidate | Pain point | Direction |
|---|---|---|
command stdout ergonomics | Nine examples repeat the bash -c + set -o pipefail + jq dance, and eight then need key: stdout to select stdout out of a map — an unintuitive extra hop. | A command option (or thin command:lines fact) emitting stdout directly as FormatListString, split per line. Highest reuse: it simplifies every Drush recipe at once. |
Collapse file:read → *:key chains | required-values.yml, regex-match.yml, yaml-lookup.yml and json-lookup.yml each wire a file:read fact whose only job is to feed yaml:key/json:key. | Let yaml:key/json:key read a file directly, collapsing two nodes into one. Note: yaml:key already uses path: for the key path within the document (pkg/fact/yaml/key.go:22), so this needs a different key (e.g. file:). |
string:transform helper | docker.yml repeats yaml:key over compose-services-nodes four times and strips image tags with a hard-to-read regex (package-match: '^(.[^:@]*)?[:@]?([^ latest$]*)'). | A small transform helper (strip suffix, split on :) so the very common image:tag split doesn't need a fragile regex. |
Named breach-format presets | domain-in-db-tables.yml needs a large inline breach-format template for readable output. webforms-tokenised-email-handlers.yml already works around the repetition with YAML anchors. | A library of named presets (e.g. key-value-table). Lowest urgency — YAML anchors already mitigate it. |
The first item is the highest-value, lowest-risk of the four and the natural next increment. The remaining three are held pending demonstrated demand.
# How to contribute
The gaps matrix is the source of truth for what remains open.
To pick up an item:
- Check the issue tracker (opens new window) for an existing issue before opening a new one.
- Reference the 0.x check type (e.g.
drupal-admin-user) in the issue title so it stays traceable to this roadmap.
An item is done when:
- Recipe items — the recipe is documented in
docs/src/reference/, a working file exists inexamples/, and the gaps matrix row is updated to Achievable now. - Capability items — a new general-purpose collect plugin is registered and documented (not a check-specific plugin), with a recipe showing how it reproduces the 0.x check.
Contributions to either group are welcome. There is no strict ordering — pick whatever is most useful to you first.