Adopting Wardline in an existing project¶
This guide walks you through adding Wardline to a codebase that already has code. It assumes you have read Getting Started and understand the basic concepts (tiers, taint states, decorators).
Overview¶
Adoption is incremental. You do not need to annotate every function on day one. The recommended sequence is ordered by leverage — each step creates the conditions for the next to succeed. For the systems thinking behind this sequence, see Systems Thinking & Leverage Points.
The recommended sequence:
- Install and create a manifest
- Run a baseline scan
- Triage findings — fix, except, or defer
- Add decorators to key boundaries
- Wire into CI
- Iterate: reduce unknowns, increase analysis level
Step 1: Install and Create a Manifest¶
Create a minimal wardline.yaml at your project root:
$id: "https://wardline.dev/schemas/1.0/wardline.schema.json"
module_tiers:
- path: "src/myapp/"
default_taint: "UNKNOWN_RAW"
metadata:
organisation: "My Company"
Starting with UNKNOWN_RAW is honest — you are declaring that no validation assumptions exist yet. The scanner will report findings against this baseline, and you will promote modules as you add decorators.
Validate the manifest:
Step 2: Run a Baseline Scan¶
Expect many findings. This is normal. The baseline tells you where your boundaries are missing.
If you have jq installed, review the summary:
# Count findings by rule
jq '[.runs[0].results[].ruleId] | group_by(.) | map({rule: .[0], count: length})' baseline.sarif
Step 3: Triage Findings¶
For each finding, decide:
- Fix: Change the code to satisfy the rule. This is the right choice for real violations.
- Except: Grant an exception for findings that are intentional or deferred. Use
wardline exception add. - Suppress via module_tiers: Promote a module's default taint (e.g., from
UNKNOWN_RAWtoGUARDED) if you are confident the module's code is at that trust level.
Start with the highest-severity findings (ERROR at INTEGRAL/ASSURED) — these represent the most significant trust violations.
Step 4: Add Decorators to Key Boundaries¶
Identify your trust boundaries — the functions where external data enters and where validation happens. Decorate them:
from wardline.decorators import external_boundary, validates_shape, integrity_critical
@external_boundary
def receive_api_request(request):
...
@validates_shape
def parse_request(raw):
if "required_field" not in raw:
raise ValueError("missing required_field")
...
@integrity_critical
def write_audit_log(validated_data):
...
After adding decorators, promote the module in your manifest:
module_tiers:
- path: "src/myapp/adapters/"
default_taint: "EXTERNAL_RAW"
- path: "src/myapp/core/"
default_taint: "ASSURED"
- path: "src/myapp/audit/"
default_taint: "INTEGRAL"
Re-scan and compare:
Step 5: Wire into CI¶
See CI Integration Guide for detailed examples.
Quick GitHub Actions setup:
Step 6: Iterate¶
- Reduce unknowns: Add
module_tiersentries and decorators untilUNKNOWN_RAWfindings are near zero. - Increase analysis level: Move from L1 to L2 or L3 as your annotation coverage improves. See Analysis Levels.
- Choose governance profile: Once decorators are in place, consider moving from
litetoassurance. See Profiles.
Common Mistakes¶
| Mistake | Why It's Wrong | Fix |
|---|---|---|
Setting everything to INTEGRAL | Triggers ERROR on every pattern rule | Start with UNKNOWN_RAW and promote as you add decorators |
| Excepting everything | Defeats the purpose; exceptions accumulate governance debt | Only except findings you have reviewed and accepted |
Skipping @validates_shape | Data flows from T4 to T2 without structural validation | Add shape validation before semantic validation |
One big module_tiers entry | Blanket assignment triggers GOVERNANCE-MODULE-TIERS-BLANKET | Use per-package entries with appropriate taint levels |
Further Reading¶
- Getting Started — 15-minute introduction
- Rule Quick Reference — what each finding means
- Severity Matrix — severity per rule per taint
- Governance Walkthrough — managing exceptions