Skip to content

Loom integration

Wardline is a citizen of the Loom suite, but integration is additive, never load-bearing: wardline scan boots, analyzes, writes findings, and gates with both siblings absent. The three output paths below are enrichment you opt into.

Path How Consumer
SARIF 2.1.0 --format sarif --output FILE any SARIF tool (GitHub code scanning, CI dashboards)
Native Filigree emitter --filigree-url URL Filigree's Loom scan-results lifecycle
Clarion producer conformance automatic in metadata.wardline.qualname Clarion entity reconciliation

SARIF

SARIF 2.1.0 is a standard interchange format, so this path works with any SARIF consumer — it is not Filigree-specific.

$ wardline scan src/wardline --format sarif --output results.sarif

With --format sarif and no --output, the default file is findings.sarif in the scan path. The log carries one run with a wardline driver, minimal rule descriptors (the distinct rule IDs seen), and one result per finding — ruleId + ruleIndex, a level mapped from severity (CRITICAL/ERRORerror, WARNwarning, INFOnote, NONEnone), a physical location, and partialFingerprints carrying Wardline's stable fingerprint. Suppressed findings (baseline / waiver / judged) emit a SARIF suppressions entry (kind: external, status: accepted), with the waiver reason as the justification.

The --fail-on gate and suppression annotation run on the findings regardless of output format, so SARIF output and CI gating compose.

Dogfooded in CI

Wardline's own CI scans the wardline source to SARIF and uploads it to GitHub code scanning:

- name: Scan self -> SARIF
  run: wardline scan src/wardline --format sarif --output results.sarif
- name: Upload SARIF
  if: always()
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: results.sarif
    category: wardline-self-hosting

Native Filigree emitter

--filigree-url POSTs findings into Filigree's Loom scan-results lifecycle — Filigree owns finding state (status, seen-count, issue links); Wardline owns the analysis fact and the local baseline. Pass the full endpoint URL:

$ wardline scan . --filigree-url http://localhost:8377/api/loom/scan-results

This is layered on top of the normal local output — Wardline still writes findings.jsonl (or your --output) and runs the gate; emission is additive. The emitter is stdlib urllib only (no new dependency). Findings of all kinds are sent; each goes on the wire with path, rule_id, message, mapped lowercase severity, line range, a top-level fingerprint (Filigree's cross-run identity key), and a metadata.wardline.* namespace carrying qualname, kind, internal severity, and per-rule properties.

The outcome split is load-bearing for the charter guarantee:

  • Sibling absent / outage — connection refused, timeout, or any 5xx: warn and continue. The scan proceeds to its gate; the exit code is unaffected. A Filigree outage must never make Wardline's gate load-bearing.
  • Client/protocol error — a 4xx (or stray 3xx): loud failure. Wardline sent a request the server rejected — a payload/config bug — so the response body is echoed and the command exits 2, even if findings are otherwise clean.
  • Success — a one-line summary reports created/updated counts plus any server-side warnings (Filigree reports severity coercions and line clamps there) and partial-ingest failures.

The severity map (Wardline's 4 levels + facts → Filigree's 5) is:

Wardline Filigree
CRITICAL critical
ERROR high
WARN medium
INFO low
NONE (facts/metrics) info

Clarion producer conformance

Clarion enriches findings by reconciling Wardline's Python qualnames to Clarion entity IDs. Wardline's only obligation is to emit the right qualname — Clarion is purely an enrichment consumer, never on the transport path between Wardline and Filigree.

Wardline emits metadata.wardline.qualname as the combined dotted module.qualified_name (e.g. auth.tokens.TokenManager.issue), composed with Clarion's exact module-normalization rules (strip one leading src/, drop .py, collapse __init__.py to the parent) and the Python __qualname__ preserved byte-for-byte (<locals> markers and nested-class chains pass through untouched).

A vendored conformance corpus pins Wardline's producer against Clarion's normalization vectors in CI, converting byte-equality from an assumption into a test. In Clarion 1.0.0 the reconciliation consumer is not yet built, so this is producer-pinning today; emitting the correct qualname now is what makes future reconciliation lossless.

See also