vacuum v0.30.1 was published on 26 August 2026. vacuum docs can now attach family specific lint diagnostics when a catalog mixes OpenAPI HTTP contracts and AsyncAPI event contracts. --openapi-ruleset and --asyncapi-ruleset select those sets instead of forcing one ruleset onto both families.
The full release notes and downloads are on the GitHub release page.
Family rulesets for mixed OpenAPI and AsyncAPI catalogs ¶
vacuum docs already ran lint while it generated HTML. A catalog that holds HTTP OpenAPI files next to AsyncAPI event contracts used to share one ruleset path. OpenAPI only rules could show up on event pages. AsyncAPI contact rules could show up on HTTP pages. That is noise in a CI gate that treats generated diagnostics as a quality check.
Commit d2165e5 adds --openapi-ruleset and --asyncapi-ruleset on vacuum docs. Each spec family loads its own ruleset. A custom OpenAPI rule stays in the HTTP diagnostics payload. AsyncAPI rules such as AsyncAPIInfoContact stay on the event side. The generated artifact is still data/pages/diagnostics.js under each spec tree.
--ruleset still works. When it is set, that single file is the legacy path for every spec in the run. Teams that already pin one Spectral compatible ruleset do not have to split it on day one.
Catalog grouping still comes from --docs-config. The tests for this change use a printing press config with grouping.serviceIdentity.metadataPointers set to /info/x-owner/service and contract roles for http-api and published-events. --no-diagnostics still turns the lint pass off.
Built in ruleset fingerprints for docs cache identity ¶
Commit bfe78c3 is a follow up, not a new command. Built in family rulesets now load once per docs run and join the diagnostics fingerprint.
The fingerprint payload already tracked custom OpenAPI and AsyncAPI ruleset identities. It now also records openapiFallbackRuleset and asyncapiFallbackRuleset when those built in sets are in play. If the default OpenAPI set changes, the cache key changes. Same for AsyncAPI. Reloading the same family returns the same ruleset pointer instead of building a second copy.
That matters when vacuum docs walks a large tree in CI. Diagnostics output should not reuse a cache entry from the other family. It should not rebuild the default set for every file either. The load once test covers both OpenAPI and AsyncAPI defaults in one context.
The same tag pins github.com/pb33f/doctor from v0.0.79 to v0.0.80 in go.mod, commit 488a197. The notes call that a chore. There is no doctor changelog in this release text, so treat it as a dependency pin, not a vacuum feature.
Concurrent lint resource release ¶
Rule timeouts used to race with resource cleanup. Lint rules execute in goroutines with a timeout. When a rule overran that timeout, vacuum used to release owned documents, indexes, and the doctor document while the goroutine could still be inside RunRule. Commit b91b97f closes that race.
RuleSetExecution now keeps a sync.WaitGroup and an activeRuleWorkers counter. executeRuleContext increments both before runRule and decrements them in a defer. RuleSetExecutionResult.release starts once via sync.Once. If workers are still live, finishRelease continues in the background after they exit. Callers of Release() and ReleaseOwnedResources() no longer free the spec tree under a timed out rule.
The global catch remains. Release() still calls libopenapi.ClearAllCaches() when asked to clear caches. That reset is process wide. Embedding vacuum in a long lived service that lints several specs at once can still have one result clear caches used by the others. The race on owned documents is the part this tag closes.
Code lives in motor/rule_applicator.go and motor/rule_runner.go. No CLI flag changed for this path. Jobs that call vacuum lint and library users that call ApplyRulesToRuleSet pick it up by moving to v0.30.1.