Documentation Drift Detection¶
This document describes the automated system for detecting when code changes require documentation updates. The system analyzes git diffs, identifies documentation gaps, and creates non-blocking Linear tasks for follow-up.
Overview¶
Documentation drift occurs when code changes without corresponding documentation updates. This system:
- Detects code changes that typically require documentation
- Creates detailed Linear tasks with full context
- Comments on PRs with a summary of detected drift
- Does not block development - tasks go to backlog
Why This Matters¶
- Documentation stays synchronized with code
- New developers can find accurate information
- API consumers have up-to-date references
- Knowledge doesn't get lost when features are added
Design Principles¶
| Principle | Implementation |
|---|---|
| Non-blocking | Never fails CI; creates backlog tasks |
| Detailed | Tasks include diff excerpts and context |
| Deduplicated | Won't create duplicate tasks for same drift |
| Configurable | Rules in YAML, not hardcoded |
| Integrated | Uses existing Linear workflow |
How It Works¶
The documentation drift detection system runs in three stages:
%%{init: {'theme': 'dark'}}%%
flowchart TB
PR[PR Opened/Updated]
CheckDrift["check-docs-drift.py<br/>Analyze git diff, match rules"]
Report["drift-report.json"]
CreateTasks["create-docs-tasks.py<br/>Create Linear tasks via API"]
Comment["PR Comment Posted<br/>Summary with task links"]
PR --> CheckDrift
CheckDrift --> Report
Report --> CreateTasks
CreateTasks --> Comment Stage 1: Detection¶
The check-docs-drift.py script:
- Gets list of changed files between base and head commits
- Matches each file against rules in
docs-drift-rules.yml - For matching files, checks if content patterns match (if defined)
- Determines which documentation files should exist or be updated
- Outputs a structured JSON report
Stage 2: Task Creation¶
The create-docs-tasks.py script:
- Reads the drift report JSON
- Generates a deterministic ID for each drift item (for deduplication)
- Checks if a task with that drift ID already exists in Linear
- Creates new tasks only for previously unseen drift
- Applies appropriate labels and priority
Stage 3: PR Comment¶
The GitHub Actions workflow:
- Reads the drift report
- Generates a markdown table summarizing detected drift
- Posts a comment on the PR with priority indicators
- Links are non-blocking - merging is still allowed
Configuration¶
Rules File¶
The detection rules are defined in scripts/docs-drift-rules.yml. This allows adding or modifying rules without changing Python code.
Location: scripts/docs-drift-rules.yml
Rule Format¶
Each rule has the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique identifier for the rule |
description | string | Yes | Human-readable description |
file_patterns | string[] | Yes | Glob patterns for files to watch |
content_patterns | string[] | No | Regex patterns to match within changed files |
new_file_only | boolean | No | If true, only trigger for newly created files |
priority | string | Yes | high, medium, or low |
required_docs | string[] | Yes | Documentation files that should exist/be updated |
Template Variables¶
In required_docs, you can use these template variables:
| Variable | Description | Example |
|---|---|---|
{dir} | Directory containing the changed file | backend/api/routes for a route file |
{file} | Filename without extension | cameras for cameras.py |
Example Rule¶
- id: new-api-endpoint
description: 'New API endpoint added'
file_patterns:
- 'backend/api/routes/*.py'
content_patterns:
- '@router\.(get|post|put|patch|delete)\('
priority: high
required_docs:
- '{dir}/AGENTS.md'
- 'docs/developer/api/*.md'
This rule:
- Watches all Python files in
backend/api/routes/ - Triggers when the file contains a route decorator
- Has high priority (creates urgent tasks)
- Expects AGENTS.md in the same directory and API docs
Priority Definitions¶
| Priority | Linear Priority | Use Case |
|---|---|---|
high | 2 (High) | Critical docs affecting production usage |
medium | 3 (Medium) | Important docs for developer understanding |
low | 4 (Low) | Nice-to-have documentation improvements |
Adding New Rules¶
To add a new detection rule:
- Edit
scripts/docs-drift-rules.yml - Add a new rule entry with all required fields
- Test locally (see Manual Usage)
- Commit the change
Example: Adding a rule for new middleware:
- id: new-middleware
description: 'New FastAPI middleware'
file_patterns:
- 'backend/middleware/*.py'
content_patterns:
- "class \\w+Middleware"
- 'async def dispatch'
new_file_only: true
priority: medium
required_docs:
- 'backend/middleware/AGENTS.md'
- 'docs/architecture/middleware.md'
Ignore Patterns¶
Files matching ignore patterns are never analyzed:
ignore_patterns:
- '**/test_*.py'
- '**/*.test.ts'
- '**/*.test.tsx'
- '**/conftest.py'
- 'mutants/**'
- '.venv/**'
- '**/__pycache__/**'
- '**/node_modules/**'
Handling Generated Tasks¶
When documentation drift tasks are created, follow this workflow:
Task Structure¶
Each generated task includes:
## Documentation Update Required
**Trigger:** {rule_id}
**Detected:** {timestamp} in commit `{sha}`
**PR:** #{pr_number}
### What Changed
[Diff excerpt showing the code change]
### Documentation Impact
- [ ] {required_doc_1}
- [ ] {required_doc_2}
### Suggested Updates
[Specific suggestions based on the rule]
### Acceptance Criteria
- [ ] Documentation accurately reflects implementation
- [ ] Links from hub documents work
- [ ] AGENTS.md updated if new file added
- [ ] ./scripts/validate.sh passes
Addressing Documentation Debt¶
- Claim the task - Assign to yourself in Linear, set to "In Progress"
- Review the diff - Understand what changed in the code
- Update documentation - Follow the suggested updates checklist
- Verify links - Ensure all internal links work
- Validate - Run
./scripts/validate.sh - Close the task - Set to "Done" in Linear
Batch Processing¶
If multiple drift tasks relate to the same feature:
- Create a parent task for the feature documentation
- Link the auto-generated tasks as subtasks
- Address them together in a single PR
- Use PR title:
docs: document {feature} (NEM-xxx, NEM-yyy)
Manual Usage¶
Running Detection Locally¶
# Analyze changes between main and current HEAD
uv run python scripts/check-docs-drift.py \
--output drift-report.json \
--base main \
--head HEAD
# View the report
cat drift-report.json | jq .
Creating Tasks Manually¶
# Requires LINEAR_API_KEY environment variable
export LINEAR_API_KEY="lin_api_xxxxx" # pragma: allowlist secret
uv run python scripts/create-docs-tasks.py drift-report.json
Testing Rules¶
To test a new rule without creating tasks:
# Generate report only (no Linear integration)
uv run python scripts/check-docs-drift.py \
--output test-drift.json \
--base main \
--head HEAD \
--dry-run
# Check what would be detected
cat test-drift.json | jq '.drift_items[] | {id, description, source_file}'
Checking Specific Files¶
# Test rules against specific file changes
uv run python scripts/check-docs-drift.py \
--files backend/api/routes/cameras.py backend/services/detection.py \
--output test-drift.json
CI/CD Integration¶
GitHub Workflow¶
The drift detection runs automatically via .github/workflows/docs-drift.yml:
Triggers:
- Pull requests to
main(opened or synchronized) - Push to
main
Steps:
- Checkout with full history (
fetch-depth: 0) - Set up Python environment with uv
- Run drift detection script
- Create Linear tasks (if
LINEAR_API_KEYsecret exists) - Comment on PR with summary
- Upload drift report as artifact
Required Secrets¶
| Secret | Description | Required |
|---|---|---|
LINEAR_API_KEY | Linear API key with write access | Optional |
If LINEAR_API_KEY is not set, drift is still detected and reported in PR comments, but Linear tasks are not created.
Artifact Retention¶
The drift report JSON is uploaded as an artifact with 30-day retention for debugging and analysis.
Troubleshooting¶
No Drift Detected When Expected¶
- Check file patterns - Ensure your file matches a rule's
file_patterns
- Check content patterns - If the rule has
content_patterns, verify your code matches
- Check ignore patterns - Verify file isn't being ignored
- Check new_file_only - Some rules only trigger for new files
Duplicate Tasks Being Created¶
Tasks use a deterministic drift-id hash for deduplication. If duplicates appear:
- Check drift-id in description - Each task should have
drift-id:xxxxx - Search Linear -
drift-id:xxxxxshould find existing tasks - Verify API access - Ensure
LINEAR_API_KEYhas read permissions
Linear Task Creation Fails¶
- Check API key - Verify
LINEAR_API_KEYis set correctly
# Test API access
curl -H "Authorization: $LINEAR_API_KEY" \
"https://api.linear.app/graphql" \
-d '{"query": "{ viewer { id } }"}'
-
Check team ID - Verify the team ID in the script matches your workspace
-
Check permissions - API key needs
issues:writescope
PR Comment Not Appearing¶
- Check workflow permissions -
pull-requests: writemust be set - Check drift count - Comments only appear if drift items exist
- Check workflow logs - View the "Comment on PR" step output
Script Errors¶
# Run with verbose output
uv run python scripts/check-docs-drift.py \
--base main --head HEAD \
--verbose \
--output drift-report.json
Common issues:
- Missing git history: Ensure
fetch-depth: 0in workflow - Invalid base ref: Use full SHA or branch name
- YAML parse errors: Validate
docs-drift-rules.ymlsyntax
Related Documentation¶
- Linear Integration - MCP tools and workflow states
- Contributing Guide - PR process and code standards
- Pre-commit Hooks - Other automated checks
- Code Quality - Linting and formatting tools
- AGENTS.md - Project instructions