Skip to content

Python API

The verdict bus, senses, and CI stages are all importable. Example — gate any repo:

from verel.ci import inner_loop_stage, run_stage
result = run_stage(inner_loop_stage(".", with_lint=True))
print(result.verdict)          # pass / warn / fail

The verdict contract

models

The Verdict-bus data model — the unified Report/Percept contract.

This is an extension of AgentVision's Report reached through the sight adapter (verel.senses.sight), NOT a copy. Fields marked COMPUTED are produced by Verel: Issue.fingerprint (§7.2), Report.cost_usd, Report.errored, Report.run_receipt.

Faithful to docs/VEREL_DESIGN.md §7.1.

SignableReceipt

Bases: Protocol

The structural shape the signing/verification machinery (gate, keys) operates on — shared by RunReceipt (per-grader) and GateReceipt (gate-level envelope) so both reuse one signer.

RunReceipt

Bases: BaseModel

Grader-execution attestation (§7.1). Required for graders in the required set.

Two-tier signing (§11): alg="hmac-sha256" (default) is fast and shared within a trust domain; alg="ed25519" is publicly verifiable across domains — runner_identity carries ed25519:<key_id> and verify needs only the producer's PUBLIC key, no shared secret.

Report

Bases: BaseModel

EXTENSION of AgentVision's Report via the §8.3 adapter.

Observation

Bases: BaseModel

One percept observation — the per-issue payload of a Percept.

Percept

Bases: BaseModel

The senses/perception-bus envelope (§8.3). Sight is one sense among many.

ReceiptVerification

Bases: BaseModel

Result of the public verify verb (§11) — did a receipt check out, and could a stranger?

GraderAttestation

Bases: BaseModel

One grader's line in a gate-level receipt (§4): its verdict + the signed RunReceipt that attests it actually ran. Advisory graders (vision/llm) inform but never gate, so they may omit a receipt; a precise grader in the gate's required set must carry a verifiable one.

GateReceipt

Bases: BaseModel

The gate-level receipt (§4) — the headline wedge surfaced over MCP. Wraps the per-grader RunReceipts so a SECOND party can confirm an agent's verdict was real.

The whole envelope is SIGNED (signing_payload binds verdict + fingerprint + identity, alg first), so the aggregate verdict and the grader set are cryptographically bound — not merely fingerprinted. Without that signature an attacker could flip the verdict or relabel a grader precise=false (to skip its signature check) and just recompute the unsigned fingerprint. The same field shape as RunReceipt lets it reuse the ed25519/HMAC signing machinery (keys, gate).

GateReceiptVerification

Bases: BaseModel

Result of verifying a gate-level receipt: did the fingerprint recompute and every precise grader's signature check out — and was the whole thing publicly verifiable (ed25519)?

report_result_digest

report_result_digest(report: Report) -> str

Digest of a report's graded OUTCOME — bind EVERY field the gate trusts so a Report tampered after signing is rejected (§7.1). That means not just verdict + issue (kind, severity, message) but also confidence and source (the gate clamps severity by these) and errored (the dead-gate path) — otherwise an attacker flips confidence HIGH→LOW to clamp a CRITICAL to WARNING while the receipt still matches.

The gate

gate

The Gate — a typed reducer with an explicit CEILING clamp + grader attestation (§7.1), plus generalized stuck/progressed detection (§7.2).

This is the single most load-bearing safety surface in Verel. Every rule here is the direct output of the design's critic loop; do not "simplify" the clamp to a min-by-key.

sign_receipt

sign_receipt(receipt: SignableReceipt, secret: bytes = _RUNNER_SECRET) -> str

Sign per receipt.alg. ed25519 signs with the local runner's key (the caller must have stamped runner_identity/public_key first — see keys.attest_self); hmac-sha256 (default) keys off the shared trust-domain secret.

verify_signature

verify_signature(receipt: SignableReceipt, secret: bytes = _RUNNER_SECRET, *, allowed_algs: set[str] | None = None) -> bool

True iff the receipt's signature is valid under its alg. Fails CLOSED on: empty signature, an alg outside allowed_algs (when a policy is given), an unknown alg, ed25519 with an untrusted key, or PyNaCl absent. ed25519 needs only a trusted PUBLIC key (no shared secret).

verify_receipt

verify_receipt(receipt: SignableReceipt, *, secret: bytes = _RUNNER_SECRET, allowed_algs: set[str] | None = None) -> ReceiptVerification

The public verify verb (§11): check a receipt and explain the result — including whether it was publicly verifiable (ed25519 against a trusted public key) or shared-secret (HMAC).

coverage_satisfied

coverage_satisfied(coverage_assertion: str, diff_files: set[str]) -> bool

The grader must prove it scanned at least one changed file.

coverage_assertion is of the form "scanned files: a.py,b.py". An empty diff set is treated as satisfied (nothing changed to cover).

progressed

progressed(curr: Report, prev: Report) -> bool

STRICT subset shrinkage of the gating-failure set. Equal-cardinality swaps and growth are NOT progress (a decoy that adds a new gating issue is a regression).

Senses (the eyes)

sight

The sight sense — AgentVision adapter (§8.2, §8.3).

Faithful rules enforced here: - Grader identity & precise-vs-advisory key off Issue.source (closed dom/ocr/cv/vision), NEVER off Report.backend (an open provenance string). - The "reachable without a vision backend" capability set is imported from agentvision.core.checks.CLASSIC_CAPABILITIES, not hand-transcribed (drift-proof). - One AgentVision Report is split into one Verel Report per source-grader, so the Gate's report-level advisory clamp (§7.1) is exactly correct (a vision report clamps to WARN; a dom report does not). A single combined Percept is emitted for the episodic log. - Issue.fingerprint, errored, and the synthetic-fallback filter are COMPUTED here.

AgentVision is an OPTIONAL dependency: import it lazily so the verdict-bus core has no heavy deps. Install with pip install "verel[sight]".

SightResult dataclass

What the sight sense produces for one saccade (one analyze call).

classic_capabilities

classic_capabilities() -> set[str]

The kinds the no-LLM (local/checks) path can emit, imported from source so it cannot silently drift from AgentVision.

from_agentvision

from_agentvision(av_report, *, sense: str = 'sight', agent_id: str = '', artifact_id: str = '', cost_usd: float = 0.0) -> SightResult

Map a real agentvision.models.report.Report into the Verel verdict-bus contract.

Pure function over the AgentVision object — no rendering, no I/O.

perceive async

perceive(source: str, *, backend: str = 'local', agent_id: str = '', full_page: bool = True, allow_local: bool = False, settings_overrides: dict | None = None, **analyze_kwargs) -> SightResult

Render + analyze source through AgentVision, return the Verel SightResult.

Thin wrapper over agentvision.analyze(...). Requires verel[sight].

SSRF: AgentVision's block_private_networks guard is left ON by default — source is attacker/agent-controlled, so localhost/LAN/metadata endpoints are refused. allow_local=True is an EXPLICIT opt-in (e.g. an agent verifying its own dev server) that disables the guard.

watch async

watch(source: str, *, backend: str = 'local', agent_id: str = '', **watch_kwargs) -> SightResult

Temporal perception — watch source OVER TIME (playback / loading / liveness).

Thin wrapper over agentvision.watch(...); returns the same Verel SightResult so the verdict bus and brain consume it like any other sense. A deterministic stall (video not advancing) gates to FAIL; the temporal vision findings are advisory/clamped. The percept carries playing/live/stabilized so the brain can compound "playback verified". Requires verel[sight].

CI stages & self-healing

pipeline

Agent-run CI/CD pipeline — inner loop + pre-commit gate (§7.4, v1 stages).

Stages compose graders (any sense) and gate them through the verdict bus with attestation. The pre-commit stage additionally consults failure-memory: a change that reintroduces a previously-fixed failure is gated FAIL from memory alone (§7.5), and new gating failures are recorded so the fleet stops repeating them. This is "CI run by agents" with the same safety contract as everything else — nothing is green unless a grader said so.

run_stage

run_stage(stage: Stage, *, diff_files: set[str] | None = None, runner: Runner = subprocess_runner, ledger: FailureLedger | None = None, ts: float = 0.0, flaky_signatures: set[str] | None = None, attest: str = 'hmac') -> StageResult

Run all graders in a stage, gate them, and (if a ledger is given) apply the failure-memory regression check + record new gating failures. attest selects the receipt scheme: "hmac" (default) or "ed25519" (publicly verifiable, needs verel[attest]).

precommit_stage

precommit_stage(repo: str, *, covers: list[str] | None = None, language: str = 'python') -> Stage

Unit + affected tests + lint; the failure-memory fingerprint check is applied by run_stage when a ledger is passed.

postmerge_stage

postmerge_stage(repo: str, *, smoke_paths: list[str] | None = None, covers: list[str] | None = None, language: str = 'python') -> Stage

Smoke/E2E canary on the merged code (§7.4). A failing canary on PRECISE evidence is what the rollback policy engine acts on (canary.py).

premerge_stage

premerge_stage(repo: str, *, covers: list[str] | None = None, language: str = 'python', with_types: bool = True, security: bool = False, perf: GraderSpec | None = None) -> Stage

Full suite + lint (+types) — the sandbox-CI gate before a merge is allowed (§7.4). Optionally adds a SECURITY scanner (HIGH/CRITICAL gate) and a PERF budget grader. Pair with a regression-guard by passing a ledger to run_stage.

heal

Self-healing CI (§7.4 v2) — the ci-medic actually executes its remediations.

Run a stage; if it fails, triage each failure and act — RETRY re-runs, QUARANTINE_FLAKY downgrades (ERROR→WARNING, ticketed), FIX_BRANCH invokes the code-fixer agent — then re-gate. Repeat until PASS or the rounds run out (escalate to a human with the trail). The agent only proposes patches; the graders decide done, every round.

Memory

view

MemoryView — the trust layer Verel owns over a (rentable) memory backend (§5).

Faithful to the design's load-bearing rules: - Two orthogonal quantities, never multiplied into one stored field (§5): * epistemic_confidence — how true we believe it is. Moved ONLY by corroboration(+)/ contradiction(-). Retrieval NEVER touches it. * retrieval_strength — how reachable it is. Power-law decay with disuse; reset+extended by recall (the testing effect). Decay NEVER mutates truth. - Ranking combines the two by a DOCUMENTED rule (rank() below); it does not collapse them. - Prune ONLY when ALL hold: retrieval_strength < 0.15 AND epistemic_confidence < 0.4 AND support_count < 2 AND trust != verified. - subj_pred_key is the interference key: a new value for the same (subject, predicate, scope) supersedes rather than silently duplicating.

MemoryView is a Protocol so the rented backend (mem0) and the bundled zero-dep LocalMemory (sqlite) are interchangeable. Verel's value is THIS layer, not the storage.

relevance

relevance(query: str, record: MemoryRecord) -> float

Lexical token-overlap relevance (shared by all backends; embeddings are the v2 upgrade behind the same interface).

rank

rank(record: MemoryRecord, relevance: float) -> float

The DOCUMENTED ranking rule. Combines the orthogonal signals + relevance + trust tier; it never multiplies confidence into strength or vice-versa. The trust term is small, so a much more relevant candidate still beats a barely-relevant verified one — but at equal relevance/confidence, verified wins (so a poisoned candidate can't outrank a verified fact).

is_volatile

is_volatile(r: MemoryRecord) -> bool

Volatile-until-confirmed: not retained unless corroborated/verified.

is_expired

is_expired(r: MemoryRecord, now: float) -> bool

Hard TTL — for ephemeral environment facts (e.g. 'current branch is X').

correction_chain

correction_chain(r: MemoryRecord) -> list[dict]

The history of values this record superseded (newest supersession last).

effective_half_life

effective_half_life(r: MemoryRecord, base_half_life_s: float) -> float

Per-record half-life: the base, stretched by demonstrated usefulness (support_count + epistemic_confidence), capped at HL_MAX_FACTOR×. A one-off weak memory decays at the base rate; a corroborated, believed one persists much longer. Reachability tuning only.

apply_decay

apply_decay(r: MemoryRecord, *, now: float, half_life_s: float, stale_after_s: float, volatile_ttl_s: float) -> bool

Mutate r per the decay policy; return True if it should be pruned/deleted. Shared by every backend so lifecycle behaviour is identical across LocalMemory/mem0.