# 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:

  1. Check the issue tracker (opens new window) for an existing issue before opening a new one.
  2. 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 in examples/, 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.