How to Add a New Service-Lint Rule
This guide walks you through creating, documenting, registering, and wiring a new Service-Lint rule so it appears in the docs and is executed by the engine.
Overview
To add a rule, you will:
-
Create a new class that implements
gov.va.mobile.tools.servicelint.rule.Rule. -
Annotate the rule with documentation metadata and provide good/bad examples.
-
Register the rule via Java Service Provider Interface (SPI) in
META-INF/services/gov.va.mobile.tools.servicelint.rule.Rule. -
Ensure the rule is included in
DefaultRuleEngineProvider(if applicable in your version) or is discoverable by the engine. -
Add a rule documentation page (SLxxx.adoc) if your repo uses individual pages per rule.
1) Create a Rule Class
Create your rule in the service-lint-rules module under an appropriate package, for example:
package gov.va.mobile.tools.servicelint.rules;
import gov.va.mobile.tools.servicelint.model.*; // Adjust as needed for your project
import gov.va.mobile.tools.servicelint.rule.Rule;
import gov.va.mobile.tools.servicelint.rule.RuleContext;
import gov.va.mobile.tools.servicelint.rule.Severity; // if applicable
/**
* Example rule that validates something in a repo.
*/
public class SL999ExampleRule implements Rule {
@Override
public String getId() {
return "SL999"; // Unique ID following the SLxxx pattern
}
@Override
public String getTitle() {
return "Example Validation"; // Short title used in lists
}
@Override
public String getDescription() {
return "Explains what the rule checks and why it matters.";
}
@Override
public Severity getSeverity() {
return Severity.INFO; // Or WARNING/ERROR depending on impact
}
@Override
public RuleResult evaluate(RuleContext ctx) {
// Implement your logic; return pass/fail with messages & remediation
boolean ok = true; // replace with actual check
if (ok) {
return RuleResult.pass(getId());
} else {
return RuleResult.fail(getId(), "Found an issue", "How to fix it");
}
}
}
Tips:
-
Keep logic deterministic and fast; avoid network calls where possible.
-
Prefer small, focused rules.
-
Use existing utilities from the project if available (path scanning, YAML/JSON helpers, Maven POM helpers, etc.).
2) Add Annotations and Inline Docs
Many rules embed documentation in annotations to auto-generate docs. If your project has such annotations, add them to your class.
For example (adjust annotation names to match your project):
@RuleDoc(
id = "SL999",
title = "Example Validation",
severity = Severity.INFO,
since = "1.0.7",
summary = "Validates example condition.",
rationale = "Explain why this is important.",
remediation = "Explain how to resolve violations."
)
@RuleExamples(
good = {
@Example(title = "Good: compliant file", code = """
# some.yaml
key: expected
"""),
},
bad = {
@Example(title = "Bad: non-compliant file", code = """
# some.yaml
key: unexpected
""")
}
)
public class SL999ExampleRule implements Rule { /* ... */ }
Notes:
-
If your codebase doesn’t have these annotations yet, you can skip them, but prefer adding JavaDoc on methods to describe behavior and examples.
-
Use triple-quoted strings or escaping for multi-line example snippets. If your Java version doesn’t support text blocks, store examples as resource files and reference them in documentation.
3) Provide Good/Bad Examples
Good/Bad examples help users quickly understand violations.
Options:
-
Inline via annotations (shown above).
-
As resource files under
service-lint-rules/src/test/resources/rules/SL999/and reference them from your docs.
Example file layout:
service-lint-rules/
src/test/resources/rules/SL999/
good-example-1.yaml
bad-example-1.yaml
4) Register the Rule with Java SPI
Add your fully-qualified rule class name to the service provider file so the engine can find it via ServiceLoader.
File: service-lint-rules/src/main/resources/META-INF/services/gov.va.mobile.tools.servicelint.rule.Rule
Append a new line with your class name:
# Existing rules...
gov.va.mobile.tools.servicelint.rules.SL999ExampleRule
Guidelines:
-
One class per line, no trailing spaces.
-
Keep the list sorted by ID if that’s the existing convention.
5) Ensure the Rule is Wired into the Engine
Depending on your version, the engine may rely on ServiceLoader or hard-coded registration in DefaultRuleEngineProvider.
-
If ServiceLoader is used, updating the SPI file may be sufficient.
-
If rules are explicitly added in
DefaultRuleEngineProvider, add your rule there.
Example (simplified):
package gov.va.mobile.tools.servicelint.engine;
import gov.va.mobile.tools.servicelint.rule.Rule;
import gov.va.mobile.tools.servicelint.rules.SL999ExampleRule;
public class DefaultRuleEngineProvider implements RuleEngineProvider {
@Override
public RuleEngine getRuleEngine() {
RuleEngine engine = new RuleEngine();
// If not using ServiceLoader, register explicitly:
engine.register(new SL999ExampleRule());
return engine;
}
}
Check your current implementation of DefaultRuleEngineProvider and follow the prevailing pattern.
6) Add a Documentation Page for the Rule (SLxxx)
If the project keeps one page per rule (e.g., SL009.adoc, SL010.adoc exist), add a page for your new rule, such as SL999.adoc.
File: service-lint/docs/modules/ROOT/pages/SL999.adoc
= SL999: Example Validation
:description: Validates example condition.
:severity: INFO
:since: 1.0.7
== Summary
Validates example condition across the repository.
== Rationale
Explain why the rule exists.
== Good example
[source,yaml]
key: expected
== Bad example
[source,yaml]
key: unexpected
== Remediation
Explain how to resolve the violation and provide tips.
Finally, add the page to the navigation if the project maintains a manual nav list (see next step).
7) Update the Docs Navigation
Update service-lint/docs/modules/ROOT/nav.adoc to include your new rule page and/or this guide.
-
To add this guide to the navigation, add a new entry, for example under Rules or a new Contributing section:
* xref:index.adoc[Rules]
** xref:SL001.adoc[SL001: Service Lint Version Up-to-date]
...
* xref:adding-a-new-rule.adoc[How to Add a New Rule]
-
To add your specific rule page, add an item to the Rules list in
nav.adocin the correct numerical order.
8) Test Locally
-
Run any available docs generation to ensure the new pages render correctly (Antora or build tooling).
-
Execute service-lint in a test repository to verify the rule loads (check logs for SL999). If the engine uses
ServiceLoader, ensure the SPI file contains your class and is packaged correctly.