Skip to main content

Weir - unit tests for your agents

Weir is a CI gate for AI agents.

CI PyPI License: Apache-2.0

Terminal recording: weir scan reports a verdict-grade finding with a witness path, then exits 1

It reads the OpenTelemetry traces your agent already emits and fails the build when sensitive data reaches a sink it should not reach.

Weir asks a structural question:

Did sensitive data flow through the agent to a sink it should never have reached?

Your agent already answers that question in the traces it emits. Weir reconstructs the session graph, tracks taint through it, and shows the evidence node by node.

flowchart LR
  A["traces your agent<br/>already emits"] --> B{"weir gauge"}
  B -->|"coverage too low"| C["names the exact<br/>instrumentation switch"]
  C -.->|"flip it, re-run"| B
  B -->|"coverage sufficient"| D{"weir scan"}
  D -->|"no forbidden flow"| E["exit 0"]
  D -->|"forbidden flow"| F["exit 1 + witness path<br/>n2 → n3 → n4 → n5 → n6"]

Try it in two minutes

pip install weir-scan
weir gauge your-export.jsonl   # or: weir gauge --sample

gauge asks the question every other tool skips: can your telemetry even support the assertion you want to make?

evidentiary coverage: 0%
argument capture: 0%
degraded: 100%
tool arguments not captured - this scope is emitted by Traceloop/OpenLLMetry's LangChain instrumentation, which captures content to span attributes by default; check TRACELOOP_TRACE_CONTENT (false disables capture) in the traced service's environment
  linkage: explicit (gen_ai.tool.call.id present)
  payloads: absent - content capture is off
at your current telemetry: coverage reporting YES - taint/scan NO
content capture is off; for OTel GenAI instrumentations built on the util-genai layer, set OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental and OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=SPAN_ONLY to capture gen_ai.input.messages / gen_ai.output.messages / gen_ai.tool.call.arguments and unlock cross-step analysis
exposure scan: 8 spans, 29 strings - no credential-shaped values

Most exports fail this first step, because content capture ships off by default almost everywhere. So weir names the exact switch to flip, read from the instrumentation in your own trace.

Flip it, re-run, and weir scan is the actual test:

1 verdict-grade finding(s)
finding: injection-exfil-to-outbound-sink
  source: financial_account_identifier at node 2 (tool_result)
  sink: send_email at node 6
  witness path: n2 -> n3 -> n4 -> n5 -> n6
  join tiers crossed: explicit
  verdict grade: yes
  matched value: 22 chars

Exit 1, build fails, secret redacted. That finding came from one rule, and a rule is just a file. This is the whole thing:

{
  "id": "injection-exfil-to-outbound-sink",
  "version": "1.0.0",
  "stage": "active",
  "description": "Untrusted content reaches an outbound sink verbatim, carrying a source-class-eligible sensitive value (R5.9).",
  "source_class": "financial_account_identifier",
  "sink_tool_name": "send_email",
  "mode": "verbatim"
}

No code, no DSL. You name a source, a sink, and a mode. The engine builds the graph, tracks the taint, and shows its work.

Rewording your prompts will not move a finding. Adding a step will not move it. If the evidence genuinely weakens, the finding is demoted and says why, instead of quietly flipping to green.

weir asks a simpler question of the same bytes too: is a credential present in this export at all? An instrumentor that serializes an agent object into a span attribute writes the provider key straight into your traces - a finding with one location and no flow at all.

Why you can trust it

  • It reads real traces, not ones we wrote. The suite pins a frozen capture that no weir code ever touched (provenance). Broken input degrades under one of 18 named rows; it never guesses (contract).
  • Attacker content cannot rewire it. Joins follow evidence tiers, and a finding that crosses a weak one is never verdict-grade. Get past that and it is a security bug (SECURITY.md).
  • The gauge is calibrated. One plan emits both native and OTLP traces; the adapter is accepted only on byte-for-byte equivalence.
  • Claims about other people's software are sourced and dated (REMEDIATION_SOURCES.md).
  • Nothing weir prints is a credential. No rendered weir output - text, gauge, HTML, ledger - contains a credential-shaped value; that is a test over every fixture in the corpus, not a promise.

What ships today

The OTel GenAI adapter, session graph, taint and evaluation, the gauge, HTML reports, and the trace generator behind the test corpus.

Next: the weir diff baseline gate, more rules, more dialects.

Apache-2.0, all of it, permanently. Nothing held back, nothing gated, nothing phoning home - the analysis path opens no sockets, and that is a test, not a promise.

Install weir-scan; the import and the command are both weir.

Why "weir"

A weir: a low dam across a river, with water flowing evenly over its crest
A weir is a low dam built across a river to regulate and measure its flow - the water keeps moving; the measurement happens anyway.
Damhead Weir, Water of Leith. Photo by 501ghost, Wikimedia Commons, CC0.

Metadata

Release files for weir-scan 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for weir-scan 0.4.0
File Size Uploaded
weir_scan-0.4.0.tar.gz 126.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for weir-scan 0.4.0
File Interpreter ABI Platform
weir_scan-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 219.9 kB

Release files / weir_scan-0.4.0.tar.gz

Download URL weir_scan-0.4.0.tar.gz
Size 126.3 kB
Tags Source
SHA-256 checksum
How to use checksums
d3780d43467c4e2b5f322884d0445efcc0dcf473dffe36bcef27131ae5aa3ad3
BLAKE2b-256 checksum
How to use checksums
b985f0cc51592ab8ddb0a93379d3c97acd996d20cdfb010e2a4d85637c647c95
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release files / weir_scan-0.4.0-py3-none-any.whl

Download URL weir_scan-0.4.0-py3-none-any.whl
Size 93.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0ef3fcdd32536ec611696dcacdf6a7682400e7d3a2092695dfc5b659a958a553
BLAKE2b-256 checksum
How to use checksums
a6ee2f1f48788699e052723ec9329da0eca008f1591c211089f5f2b1b46bf200
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 7, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page