Weir - unit tests for your agents
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.
You cannot unit-test an agent by string-matching its output, and an LLM judge drifts with its model. But what you actually need to assert is structural: did untrusted tool output reach an outbound sink, did the payment tool fire without its guard, did the secret leave the session. Those facts live in the traces your agent already emits.
Weir is that assertion. Point it at an OpenTelemetry GenAI export; it
exits 1 if a forbidden flow happened - with a witness path you can walk
node by node - and 0 if not. Deterministic and byte-identical on every
run, no LLM in the loop, no network access, never runs your agent (all
tested guarantees). Like its namesake: the session keeps flowing; the
measurement happens anyway.
Try it in two minutes
pip install weir-scan
weir gauge your-export.jsonl # or: weir gauge --sample
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
The gauge answers the question every other tool skips: can your telemetry
support the assertion you want to write? Most real exports cannot yet
(content capture is off by default across the ecosystem), so it names the
exact switch - derived from the instrumentation recorded in the trace
itself. Flip it, re-run, and weir scan becomes your unit 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 - fail the build. The finding carries its evidence, with the
matched secret redacted. Structural facts do not flap: rewording and
step-count variance cannot move them, and when evidence genuinely weakens
a finding demotes with its reason stated instead of silently flipping.
Why you can trust it
- It reads your traces, not ours. The suite includes a frozen JSONL
capture produced by
opentelemetry-sdkand Google's protojson encoder that no weir code touched (provenance). Ingestion is reject-narrow: only not-telemetry is refused; every real malformation degrades under one of 18 named contract rows with a remediation, generated from code and drift-tested: docs/contract.md. - Attacker content cannot rewire it. Joins follow evidence tiers (explicit id, then span nesting, then content-mined); content-mined evidence fills absences only, ambiguity is reported rather than resolved, and a finding crossing a content-mined join is never verdict-grade. If you can make attacker content do more than add visible low-confidence noise, that is a security bug: SECURITY.md.
- The gauge is calibrated against known ground truth. A paired generator emits each scenario as native traces and OTLP-JSON from one plan; the adapter's acceptance test is byte-for-byte equivalence, and gauge numbers are pinned against corpora with known degradation, mutation-proven.
- Every external claim is sourced. Remediation strings that describe someone else's software carry a recorded source and check date: REMEDIATION_SOURCES.md.
What ships, what does not
Shipped: the OTel GenAI adapter (pinned otel-genai/1.42.0), session
graph, verbatim taint and evaluation, the gauge and its capability ladder,
HTML reports, the paired generator and corpora, one teaching rule
(injection-to-exfiltration). Roadmap: the weir diff baseline gate, the
rule-contribution gate and a broader rule set, signed bundles, more
dialect rows.
Open core, in writing: the engine - including the future diff gate - is Apache-2.0 permanently; authenticity, never entitlement. Full signed boundary: docs/open-core.md.
Install name weir-scan (PyPI weir was taken); import weir; command
weir. Apache-2.0.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file weir_scan-0.1.0.tar.gz.
File metadata
- Download URL: weir_scan-0.1.0.tar.gz
- Upload date:
- Size: 73.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cdd07b4adcb586ac3888a6982b14c967aafe7fa3710562a9e3ed16884745eb60
|
|
| MD5 |
f29242505ab17de7b4cb11ca165cd970
|
|
| BLAKE2b-256 |
3f3f51319d51c3dcb2d8c49d8c874a284d4a27a9412aed101d105a3d0dcc3fa2
|
Provenance
The following attestation bundles were made for weir_scan-0.1.0.tar.gz:
Publisher:
release.yml on IdoGol24/weir
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
weir_scan-0.1.0.tar.gz -
Subject digest:
cdd07b4adcb586ac3888a6982b14c967aafe7fa3710562a9e3ed16884745eb60 - Sigstore transparency entry: 2528623979
- Sigstore integration time:
-
Permalink:
IdoGol24/weir@763eb9a13cae96f97c0fc09dbf21e7c02c79845e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/IdoGol24
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@763eb9a13cae96f97c0fc09dbf21e7c02c79845e -
Trigger Event:
release
-
Statement type:
File details
Details for the file weir_scan-0.1.0-py3-none-any.whl.
File metadata
- Download URL: weir_scan-0.1.0-py3-none-any.whl
- Upload date:
- Size: 65.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
839ea837f090c7d613d291bae5570bc52200a84d8d6d9b4dae8d4a386a9cbeea
|
|
| MD5 |
d9247c158a2b05eb123de5c426c6dd76
|
|
| BLAKE2b-256 |
6115a3bc0fea74c8f1f2ac46c505b14364641f4c5219a3c679901147e58da115
|
Provenance
The following attestation bundles were made for weir_scan-0.1.0-py3-none-any.whl:
Publisher:
release.yml on IdoGol24/weir
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
weir_scan-0.1.0-py3-none-any.whl -
Subject digest:
839ea837f090c7d613d291bae5570bc52200a84d8d6d9b4dae8d4a386a9cbeea - Sigstore transparency entry: 2528624079
- Sigstore integration time:
-
Permalink:
IdoGol24/weir@763eb9a13cae96f97c0fc09dbf21e7c02c79845e -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/IdoGol24
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@763eb9a13cae96f97c0fc09dbf21e7c02c79845e -
Trigger Event:
release
-
Statement type: