Repository navigation
Releases: pulseengine/jess
Release list
v0.7.2 — with-device 0.2.2: the claim becomes assertable
with-device 0.2.2 — the claim becomes assertable
with-device now exports WITH_DEVICE_CLAIM into the wrapped command, and adds
--require-claim, so a script can assert its own precondition instead of trusting that whoever
invoked it remembered:
$ with-device --require-claim pixhawk-6xrt
with-device: NOT UNDER A CLAIM for pixhawk-6xrt.
This process is not running inside `with-device`, so nothing stops another agent from driving
the same hardware at the same time — and a collision on a tty is SILENT: both readers get a
partial stream and neither errors.
Re-run as: with-device pixhawk-6xrt --purpose '<why>' -- <your command>
Nesting unions rather than overwrites, so with-device a -- with-device b -- cmd leaves the
command able to assert either.
Why it exists: jess broke its own always-claim rule five times in one session — every one of
them mid-debugging, which is exactly when a remembered rule fails. A discipline that only holds
while you are calm is not a control.
Also in this release, from 0.2.1
Nothing else changed in the interface. 0.2.1's fix (a wrapped command containing -h, -V,
--version, --status or --self-test hijacked the invocation and the tool exited 0 without
running anything) is carried forward and covered by tests.
Testing
40 tests — 20 unit over the pure core, 20 behavioural spawning the real binary — at 96.8%
line coverage, gated at 90 in CI on both ubuntu and macos. The two-platform matrix is not
decoration: an assertion once passed on macOS and failed on Linux because GNU echo interprets
--version while BSD echo prints it, and a second flock on one fd succeeds on BSD but blocks
on Linux.
--self-test remains the field check for machines with no source tree. It is not the suite,
and 0.2.0 is the standing reminder why: it reported 5/5 PASS on a binary that returned exit 0 for
commands it never ran.
Pin
release:pulseengine/jess@v0.7.2!with-device-0.2.2-<triple>.tar.gz!with-device
Assets are cosign-signed with SLSA build provenance and a CycloneDX SBOM; the release workflow
ends by re-downloading the published release and verifying it as a consumer would. Every target
is built and tested on a host that can execute it — including aarch64-unknown-linux-gnu on a
native arm64 runner, so nothing ships unexecuted.
Falsification statement
If this release is what it claims: the cosign signature over SHA256SUMS.txt verifies against
this workflow's identity; every asset matches that manifest; each archive's SLSA provenance
verifies; --require-claim exits 2 outside a claim and 0 inside one for the named device,
and still exits 2 when the claim is on a different device; a wrapped command containing -h or
--version still runs; and a kill -9'd holder's device is immediately claimable. All are
asserted by the release's own verify job and by cargo test.
v0.7.1 — with-device 0.2.1: signed, verified, and the fix for v0.7.0
with-device 0.2.1 — supersedes v0.7.0, which reported success for commands it never ran
If you pinned v0.7.0, move. That release's with-device 0.2.0 matched its mode flags across
the whole argv, so any wrapped command containing -h, --help, -V, --version, --status
or --self-test hijacked the invocation: the device was claimed and released around nothing,
and the caller got exit 0.
$ with-device d --registry reg.yaml -- echo --version
with-device 0.2.0 # its OWN version
rc=0 # and SUCCESS
Its --self-test was green throughout — it only ever wraps sleep, true and kill -9, none
of which carries such a flag, so the entire bug class sat outside every path it walks. Fixed by
splitting on -- first; nothing after it is interpreted. AFD-084.
This release is signed, and verified as a consumer sees it
v0.7.0 shipped a bare SHA256SUMS.txt and nothing else. That is not a supply-chain control —
whoever can replace an asset can replace the checksums beside it — and it meant varve's deposit,
which is fail-closed on a cosign-signed manifest, could not have ingested jess at all. Now
matched to the org pattern used by synth / loom / ordeal / rivet / spar / sigil:
| asset | what it is |
|---|---|
with-device-0.2.1-<triple>.tar.gz |
binary + devices.yaml.example, four targets |
with-device-0.2.1.cdx.json |
CycloneDX SBOM (generated before the checksums, so the signature covers it) |
SHA256SUMS.txt |
manifest over every asset |
SHA256SUMS.txt.cosign.bundle / .sig / .pem |
Sigstore keyless signature over that manifest |
Every archive also carries an in-toto SLSA v1 build-provenance statement in GitHub's attestation
store.
cosign verify-blob \
--certificate-identity-regexp \
'https://github.com/pulseengine/jess/.github/workflows/release.yml@.*' \
--certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
--bundle SHA256SUMS.txt.cosign.bundle SHA256SUMS.txt
sha256sum -c SHA256SUMS.txt
gh attestation verify with-device-0.2.1-<triple>.tar.gz --repo pulseengine/jessA verify job runs exactly that against the published release from a clean runner before this
release is considered good — because "cosign exited 0" and "the release verifies" are different
claims, and they diverge whenever the wrong file was signed or an asset was clobbered afterwards.
Nothing ships unexecuted
v0.7.0 cross-built aarch64-unknown-linux-gnu — the Raspberry Pi target — and skipped its
self-test. That caveat is gone rather than restated: every target is now built on a host that can
execute it (ubuntu-24.04-arm for aarch64-linux, Rosetta for x86_64-darwin), and each runs the
full 35-test suite there.
Testing
35 tests — 18 unit over the pure core, 17 behavioural spawning the real binary — at 96.8% line
coverage, gated at 90 in CI. --self-test remains as the field check that runs where there is
no source tree; this release is the reminder that it is not a substitute for a suite.
Two properties are now asserted that never were: a blocked claimant holds nothing while it
waits (the load-bearing deadlock mechanism identified in AFD-083, which --self-test does not
check), and exit 3 means nothing ran — asserted by the absence of a side effect, not by the
exit code.
Pin
release:pulseengine/jess@v0.7.1!with-device-0.2.1-<triple>.tar.gz!with-device
Falsification statement
If this release is what it claims: the cosign signature over SHA256SUMS.txt verifies against
this workflow's identity; every asset matches that manifest; each archive's SLSA provenance
verifies; a wrapped command containing -h/-V/--version/--status still runs and its exit
code passes through; a second claimant on a held device exits 3 having run nothing; a kill -9'd
holder's device is immediately claimable; an unregistered name is refused; and two processes
claiming the same two devices in opposite order both complete promptly. All are asserted by the
release's own verify job and by cargo test. If any fails, this release is wrong.
v0.7.0 — with-device: the shared-bench claim, as a binary
⚠️ Superseded — do not use
with-device0.2.0 in this release reports exit 0 for commands it never ran. Mode flags
were matched across the whole argv, so any wrapped command containing-h,--help,-V,
--version,--statusor--self-testhijacked the invocation: the device was claimed and
released around nothing, and the caller got success.$ with-device d --registry reg.yaml -- echo --version with-device 0.2.0 # its OWN version rc=0 # and SUCCESSIts
--self-testwas green throughout — it only ever wrapssleep,trueandkill -9, so the
entire bug class sat outside every path it walks. Fixed in 0.2.1; see AFD-084.
with-device — jess's first shipped binary
jess has cut releases before, but never a binary: v0.6.0 and earlier were findings and
artifacts. This release exists because the shared bench needed a tool that a second agent could
run, and that varve could pin — neither of which a script sitting in the repo tree allows.
What it is
Two agents share one physical bench (an STLINK-V3, a Pixhawk 6X-RT). The failure with-device
prevents is not "both want the probe" — it is the silent one. Two readers on a single tty
each receive a subset of the stream and neither errors. Measured on the real board:
ONE reader alone : 66,872 B in 5.0 s
TWO readers together : 37,787 B + 40,053 B
Each got about half, and nothing reported a fault. That is indistinguishable from a flaky USB
link — the worst possible thing to have sitting in a safety campaign's evidence.
The claim is an flock(2) held for exactly the lifetime of the wrapped command, so a crash
releases it. No stale-lock reaper, no release step to forget.
with-device <device>... [--purpose <text>] [--wait <s>] -- <command>...
with-device --status [--format json]
with-device --self-test
Exit codes: 2 usage (including an unregistered device name — a typo must not create its own
lock and exclude nobody), 3 device busy with nothing run, otherwise the wrapped command's own
status.
Multi-device claims are deadlock-free — negative-controlled, not asserted
The original rationale was sorted acquisition (Dijkstra's resource hierarchy). The self-test —
two processes racing for the same two devices in opposite order — passed first run. Removing
the sort showed that was the wrong explanation:
| variant | opposite-order pair |
|---|---|
| as shipped | 0,0 in 2.1 s — pass |
| sort removed, rest intact | 0,0 in 2.1 s — still passes |
| sort removed and hold-and-wait restored | 3,3 in 15.1 s — a real cycle |
The load-bearing mechanism is no hold-and-wait: on contention the claimant drops every lock
it already holds before retrying. Sorting stays as defence in depth and the module header now
says which of the two the test can actually see. Recorded as AFD-083.
Assets
with-device-0.2.0-<triple>.tar.gz for x86_64-unknown-linux-gnu,
aarch64-unknown-linux-gnu, aarch64-apple-darwin, x86_64-apple-darwin, plus
SHA256SUMS.txt. Pin it the way you pin any PulseEngine tool:
release:pulseengine/jess@v0.7.0!with-device-0.2.0-<triple>.tar.gz!with-device
Known scope limit: aarch64-unknown-linux-gnu is cross-built and its self-test is skipped
in the release job — that asset ships unexecuted. It is the Raspberry Pi target, so it wants a
real run on aarch64 before it is trusted there. Every other target is built and self-tested on a
host that runs it.
Zero third-party dependencies — flock(2) is declared with a bare extern "C" — which is what
makes it reasonable to carry inside a signed varve layer (pulseengine/varve#130).
Falsification statement
If with-device is what it claims, then: a second claimant on a held device exits 3 having run
nothing; a device whose holder was kill -9'd is immediately claimable; an unregistered name is
refused rather than silently locked; and two processes claiming the same two devices in opposite
order both complete promptly. Each is asserted by --self-test, which runs on every PR. If any
one of those fails, this release is wrong.
v0.6.0 — RT1176 hermetic emulation + MAVLink bench
jess's first tagged engineering milestone: hermetic Pixhawk 6X-RT (i.MX RT1176) M7 emulation + real MAVLink decode, both CI-gated — the foundation the on-target bring-up (v0.7.0+) builds on.
Scope (V-closed, accepted)
- REQ-PIX-005 — the hand-authored RT1176 Renode platform boots bring-up firmware to the
JESS-RT1176 boot OKbanner on LPUART1. Verified by TEST-PIX-005. The samerenode-smokegate also runs the synth#374 OOB-trap oracle (TEST-PIX-013) and the synth#507 br_table oracle (TEST-PIX-020). - REQ-PIX-010 — the MAVLink bench decodes the committed 6X-RT sample with correct X.25 CRC. Verified by TEST-PIX-010.
CI on the tagged commit (7b591ac) — all SUCCESS
rivet validate · spar model · mav_bench oracle · Renode RT1176 smoke
Falsification statement
v0.6.0 is falsified if, on the tagged commit, the RT1176 Renode platform faults or fails to reach JESS-RT1176 boot OK, or the MAVLink bench miscomputes the X.25 CRC on the committed sample, or any of the four CI gates is not success.
Deferred (logged, DD-021)
REQ-PIX-001 (falcon fpv5-d16 hard-float firmware) → v0.7.0 — cannot reach verified until synth#369 (VFP lowering) + #275 (dispatch) land (both external).
Planned roadmap
v0.7.0 on-target bring-up · v0.8.0 PX4 HITL + persistence + root-of-trust · v0.9.0 gust/F100 + combined vehicle + witness/scry gates. All gated on the supplier poles (synth #369/#275/#503, gale#65, hub#98).
🤖 Generated with Claude Code