| name | vale-linting |
| description | Run, debug, and maintain Vale style linting for InfluxData documentation: run Vale, interpret alerts, manage vocabulary, configure product-specific .vale.ini files, and understand which rules are enabled and why. Use when running Vale, investigating or fixing Vale warnings, adding accept/ignore vocabulary terms, or setting up a product config. To author custom rule patterns and regex, see vale-rule-config. |
Vale Style Linting Workflow
Purpose
This skill covers the complete Vale linting workflow for InfluxData documentation, including running Vale, understanding the rule configuration, adding vocabulary terms, and creating custom rules.
Use this skill when:
- Running Vale style checks on documentation
- Debugging Vale warnings or errors
- Adding terms to the vocabulary (accept/ignore lists)
- Creating or modifying custom Vale rules
- Understanding why certain patterns are flagged
Quick Reference
.ci/vale/vale.sh --config=.vale.ini content/influxdb3/core/**/*.md
.ci/vale/vale.sh --config=.vale.ini --minAlertLevel=warning content/path/
.ci/vale/vale.sh sync
.ci/vale/vale.sh ls-config
Part 1: How Vale Runs
Execution via .ci/vale/vale.sh
The wrapper script .ci/vale/vale.sh runs Vale using:
- Local binary (preferred) — if
vale is installed and version >= 3.x
- Docker fallback —
jdkato/vale:v${VALE_VERSION} (pinned version)
.ci/vale/vale.sh --config=.vale.ini content/path/
Critical limitation: Only files inside the repository are accessible when using Docker fallback. Files in /tmp or other external paths will silently fail.
macOS note: The CI script .github/scripts/vale-check.sh uses declare -A (associative arrays) which requires bash 4+. macOS ships bash 3.2. Use /opt/homebrew/bin/bash or run tests in CI instead.
Configuration Files
| File | Purpose |
|---|
.vale.ini | Main configuration (styles, packages, rule toggles) |
.ci/vale/styles/InfluxDataDocs/ | Custom rules for InfluxData docs |
.ci/vale/styles/config/vocabularies/InfluxDataDocs/ | Vocabulary (accept/reject terms) |
content/*/.vale.ini | Product-specific overrides |
Part 2: Understanding Vale Rules
Rule Sources
Vale uses multiple rule sources, configured in .vale.ini:
BasedOnStyles = Vale, InfluxDataDocs, Google, write-good
- Vale - Built-in rules (Spelling, Terms, Repetition)
- InfluxDataDocs - Custom rules for this repo
- Google - Google Developer Documentation Style Guide
- write-good - Plain English suggestions
Disabled Rules (and Why)
Rules are disabled in two categories across .vale.ini and all product configs:
Mechanical rules disabled (replaced by custom equivalents or incompatible with InfluxDB syntax):
| Rule | Reason |
|---|
Google.Acronyms | Custom InfluxDataDocs.Acronyms handles this |
Google.DateFormat | Custom InfluxDataDocs.DateFormat handles this |
Google.Ellipses | Custom InfluxDataDocs.Ellipses handles this |
Google.Headings | Too strict for technical doc headings |
Google.WordList | Custom InfluxDataDocs.WordList handles this |
Google.Units | Flags InfluxDB duration literals (30d, 24h); custom InfluxDataDocs.Units checks byte units only |
Vale.Spelling | Custom InfluxDataDocs.Spelling handles this |
Vale.Terms | False positives from URLs, file paths, and code |
Style rules disabled (high false-positive rate in technical docs):
| Rule | Reason |
|---|
Google.Contractions | Not relevant to InfluxData style |
Google.FirstPerson | Tutorials use "I" intentionally |
Google.Passive | Technical docs use passive voice legitimately |
Google.We | "We recommend" is standard in docs |
Google.Will | Future tense is standard in docs |
write-good.Cliches | High false positive rate |
write-good.Passive | Duplicate of Google.Passive concern |
write-good.So | Starting with "So" is fine |
write-good.ThereIs | Often the clearest phrasing |
write-good.TooWordy | Flags legitimate terms: aggregate, expiration, multiple |
write-good.Weasel | Context-dependent, better handled during content review |
Active Custom Rules
| Rule | Purpose |
|---|
InfluxDataDocs.Spelling | Spell checking with technical term exclusions |
InfluxDataDocs.Units | Byte units only (allows duration literals) |
InfluxDataDocs.WordList | Terminology standards (admin → administrator) |
InfluxDataDocs.Capitalization | Heading case rules |
InfluxDataDocs.Branding | Product name consistency |
Part 3: Adding Vocabulary Terms
Accept List (Technical Terms)
Add terms to .ci/vale/styles/config/vocabularies/InfluxDataDocs/accept.txt:
# Case-insensitive by default
CPUs
systemd
preconfigured
# Regex patterns for variations
[Dd]ownsampl(e|ed|es|ing)
subprocess(es)?
# Exact case matching (regex)
(?i)InfluxQL
Ignore List (Spelling Only)
Add terms to .ci/vale/styles/InfluxDataDocs/Terms/ignore.txt:
# Simple words (case-insensitive)
cgroup
humantime
deadman
# The ignore.txt is referenced by Spelling.yml
Key difference in this repo:
accept.txt - Terms that are part of the shared spelling vocabulary (via Vocab = InfluxDataDocs). In this repository, it does not currently create substitution rules because Vale.Terms is disabled in .vale.ini. If Vale.Terms is enabled in the future, these terms may also drive substitution behavior.
ignore.txt - Additional terms to skip in spell checking only (an ignore list layered on top of the shared vocabulary; no substitution rules).
When to Use Each
| Scenario | File |
|---|
| Technical term that's spelled correctly | ignore.txt |
| Preferred capitalization (API, CLI) | accept.txt |
| Product name with specific casing | accept.txt |
| Variable name appearing in prose | ignore.txt |
Part 4: Creating Custom Rules
This section covers where rules live in this repo and shows two real
examples. For the rule-authoring deep dive — rule types, the regexp2 engine,
PCRE lookarounds, and testing patterns in isolation — use the
vale-rule-config skill.
Rule File Location
Custom rules go in .ci/vale/styles/InfluxDataDocs/:
.ci/vale/styles/InfluxDataDocs/
├── Acronyms.yml
├── Branding.yml
├── Capitalization.yml
├── Ellipses.yml
├── Spelling.yml
├── Units.yml # Custom: allows duration literals
├── WordList.yml
└── Terms/
├── ignore.txt
└── query-functions.txt
Example: Custom Units Rule
This rule validates byte units while allowing InfluxDB duration literals:
extends: existence
message: "Put a nonbreaking space between the number and the unit in '%s'."
link: "https://developers.google.com/style/units-of-measure"
nonword: true
level: warning
tokens:
- \b\d+(?:B|kB|MB|GB|TB|PB)
Example: Spelling Rule with Scope Exclusions
extends: spelling
message: "Did you really mean '%s'?"
level: warning
scope:
- ~code
- ~raw
- ~table.header
- ~table.cell
ignore:
- InfluxDataDocs/Terms/ignore.txt
- InfluxDataDocs/Terms/query-functions.txt
filters:
- '/[a-zA-Z0-9/_\-\.\{\}]+'
- 'https?://[^\s\)\]>"]+'
Part 5: Debugging Vale Issues
Common Problems
"0 errors in stdin"
- File is outside the repository (Docker can't access it)
- Solution: Use files inside the repo for testing
Rule not triggering
- Check if rule is disabled in
.vale.ini
- Verify rule file has valid YAML syntax
- Run
vale sync after adding new packages
False positives in URLs/code
- Add patterns to
TokenIgnores in .vale.ini
- Add scope exclusions (
~code, ~raw) to the rule
- Add terms to
ignore.txt
Debugging Commands
.ci/vale/vale.sh ls-config
node -e "require('js-yaml').load(require('fs').readFileSync('path/to/rule.yml'))"
.ci/vale/vale.sh --config=.vale.ini --minAlertLevel=suggestion path/to/file.md
Part 6: Vale Cannot Inspect URLs
TokenIgnores in .vale.ini strips all URLs before any rules run:
TokenIgnores = https?://[^\s\)\]>"]+
This means no Vale rule can match URL content. An earlier attempt to create a SupportLink.yml rule to validate support.influxdata.com URL patterns failed for this reason — the URLs were stripped before the rule could see them. Support URL validation uses a separate shell script (.ci/scripts/check-support-links.sh) instead.
Keep this in mind when designing rules: if the pattern to match is inside a URL, use a shell script or pre-commit hook, not a Vale rule.
Part 7: TokenIgnores vs Rule Filters
TokenIgnores (in .vale.ini)
Applied globally to all rules. Matches whole tokens:
TokenIgnores = /[a-zA-Z0-9/_\-\.]+, \
https?://[^\s\)\]>"]+, \
`[^`]+`
Limitation: Cannot match substrings within words. If "api" appears in /api/v3/write, the URL pattern must match the entire URL to exclude it.
Rule Filters (in rule YAML)
Applied to specific rules. Can match patterns within text:
filters:
- '[Ss]erverless'
- '/[a-zA-Z0-9/_\-\.\{\}]+'
When to Use Each
| Use Case | Approach |
|---|
| Exclude entire URLs from all rules | TokenIgnores |
| Exclude inline code from all rules | TokenIgnores with backtick pattern |
| Exclude patterns from one rule | Rule-specific filters |
| Skip checking inside code blocks | Rule scope: [~code] |
Part 7: Workflow Examples
Adding a New Technical Term
grep -r "systemd" content/
echo "systemd" >> .ci/vale/styles/InfluxDataDocs/Terms/ignore.txt
echo "systemd" >> .ci/vale/styles/config/vocabularies/InfluxDataDocs/accept.txt
.ci/vale/vale.sh --config=.vale.ini content/path/with/term.md
Creating a Product-Specific Override
[!Important]
Product-specific .vale.ini files must include the same disabled rules as the
root .vale.ini. Rules disabled in the root config are not inherited by
product-specific configs. Omitting them re-enables the rules for those products.
For example, omitting Google.Units = NO causes duration literals like 7d,
24h to be flagged as errors in product-specific linting runs.
cat > content/influxdb3/cloud-dedicated/.vale.ini << 'EOF'
StylesPath = ../../../.ci/vale/styles
MinAlertLevel = warning
Vocab = InfluxDataDocs
Packages = Google, write-good, Hugo
[*.md]
BasedOnStyles = Vale, InfluxDataDocs, Cloud-Dedicated, Google, write-good
Google.Acronyms = NO
Google.DateFormat = NO
Google.Ellipses = NO
Google.Headings = NO
Google.WordList = NO
Google.Units = NO
Vale.Spelling = NO
Vale.Terms = NO
Google.Contractions = NO
Google.FirstPerson = NO
Google.Passive = NO
Google.We = NO
Google.Will = NO
write-good.Cliches = NO
write-good.Passive = NO
write-good.So = NO
write-good.ThereIs = NO
write-good.TooWordy = NO
write-good.Weasel = NO
TokenIgnores = /[a-zA-Z0-9/_\-\.]+, \
https?://[^\s\)\]>"]+, \
`[^`]+`
EOF
# 2. Run Vale with product config
.ci/vale/vale.sh --config=content/influxdb3/cloud-dedicated/.vale.ini \
content/influxdb3/cloud-dedicated/**/*.md
Debugging Why a Pattern is Flagged
.ci/vale/vale.sh --config=.vale.ini path/to/file.md
cat .ci/vale/styles/InfluxDataDocs/WordList.yml
grep -i "term" .ci/vale/styles/config/vocabularies/InfluxDataDocs/*.txt
grep TokenIgnores .vale.ini
Checklist: Before Committing Vale Changes
Related Files
| File | Purpose |
|---|
.vale.ini | Main configuration |
.vale-instructions.ini | Config for non-content files (READMEs, AGENTS.md, etc.) |
.ci/vale/vale.sh | Vale wrapper (local binary or Docker fallback) |
.ci/vale/styles/ | All Vale style rules |
.ci/scripts/check-support-links.sh | Support URL validation (can't use Vale — see Part 6) |
.github/scripts/vale-check.sh | CI script: groups files by product config, runs Vale |
.github/scripts/resolve-shared-content.sh | CI script: resolves content/shared/* to product pages |
.github/workflows/pr-vale-check.yml | CI workflow: runs Vale on PR changes |
lefthook.yml | Pre-commit hooks that run Vale |
DOCS-TESTING.md | Testing documentation (includes Vale CI section) |