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.