pollard-jev
A Python companion to Pollard that turns timestamped observations and permitted choices into a governed decision, a bounded simulated action, and an outcome record. The first milestone runs entirely offline after installation. It needs no credentials, model weights, or GPU.
Current release: 0.13, with reproducible local OpenJev benchmarks and an opt-in numerical-boundary and action-priority input experiment. See the changelog.
python -m pip install --upgrade pollard-jev==0.13
pollard-jev demo --output artifacts
Published under the MIT license. The initial release is 0.1; minor increments
are +0.01, major increments are +0.10, and releases at or above 1.0 require
the owner's explicit approval. See the
release policy.
Run on Windows PowerShell
Requires Python 3.11 or newer. Tested here on Python 3.12.2.
git clone https://github.com/jemsbhai/pollard-jev.git
Set-Location pollard-jev
py -3 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -e ".[test]"
.\.venv\Scripts\python.exe -m pollard_jev demo --output artifacts
.\.venv\Scripts\python.exe -m pytest -q
Setup uses PyPI; the demo and tests make no network requests. The equivalent
installed entry point is .\.venv\Scripts\pollard-jev.exe demo --output artifacts.
Each demo writes uniquely named JSONL records and a Pollard SQLite ledger.
| Scenario | Synthetic proposal | Final policy | Simulated action |
|---|---|---|---|
| Fresh coherent observations | continue |
accept | move 20 cm |
| Conflicting observations | inspect |
need evidence | none |
| Missing evidence | inspect |
need evidence | none |
| Evidence expires during inference | continue |
defer | none |
| Request budget exhausted | none | defer | none |
| Provider failure | none | defer | none |
The stale scenario advances a virtual clock during inference so it is reproducible. Separate tests exercise real waiting, timeout, and cancellation. The state-machine baseline reads the same request at the final evaluation time. It has no provider and therefore no provider failures or inference-request cost; the table is a behavior comparison, not a performance benchmark.
Typed inputs and outputs
from datetime import datetime, timedelta, timezone
from pollard_jev.contracts import Observation
from pollard_jev.demo import example_request
from pollard_jev.loop import DecisionLoop
from pollard_jev.providers.fixture import FixtureProvider
now = datetime.now(timezone.utc)
range_reading = Observation(
observation_id="range-42", source="front-range-sensor",
feature="front_range_m", value=1.5, unit="m",
observed_at=now, valid_until=now + timedelta(seconds=5),
)
# The demo request supplies all four required sensor features and four choices.
request = example_request(now, request_id="robot-42")
request = request.model_copy(update={
"observations": (range_reading, *request.observations[1:]),
})
with DecisionLoop(FixtureProvider(), max_requests=2,
records_path="artifacts/decisions.jsonl",
pollard_path="artifacts/decisions.db") as loop:
record = loop.decide(request)
print(record.model_dump_json(indent=2))
Run the complete example with
.\.venv\Scripts\python.exe examples\offline_loop.py.
Unknown readings use status="unknown", value=None; absence of a required
feature is also explicit insufficient evidence. Datetimes must have timezones,
validity windows must be positive, and numeric values must be finite.
A successful record contains fields like these (excerpt):
{
"schema_version": "1",
"provider_status": "ok",
"provider_result": {
"semantics": "synthetic_support",
"scores": {"continue": 0.94, "inspect": 0.12, "recover": 0.12, "request_help": 0.12},
"proposed_action": "continue",
"parameters": {"distance_cm": 20},
"evidence": "sufficient"
},
"policy": {"disposition": "accept", "reason": "supported_and_feasible"},
"action": {"action": "continue", "status": "completed", "simulated": true},
"budget_limit_requests": 2,
"budget_spent_requests": 1
}
Full records include observation IDs, timestamps and units; the question and allowed choices; provider identity, version and settings; policy configuration and version; simulated skill version and observed state; timing and budget fields; and Pollard root/model/action node IDs. Records round-trip through validated Pydantic models. The JSONL is a convenient export; the same complete record is also stored as a Pollard note.
How the loop works
DecisionProvider.infer(tuple[DecisionRequest, ...]) returns typed results.
The interface is general; this milestone's policy and skill set are specific to
the robot demonstration. Providers return data and receive no dispatcher.
Pollard 1.6.0 supplies the real model-call ledger, custom request budget, registered action allowlist, integer argument bounds, refusal nodes, action records, SQLite persistence, and integrity verification. The companion supplies contracts, evidence/support policy, sensor validity checks, deadlines, cancellation, simulation, and linked decision records. The older sibling Pollard checkout was inspected but is not modified or used as a dependency. See the API inspection for exact versions and boundaries.
The policy checks four engineered sensor features: range and camera clearance
in metres, battery percentage, and a stuck indicator of 0 or 1. Conflicting,
missing, duplicate, invalid, expired, and future-dated readings block action.
Support threshold 0.80, score margin 0.15, clearance 0.5 m, battery 10%, and
range disagreement 0.4 m are demonstration settings, configurable through
PolicyConfig. Required physical units cannot be relabeled to change their meaning.
The policy also checks feasibility separately from evidence support. Task
utility is represented by the caller's question and choice hypotheses; there
is no trained utility or physical success predictor here.
The bounded simulated skills are:
| Skill | Permitted integer parameter | Default |
|---|---|---|
continue |
distance_cm: 1–50 |
20 |
inspect |
samples: 1–5 |
2 |
recover |
distance_cm: 1–20 |
10 |
request_help |
retries: 1 |
1 |
The request's selected parameter values must also match the provider proposal. Acceptance never expands the caller's available actions. The registered handler rechecks policy, freshness, cancellation, and the monotonic deadline immediately before dispatch. A second action in the same batch requires new observations, including when the first attempted action fails.
Inference runs in one daemon worker per loop. A timeout or cancellation discards
that worker's reply. Until it finishes, later calls return busy; this prevents
unbounded abandoned workers. Python cannot terminate arbitrary native model
compute. A timeout bounds response/dispatch eligibility, not GPU execution or
total process resource use. Cancellation does not undo an action already
dispatched. Actual controllers, watchdogs, immediate stopping, and motion limits
remain outside this supervisor.
The resource budget counts logical provider batch attempts: one
infer(requests) invocation costs one regardless of question count. Success,
malformed output, provider failure, timeout, and cancellation after admission
each consume one. Pre-cancelled, busy, and budget-refused calls consume zero.
Actions and audit notes consume zero model requests. This is not token, HTTP
request, measured energy, or estimated-joule accounting. An adapter may perform
multiple internal forward passes per logical batch. Budgets last for one loop
instance/run; creating a new loop creates a new budget.
Independent support/entailment scores remain independent; they are never normalized into an action distribution. Categorical results are validated as such only when explicitly declared. Neither score type proves physical success. The fixture scores are synthetic and uncalibrated.
History, policy simulation, and live reevaluation
These are separate operations:
$recordFile = (Get-ChildItem artifacts\decisions-*.jsonl | Sort-Object LastWriteTime -Descending | Select-Object -First 1).FullName
.\.venv\Scripts\python.exe -m pollard_jev history $recordFile
.\.venv\Scripts\python.exe -m pollard_jev simulate-policy $recordFile
$ledgerFile = (Get-ChildItem artifacts\pollard-*.db | Sort-Object LastWriteTime -Descending | Select-Object -First 1).FullName
.\.venv\Scripts\pollard.exe verify $ledgerFile --json
inspect_history(path) only loads recorded events. simulate_policy(record,
config, at=...) evaluates recorded observations and provider output with a
specified policy/time. Neither has a dispatcher or a provider. Policy
simulation does not replay the resource ledger, cancellation state, or a new
physical trajectory. A changed action does not reveal what its consequences
would have been.
live_reevaluate(record, loop) is an explicit new provider invocation. It
consumes the loop's request budget, writes a new record linked to the original,
preserves the original timestamps, and always disables dispatch. It uses
whatever provider the caller explicitly put in that loop; with a fixture it
remains synthetic. Old evidence normally defers at today's time. Historical
records are never overwritten.
Credential fields, arbitrary malformed responses, and exception text are not persisted. Provider authentication must remain inside the caller-owned client. Do not put secrets in questions, observations, model identities, or settings: these are intentional audit content, not automatically scrubbed free text. The JSONL export and SQLite ledger are not a cross-file transaction. A crash may leave an intent or action without the final JSONL record; use the ledger for inspection. This is a single-process demonstrator, not crash recovery or exactly-once actuator infrastructure.
Optional real backend and next milestone
The implemented adapter targets AlexWortega/openjev/qwen3.5-4b-nli-v2 at a
pinned commit. Real 4B inference was verified on Windows/CUDA on 2026-09-27,
reusing an existing local runtime and model cache without any downloads or
installs. The local runner records hashes and runtime versions, enforces offline
loading, and rejects input truncation. See OpenJev setup and evidence
and benchmark method, command and results.
Across 32 held-out synthetic cases, model proposals matched 12 of 20 expected actions. Calibration-only threshold selection yielded 11 correct simulated actions, no incorrect accepted actions, and 21 abstentions, with a 0.534-second median end-to-end decision time. The state machine matched all expected outcomes; this run does not establish a model advantage. Package policy defaults are unchanged. Recorded-sensor validation and the 0.8B comparison remain outstanding.
The follow-up input experiment tested factual text and fixed task-rule prompting. Plain text did not help on development cases. Rules improved fresh-test proposal accuracy from 60% to 67.5%, but obstacle, near-threshold and observation-order failures remain. Both candidates are opt-in; JSON remains the default. The state machine still matched all expected outcomes.
Next: improve numerical/priority interpretation with new held-out validation and recorded sensor data. Add process-level inference cancellation before device integration. Measure energy with an actual meter when available; keep TOML and other proxies separate from joules. Native sensor encoders, controller connections, and MCU deployment are later work.
See implementation status for verified behavior and remaining limitations.
Metadata
Release files for pollard-jev 0.13
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pollard_jev-0.13.tar.gz | 135.0 kB | Without provenance | Details for pollard_jev-0.13.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pollard_jev-0.13-py3-none-any.whl | Python 3 | none | any | Details for pollard_jev-0.13-py3-none-any.whl |
Total release size: 174.7 kB
Release files / pollard_jev-0.13.tar.gz
| Download URL | pollard_jev-0.13.tar.gz |
|---|---|
| Size | 135.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a6d9dc57336be43669a65604f6f9ec3cb31aef82c49b61e98968757375c4927f
|
|
BLAKE2b-256 checksum How to use checksums |
8561961af5cc60e9de335910d908935d00c214e17498b1f0bb88edcbc7ce6bc5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.2
|
Release files / pollard_jev-0.13-py3-none-any.whl
| Download URL | pollard_jev-0.13-py3-none-any.whl |
|---|---|
| Size | 39.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2da118212b151868ee8536a26934e4ad42cb63efb6344035c21a315259f789a6
|
|
BLAKE2b-256 checksum How to use checksums |
6d3deb26660d01cc1905560aaec1a956b998e46aa0ffb75317a81907d6a53582
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.2
|
Security for pollard-jev 0.13
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.