diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7cf8211..5113bfc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,3 +36,20 @@ jobs: cargo test --workspace --locked - name: Unit and equality tests (no keys, no browser) run: uv run pytest -q -m "not browser" + - name: Camera, desk inbox and workbench conformance (synthetic inputs) + run: | + uv run ruff check ../tools/camera-capture-experiment ../tools/desk-inbox-experiment ../tools/workbench-notes ../tools/physical-workcell + uv run python -m unittest discover -s ../tools/camera-capture-experiment -p 'test_*.py' -v + uv run pytest -q ../tools/desk-inbox-experiment/test_inbox.py ../tools/workbench-notes/test_workbench.py + - name: Install pinned physical simulator and headless rendering libraries + working-directory: . + run: | + sudo apt-get update + sudo apt-get install -y libegl1 libgl1-mesa-dri + uv venv --python 3.12 tools/physical-workcell/.venv + uv pip install --python tools/physical-workcell/.venv/bin/python -r tools/physical-workcell/requirements.txt + - name: Physical workcell conformance (simulation, no model calls) + working-directory: . + env: + MUJOCO_GL: egl + run: tools/physical-workcell/.venv/bin/python -m unittest discover -s tools/physical-workcell -p 'test_*.py' -v diff --git a/README.md b/README.md index cc588bf..aa37740 100644 --- a/README.md +++ b/README.md @@ -17,11 +17,40 @@ The Python package is `jevdual` (in [`jevdual/`](jevdual/)); Footwork is the pro from a run directory under [`jevdual/results/`](jevdual/results/), and every experiment was pre-registered in [`jevdual/docs/experiments/`](jevdual/docs/experiments/) before it ran. +For an agent integration, start with [capability installation](jevdual/docs/capability-packaging.md) +and the [agent-host recipe](jevdual/docs/installed-agent-host.md). The wheel supplies five MCP tools +for one scoped browser-to-TextEdit job on macOS, with durable operation results and validated completion +reports. It needs existing Python, uv, Chrome and applicable host permissions. The current candidate +does not discover arbitrary apps; its disposable fixture helpers derive the identifiers for that test job. + Optional [browser inspection utilities](tools/browser-inspection/README.md) collect redacted, timestamped evidence through an existing tab/CDP adapter. They are dependency-free ESM helpers, separate from the Python driver and verification gates. Their contract is tested offline; live capture fidelity must be calibrated on the selected browser. +The optional [desk paper inbox](tools/desk-inbox-experiment/README.md) connects a saved +camera frame to local text recognition and a reviewed inbox note. Its private review keeps +the original image and OCR text. `footwork check-artifact` checks the saved note against +the approved source bytes without a model call; it does not judge recognition accuracy. + +[Project-linked workbench records](tools/workbench-notes/README.md) attach reviewed evidence +to an explicit project, repository revision and proposed check. The optional +[MuJoCo workcell](tools/physical-workcell/README.md) explores a body interface with +physical-state feedback and a conditional computer work-order update. Simulation results +remain separate from real-camera and robot qualification. +Its [image-guided route](tools/physical-workcell/IMAGE_GUIDED.md) supplies calibrated RGB views +to an agent, accepts frame-bound proposals and requires fresh visual placement evidence before +the conditional local computer action. +The [repeatable episode interface](tools/physical-workcell/EPISODES.md) binds varied cases, +retains images when perception fails, and compares fresh-agent decisions with a deterministic +RGB controller. [SIM3 results](jevdual/results/repeatable-work-orders-20261010/report.md) +record all twelve attempted episodes and their limits. +The [independent controller](tools/physical-workcell/CONTROLLER.md) owns a simulated episode +in a separate process, with reconnectable operation status, cancellation evidence and +conservative receipt-based restart recovery. +[SIM4 results](jevdual/results/independent-controller-20261010/report.md) record disconnect, +cancellation and crash trials, retained failures and the boundary of supported recovery. + ## Drive it ```sh diff --git a/jevdual/docs/capability-packaging.md b/jevdual/docs/capability-packaging.md index f12767b..cc9a78f 100644 --- a/jevdual/docs/capability-packaging.md +++ b/jevdual/docs/capability-packaging.md @@ -107,3 +107,7 @@ The summary describes the configured task's admitted operation history as of its The local chrome-devtools-mcp package verifier checks the artifact produced for publication rather than trusting a source file list. This builder applies that mechanism to its actual wheel; it copies no upstream source. The license metadata follows [PEP 639](https://peps.python.org/pep-0639/): `License-File: LICENSE` resolves under the wheel's `.dist-info/licenses/` directory. The setup improvements use the structured prerequisite/error mechanism in local Cua, ambient-configuration isolation in Hermes, and exact emitted-command MCP discovery in Open Computer Use. Implementations are independent. Newer reference versions are not dependency upgrades; Footwork retains its pinned seams. The newer Cua doctor also performs legacy-file cleanup, so it is not used as a read-only verifier here. + +## Qualified local candidate + +The repaired guest permission-reset journey and one installed Claude Code task both passed with independent review. See [the guest report](../results/u3-packet-v4-permission-20261012-v3/report.md) and [the real-host report](../results/installed-agent-release-20261012-v2/report.md). The local release bundle preserves wheel9bb0440d and helper source445a348; its manifest identifies release documentation separately. It includes an [agent-host recipe](installed-agent-host.md) and exact qualification limits. Public registry publication, other hosts, pixel capture and disk persistence are separate gates. diff --git a/jevdual/docs/experiments/A2-installed-agent-release.md b/jevdual/docs/experiments/A2-installed-agent-release.md new file mode 100644 index 0000000..678dcb5 --- /dev/null +++ b/jevdual/docs/experiments/A2-installed-agent-release.md @@ -0,0 +1,37 @@ +# A2: installed agent host and release candidate + +Registered October12,2026 before installation or host execution. Developmental integration qualification; no A1, U2, U3 or heldout result is regraded. + +## Hypothesis and predictions + +One real Claude Code host can discover and use a fresh installed Footwork candidate for one owned browser-to-TextEdit task, with durable completion validated by the public adapter. If true, installed origins/private HOME, one exact append, one journal operation, accepted structured completion and automatic cleanup all pass. If false, a required gate is absent, wrong, refused or unverified; no generic success prose substitutes for it. + +## Design + +Use the immutable205577ee qualification packet and unchanged9bb0440d wheel from source445a348. Extract in a new owned output root; verify before running. Fresh empty-cache binary dependencies/private HOME on the existing host Mac and interpreter. Existing host permissions are prerequisites, not a new permission grant journey. Source harness/oracle may run outside the installed runtime; actual MCP server and detached worker must come from the installed venv, with -I and source/environment overrides excluded. + +One owned private Chrome fixture and exact TextEdit document; no unrelated GUI inputs. Generate the public per-job configuration and launch its actual command directly from real Claude Code, with only bind/observe/act/status/report. Use the existing isolated CLI recipe, modelclaude-opus-5-5/higheffort,1USDsoftcap and2USDschedulingreserve,180secondtimeout, no retries. Agent authentication remains opaque and unchanged; no credentials are opened/copied. Restrict MCP configuration to this job, disable builtins/hooks/slash commands/settings inheritance/session persistence. Use a fresh empty cwd inside project output. Record actual offered tools/model/version/calls and cost, exact AX oracle, read-only durable journal, runtime audits and worker absence. + +Host final structured_output must satisfy generated report-schema.json. After host exit, invoke the exact emitted report_adapter with those claims; retain accepted or rejected output and publish only trusted_summary. Independent root/Claude evidence review checks the final claim against its actual effect. One SIGINT closes the fixture automatically; verify exact document/browser/listener/worker absence. Preserve failed records; never repeat uncertain native input. No provider calls beyond this single host session and the authorized existing mailbox review. + +After passing this lane and Claude review, finalize a separate local release bundle with unchanged qualified runtime/helpers, installation and real-host guidance, MIT license, member hashes and evidence identities. Build twice and verify exact allowlisted members/digests and wheel RECORD/source equality. Keep qualification packet unchanged. No public registry upload, release/tag creation, push or merge is part of this package. + +## Primary metric and decision + +All required installation, actual host environment, exact historical effect, durable report and automatic cleanup gates must pass in one run. A missing prerequisite/host transport makes the lane unverified; a behavioral mismatch fails it. No retry changes that record. Accept only the stated host/OS/artifact scope after independent Claude review. Disk persistence, pixel routes, other agent hosts, cold OS and other machines remain unqualified. Final release bundle is a local candidate with these limits. + +## Evidence + +Private: output/installed-agent-release-20261012-v1. Sanitized: results/installed-agent-release-20261012-v1. Notebook: docs/notebook/2026-10-12-installed-agent-release.md. + +## Amendment · 2026-10-12 · before host execution + +Claude structured output may expose its schema finalizer `StructuredOutput` alongside the five MCP tools. Permit only that finalizer, which validates the requested report object and grants no resource effect. Require the exact five Footwork MCP tools, no other offered/called effect tools, and record the actual catalog. This replaces the literal five-total-tools inventory check without changing scope or completion requirements. + +A preparation CLI typo passed --root to a verifier requiring --packet-root, and the shell continued into dependency installation. No fixture or agent started. Retain v1 with this operator error; v2 first passes pristine14member verification, then performs its own fresh installation. Neither preparation contains a measured native/model task. The single paid/live attempt will be in v2. + +## Result + +October12,2026: the v2 fresh installation and one real Claude Code task passed every selected gate. Actual modelclaude-opus-5-5/CLI2.1.296 used the generated installed MCP command with five public tools plus StructuredOutput, one append and one durable final/applied/sent operation. The emitted readonly adapter accepted finalclaims afterhostexit; a missing-operation claim was rejected. ExactAXoracle, installedserver/worker/privateHOME provenance andautomaticcleanup/independentabsence pass. Cost0.0990072USD. Claude independently approved this single clean task; no real-host restart/uncertainty scenarios are claimed here. + +Final local release source d931559 packages the unchanged qualified9bb0440d wheel and445a348 helpers. Two builds match8dc81c72,30members342599bytes; manifest/RECORD/source/extractedverifier/18links pass. Independent review of v1 found identifyingpaths in legacyevidence; IA3preregistered derived sanitization, preserved originals and produced v2 with zero identifyinghomepaths and original/derived hashes. Claude1791837075792802000 approves exactv2 andclosesN1. See results/installed-agent-release-20261012-v2 and results/release-candidate-20261012. Registry publication, tags, push/merge, otherhosts/pixels/persistence remain outside this package. Original failures and all prior versions remainpreserved. diff --git a/jevdual/docs/experiments/A3-readme-only-codex-adoption.md b/jevdual/docs/experiments/A3-readme-only-codex-adoption.md new file mode 100644 index 0000000..c371883 --- /dev/null +++ b/jevdual/docs/experiments/A3-readme-only-codex-adoption.md @@ -0,0 +1,33 @@ +# A3: README-only Codex adoption on the existing Mac + +## Hypothesis + +A fresh Codex host given the exact macOS candidate and its README can install it, prepare an owned fixture and complete its bounded browser-to-TextEdit job without author-supplied process IDs, window IDs, URL or document path. + +If true, the published helpers and linked instructions suffice to derive job scope and use the registered public tools. If false, the retained attempt identifies missing installation or host-registration instructions, target discovery friction, unavailable host permissions, or an execution/report/cleanup defect. + +## Frozen inputs and prerequisites + +Use only candidate ZIP `8dc81c724bf617a9f3aa4833f250d5124b9e8c5e9a1c504c67d634080f44fd1f`, wheel `9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7`, and its extracted README and linked consumer files. Installation uses existing compatible Python, uv and Chrome and fetches declared binary dependencies. Host authentication remains opaque and unchanged. Score provenance by the exact release ZIP and wheel digests. A helper receipt's `source_commit` from this bundle means the manifest/release commit `d931559`, while its qualified product source remains `445a348`; these are distinct identities and must not be compared as interchangeable commits. + +The user substituted this existing Mac for another Mac and requires at least 8 GiB available memory with other project actors idle. Measure resources before model or fixture execution; if the gate fails, retain a blocked preparation record and do not claim an adoption attempt. Keep the macOS VM stopped. Stop only independently attested, owned idle Footwork actors; unrelated apps and other agents require direct authorization. The separate user response about which apps may be quit remains pending. + +## Design + +After PR3 lands, use a fresh empty directory under ignored project output. Disable inherited project instructions, memory, skills, hooks and MCP configuration through supported local Codex flags. Never expose repository source or previous transcripts to the adoption host. Retain actual command, CLI version, offered tools, calls, errors and final output. + +A fresh installer context receives the package README and the ordinary task: install the candidate and prepare its disposable qualification job to read the invoice total and append exactly that value while preserving existing document characters. It may read linked consumer instructions and native CLI help. It must derive all target identifiers through the bundled helpers. It may write only within the owned trial directory and may not change global settings, copy credentials, use personal browser profiles or operate unrelated documents. + +Host registration is fixed at Codex startup. If a second context is needed, the author transfers only the installer-generated MCP registration into a fresh execution context by mechanical JSON-to-TOML adaptation. Preserve the exact command, arguments and environment; add no target identifiers, source knowledge or task coaching. Record both contexts separately. The executor uses the five actual registered Footwork tools and the generated report schema. Model choice remains the host default. Bound installer and executor time; retain failed or unavailable sessions without retrying uncertain native input. + +After executor exit, the author checks the exact destination accessibility value independently, inspects the durable journal read-only, and invokes the generated report adapter with only the host's structured claims. Publish only its trusted summary. Verify complete admitted-operation coverage, one applied/sent append and exact expected text. Signal the owned fixture once and check its cleanup receipt, exact document window, owned browser/listener and detached worker absence. Saved-file persistence remains `not_checked`. + +## Primary metric and decision + +Accept only if installation, self-derived scope, actual registered MCP execution, independent exact-text oracle, durable report validation and automatic cleanup all pass for the frozen artifact. Mechanical startup transfer is disclosed as two contexts rather than claimed as a single uninterrupted session. A prerequisite failure is unverified; a behavioral mismatch fails the attempt. Preserve every record. Fix only demonstrated friction in a new artifact after review and new qualification; keep the original candidate immutable. + +This measures README-only adoption on an already provisioned Mac. It does not establish another machine, a never-granted OS, arbitrary app discovery, pixel capture, physical devices or disk persistence. + +## Evidence + +Private output: `output/readme-codex-adoption-20261014-v1`. Notebook: `docs/notebook/2026-10-14-consolidation-adoption.md`. Results will be appended after the attempt; a pending resource gate is not a passed test. diff --git a/jevdual/docs/experiments/DI1-desk-paper-inbox.md b/jevdual/docs/experiments/DI1-desk-paper-inbox.md new file mode 100644 index 0000000..c3ac6bd --- /dev/null +++ b/jevdual/docs/experiments/DI1-desk-paper-inbox.md @@ -0,0 +1,32 @@ +# DI1: Camera paper note to a Footwork-checked desk inbox + +Mode: bounded engineering/workflow feasibility. Not a model qualification or product-demand test. + +## Hypothesis + +An existing native-camera frame can supply a small paper note to local Apple Vision text recognition; an explicit review bound to the candidate digest can save exactly that text into a private inbox and Footwork can independently verify the saved bytes. + +## Predictions + +A known synthetic printed note yields expected text through the real Vision helper. A live paper note, when presented clearly and reviewed, can yield a saved note matching approved text. No text, altered candidates/source images, mismatched approval digests or invalid saved bytes cannot become verified notes. Repeating an accepted operation does not create duplicate notes. + +Against: a verified result without exact matching saved bytes, fabricated browser/native carries, unreviewed capture triggering an action, or a repeat creating another note. + +## Design + +Keep camera acquisition in the existing tools/camera-capture-experiment package and desk workflow in tools/desk-inbox-experiment. Use one native frame per deliberate operation; microphone disabled. Use installed Swift/Vision for local English OCR, with no cloud provider. Preserve OCR lines/confidence as fallible evidence, never execution authority. Explicit review approves a text digest; corrections are retained separately from original recognition. Keep images, recognized/private text, review pages, notes and per-operation receipts in ignored private output. + +Introduce a narrow provider-free read-only footwork check-artifact command and library boundary. It checks a digest-bound reviewed source and exact destination bytes. It supplies current artifact equality, not OCR correctness, native UI completion, authorization authentication or an exactly-once physical effect. Do not synthesize Footwork session carries or change native recovery/guard interfaces. + +Primary metric: one accepted note exists exactly once and matches its independently approved source bytes (binary feasibility), with source/candidate provenance retained. + +Checks: real Vision on a known generated printed note; public CLI whole workflow; repeat across processes; missing text; changed source/candidate; changed approval digest; missing/tampered destination; concurrency and interrupted write/receipt handling as appropriate. A human live note is a separate validation requiring positioning and review; if unavailable, report that gap explicitly. + +Decision rule: qualify only demonstrated source/OCR/file behavior. Advance toward a daily desk inbox only after one real note is reviewed and saved. Keep synthetic proof separate from live paper utility and semantic correctness. No model calls, UI automation, external task creation, publication or robot movement. + +## Result + +Reviewed live note handoff verified on frozen source 6e93c35: user-confirmed corrected text saved once and independently matched by Footwork; original frame/raw OCR retained. All 31 focused tests and Ruff pass. Known printed/normal/mirrored OCR matches expected text; blank and adversarial cases remain explicit. Raw live handwriting OCR required correction, so autonomous transcription and daily utility are unqualified. See results/desk-inbox-20261010/report.md and verification.json. + +## Amendment · 2026-10-10 14:54 +The user asks whether the camera mirrors. Preparation will expose an explicit --mirror-input option for OCR orientation and review display, bind it in request provenance, and retain original capture bytes. Mirroring is chosen from readable calibration text, not inferred from faces or OCR confidence. Normal/mirrored synthetic fixtures and the live LEFT 123 RIGHT line will assess orientation. diff --git a/jevdual/docs/experiments/NC1-native-camera.md b/jevdual/docs/experiments/NC1-native-camera.md new file mode 100644 index 0000000..5020bc9 --- /dev/null +++ b/jevdual/docs/experiments/NC1-native-camera.md @@ -0,0 +1,33 @@ +# NC1: Native Mac camera frame acquisition + +Mode: bounded engineering feasibility experiment, not a comparative product-value trial. + +## Hypothesis + +An isolated FFmpeg AVFoundation capture route can acquire a frame from the specifically observed built-in MacBook camera and retain a truthful operation/artifact record. + +## Predictions + +If supported and authorized, one capture yields a decodable saved PNG and matching receipt. Repeated client tokens retrieve that record without camera dispatch. Missing authorization, missing camera, missing receipt or corrupt bytes never become successful acquisition. + +If the hypothesis fails, permission/runtime acquisition or artifact verification cannot establish the requested frame; the failure remains explicit. + +## Design + +Use the installed FFmpeg and ffprobe binaries and Python standard library. Native permission inspection may use the installed Swift toolchain and AVFoundation. No new package install, provider call, microphone, screen capture, virtual-camera substitution, robot movement, or modification to jevdual production interfaces. Capture private images only in the experiment's ignored output. Use one fixed-operation ledger per run with conservative unresolved outcomes. + +Primary metric: successful bounded acquisition with independently decodable saved image and matching hash (binary feasibility result). + +Guardrails: exact physical device selection; bounded child lifetime; no microphone; no second dispatch for repeated tokens; artifact tamper/missing refusal; caller restart does not silently repeat an unresolved operation. + +Decision rule: advance only frame-acquisition feasibility when all required conformance checks pass and a real native-camera acquisition is verified. Report a permission or runtime prerequisite as unverified. This says nothing about exposure timestamps, scene interpretation, document quality, capture-to-page identity, shutter idempotency, power-loss recovery, exactly-once effects, or useful workflow comparisons. + +## Result + +Frame-acquisition feasibility verified on source 48b33a1 in attended live-v3: one 1080 by 1920 PNG independently decoded by ffprobe and FFmpeg, matching receipt hash and bytes; fresh-process repeat/status preserve the same operation/artifact, with no remaining FFmpeg process. The 18 conformance tests and Ruff pass on the unchanged source. Earlier live-v1/v2 unknown records remain preserved. Their timeout cause and prompt history are unqualified. See results/native-camera-20261010/report.md and live-v3.json. + +## Amendment · 2026-10-10 14:30 +Live-v1 on 2bccd45 found FFmpeg default 29.970030 fps unsupported; no PNG exists and its ledger remains unknown. Next frozen version will explicitly request device-reported 30 fps and record it as part of the request. Live-v2 is a distinct operation, without altering or replaying live-v1. Original scope and decision rule remain unchanged. + +## Amendment · 2026-10-10 14:40 +The user was away during prior attempts and explicitly requested another attempt while present. Live-v3 will use unchanged source 48b33a1 with a 60-second bound, a distinct run directory/token, and the same exact camera and 30 fps mode. Earlier unknown records remain unchanged. This is a new authorized acquisition, not replay or resolution of an earlier outcome. diff --git a/jevdual/docs/experiments/SIM1-physical-computer-workcell.md b/jevdual/docs/experiments/SIM1-physical-computer-workcell.md new file mode 100644 index 0000000..f5abed1 --- /dev/null +++ b/jevdual/docs/experiments/SIM1-physical-computer-workcell.md @@ -0,0 +1,31 @@ +# SIM1: Simulated physical action coupled to a computer work order + +Mode: bounded MuJoCo control/verification feasibility, not real-robot or learned-vision qualification. + +## Hypothesis + +An agent-facing observe/act/complete interface can use native MuJoCo physics and a bounded controller to move one simulated block into a work-order inspection zone. A computer work-order update is emitted only after observed simulator state satisfies the explicit physical predicate, and Footwork independently verifies the saved software result. + +## Predictions + +A small planar pusher uses actuator commands and real simulated contacts (no teleporting or externally setting the block pose). State persists between CLI processes; observations identify generation, body frame, block/pusher pose and whether the target-zone predicate holds. Admitted movement advances physics under fixed controller bounds. Completion before the block is in-zone refuses a successful computer result; completion after a verified move creates one digest-bound work-order result checked by public Footwork check-artifact. Repeated tokens do not reapply movement or duplicate results. Stale-generation/out-of-envelope requests refuse before dispatch; interrupted/unreceipted action blocks replay. + +Against: setting the block pose directly instead of using physics; empty motion reported as fulfilled without state readback; software order marked completed while physical predicate fails; stale/unsafe motion admitted; repeated or unknown movement replayed; simulator ground truth passed off as real camera/robot evidence. + +## Design + +Keep prototype in tools/physical-workcell. Install pinned mujoco==3.15.0 in its own ignored virtual environment under the user's download authorization. Use an included small XML model: tabletop plane, free block and XY pusher with native position actuators. Use a smooth bounded target trajectory and fixed simulation stepping/settling; retain model/controller hashes and work-order digest. Only a known block/task and contained workspace are supported. + +Provide init/observe/act/complete with private owned run state, process lock, explicit generation and operation token/argument binding. Serialize MuJoCo integration state without pickle; do not silently reset physics on lookup. Keep observation/frame source_kind=mujoco_simulation and outcomes of physical predicate versus software artifact separate. Optional native simulator rendering is helpful evidence but not image-recognition qualification. Proposed robot hardware/ROS/LeRobot adapters remain unimplemented. + +Primary metric: the observed simulated block reaches its explicit zone and exactly one corresponding local work-order result is independently checked (binary). Negative criterion: before motion/outside zone the same completion cannot succeed. + +Checks: actual native engine public CLI across fresh processes; before/after observed poses and moved block under contacts; constrained motion bounds; stale token/generation and repeated actions; unknown/lost receipts no replay; conditional digital result and tamper/missing result; optional render/readback. No new camera acquisition, provider call, external project write or physical actuator effect. + +Decision rule: qualify only demonstrated simulation/native-controller/local-computer coupling. This supplies a usable bounded body interface for an agent, not general locomotion/manipulation, visual grounding, real-time OS guarantees or transfer to hardware. If contact dynamics or rendering fails, preserve evidence and distinguish the failing mechanism before changing source/model settings. + +## Result + +Held for the bounded simulated workcell. Prediction committed in `49904e3`; measured implementation/model frozen in `ece54ae`. The independent public CLI run moved the free block through native contact into the work-order zone, refused premature completion, persisted state between processes, refused stale/out-of-envelope motion, and preserved state/results on repeated tokens. A conditional local work-order result passed a separate public Footwork byte check. Native before/after images were inspected. See `results/physical-workcell-20261010/report.md` and `independent-demo.json`. + +This establishes simulator/controller/local-computer coupling. It does not establish camera perception, robot hardware, general manipulation/navigation, real-time control or an external project update. diff --git a/jevdual/docs/experiments/SIM10-network-workbench.md b/jevdual/docs/experiments/SIM10-network-workbench.md new file mode 100644 index 0000000..4593144 --- /dev/null +++ b/jevdual/docs/experiments/SIM10-network-workbench.md @@ -0,0 +1,27 @@ +# SIM10: Browser workbench on a persistent network body + +Hypothesis: an image-guided browser operator can use the existing durable network client while keeping physical authority local and making transport uncertainty recoverable by explicit user controls. + +Predictions if true: successive browser jobs retain one body, independently verified measurements and contact resets; a lost motion reply visibly blocks the queue; disconnect/reconnect and reload do not resend that command; receipt lookup settles only retained evidence and requires explicit Resume before fresh planning. + +Predictions if false: the browser reports completion without proof, hides uncertainty, repeats an uncertain effect, silently resumes after reconnect, replaces body identity, or grants action authority using a retained image. + +Design: separate new browser runtime, HTTP server and HTML from the existing local operator. Reuse BodyClient and BodyService without changing core guards. An owned loopback diagnostic link retains its exact endpoint and provides disconnect, bounded delay, dropped reply and duplicate controls. The browser host uses TCP-only public observations/results, durable queue and pending-stage records, exact identity/source pins and the existing actual Codex image policy. Restart is paused; recovery is read-only and explicit. + +Primary metric: all required product transitions pass in a real browser: enqueue two jobs, show a lost dispatch reply as uncertain, show a disconnected link, refuse receipt recovery while disconnected, reconnect without sending, check the exact receipt, explicitly resume, complete both jobs with measurement and contact reset evidence, and reload without replay or loss of completed history. + +Secondary checks: deterministic real-socket cases cover uncertain start/proposal/dispatch, queue-stage restart recovery, duplicate and delayed dispatches, connection identity, source changes, pause cancellation and cached-image integrity. The frozen actual-model trial is scored separately from native snapshots, SQLite and transport/model receipts. Fake planners are conformance evidence only. + +Guardrails: no direct native calls from the browser host, no teleport, no private pose in model inputs, no uncertain retries, no automatic resume on reconnect or receipt lookup, no source edits during the measured model trial, and no changes to the existing body/core guard files. Lost receipt without proof remains blocked. Already admitted bounded motion may finish after disconnect or pause. Preserve failed attempts. + +Decision rule: adopt and show the network browser product only if conformance checks and the independently scored frozen browser/model trial pass. Otherwise retain the prior idle local product and report the failed boundary. + +Scope: same-Mac loopback simulation; remote authentication, hardware and emergency stopping remain outside this experiment. + +## Amendment · 2026-10-11 17:27 + +Host admission is the durable checked stage write, before handing the exact command to BodyClient. Pause/Disconnect prevent later host admissions; an admitted request may reach the body after the control returns. The body retains its separate lease/freshness/effect checks. Qualification must not claim that Pause interrupts this in-flight request. Add a control-boundary check: pause before admission yields no command; pause after admission permits only that command to settle and admits no successor. + +## Result · 2026-10-11 17:44 + +Held on the first frozen actual-model/browser attempt at source `d731ac8ea03f326edef18ea9c1b9cb2027421871`. Ninety-four conformance checks passed. The actual browser showed lost-reply uncertainty, disconnected receipt refusal, transport-only reconnect, exact receipt recovery and explicit Resume. Two successive jobs completed with native placement, one SQLite measurement each and contact resets independently checked after trial cleanup. All 26 model calls used exact TCP images and bound core receipts; the client sent each command once. Duplicate and 0.8-second delay controls also ran. Reload preserved completed history without a new command. Restart preserved both identities and left the host paused with no active or pending work. Evidence and retained earlier failures: [report](../../results/network-workbench-20261011/report.md), [score](../../results/network-workbench-20261011/qualification-score.json), [attempt ledger](../../results/network-workbench-20261011/attempt-ledger.json). Scope remains loopback simulation. diff --git a/jevdual/docs/experiments/SIM11-workbench-restarts.md b/jevdual/docs/experiments/SIM11-workbench-restarts.md new file mode 100644 index 0000000..5ec4c7a --- /dev/null +++ b/jevdual/docs/experiments/SIM11-workbench-restarts.md @@ -0,0 +1,27 @@ +# SIM11: Mid-job process restart and browser recovery + +Hypothesis: the retained queue, exact command journal and historical body receipts preserve a camera-guided job across process restarts, including after a dispatch reply is lost, without replaying actions. + +Predictions if true: restarting the workbench while planning cancels its owned model process and leaves the retained job paused. A visible body outage during planning pauses and cancels the host; reboot preserves body identity and motion state while revoking old leases. Restarting either process with an uncertain dispatch preserves the exact pending command, body and application identities and client send count. Receipt recovery reads the original result, requires explicit Resume and obtains a fresh TCP image before the next decision. + +Predictions if false: a restart silently resumes planning, repeats an uncertain command, resets the body or loses its identity, releases an unknown outcome without proof, leaves an orphan model process or completes a job without measurement and reset evidence. + +Design: preserve the current paused completed product. Add a separate owned restart harness and independent scorer around the unchanged TCP body, link and browser workbench. Use two new actual Codex jobs in a fresh trial root. On job one, stop/restart the workbench during model planning and later while a dispatch is uncertain. On job two, stop the body during model planning long enough to observe an outage, restart it, and later stop/restart it while a different dispatch is uncertain. All product recovery actions use the real browser. Harness process controls use exact owned PIDs, retained roots and ports, checked public-state predicates and independent journal snapshots. Graceful process termination is the tested interruption; process kill during native file persistence is outside this trial. + +Primary metric: all four interruptions recover visibly without another send of the interrupted command, followed by two independently verified continuous native placement/measurement/contact-reset cycles on one body. + +Secondary checks: stopped workbench model processes are reaped; body epochs advance and stale leases cannot dispatch; restart is paused or blocked until explicit recovery; retained camera frames cannot grant new authority; a fresh observation precedes the next decision. Reload retains final results. Existing restart conformance and affected guards remain passing. + +Guardrails: commit predictions before tests, freeze all measured source before model calls, preserve failing attempts, keep original body/core/client guard source unchanged unless a demonstrated defect requires a separately preregistered repair, use bounded owned process controls, never initialize a retained body again, never replay an uncertain request, no private pose in model inputs and no hidden automatic Resume. Independent native scoring runs only after all trial processes stop. No new remote access or hardware actuation. + +Decision rule: report restart recovery verified only if all four product interruptions and the independent scorer pass. If any boundary fails, retain its evidence and report the exact mismatch without promoting unaffected checks to a full pass. Hand off a paused connected product only after successful scoring; otherwise return to the preserved product. + +Scope: two actual-model jobs, graceful owned process restarts, same-Mac loopback simulation. Abrupt power loss and torn native writes remain untested. + +## Amendment · 2026-10-12 13:58 + +Read-only review found that a fast body reboot can fall between health polls: unchanged body identity with a new epoch currently leaves the host running and permits lease refresh around an old model decision. Add a real-socket reproduction before repair. A changed known boot epoch must invalidate the model decision and pause before a new command admission, including when the lease-acquisition read sees it before the health worker does. Pending historical commands must remain recoverable across that epoch change without lease rotation or replay. The live body-planning restart will also challenge the fast path; retained body identity and motion state remain intact. + +## Result · 2026-10-12 14:25 + +Held for all four graceful restart boundaries on the first frozen actual-model/browser trial at `d7072107d576d7a09643229ae584a3864d2fe0f6`. Workbench and body were each stopped/restarted during planning and with a different uncertain dispatch. Both identities, continuous native motion, exact pending commands and client sends survived; receipt recovery read historical proof without replay and explicit Resume preceded fresh observations. Two jobs completed with independent native, SQLite and contact-reset checks. All seven trial processes exited before scoring; reload and paused handoff preserved 83 commands. Conformance passed 103 checks. A separate fast-reboot regression failed before the epoch repair and passed afterwards. The actual body outage was visible; fast missed-poll behavior was covered by socket regressions. One additional health timeout cancelled a model call; explicit Resume completed the reset without replay. Its cause remains unresolved and its evidence is retained. An earlier abbreviated-SHA launch was refused before any process or model call. See [report](../../results/workbench-restarts-20261012/report.md), [restart score](../../results/workbench-restarts-20261012/restart-score.json), [attempt ledger](../../results/workbench-restarts-20261012/attempt-ledger.json). diff --git a/jevdual/docs/experiments/SIM12-workbench-health.md b/jevdual/docs/experiments/SIM12-workbench-health.md new file mode 100644 index 0000000..ac21a15 --- /dev/null +++ b/jevdual/docs/experiments/SIM12-workbench-health.md @@ -0,0 +1,21 @@ +# SIM12: Healthy work must survive a delayed health read + +Hypothesis: the workbench interprets a health-read deadline as confirmed body loss too early, cancelling otherwise healthy image-guided jobs. A bounded probe that distinguishes delayed observation from confirmed unsafe state can preserve progress without granting authority from old health evidence. + +Predictions if true: a live, unchanged body with a delayed health reply reproduces the original pause; a mechanism-level repair avoids that pause while preserving command admission checks. Identity/source changes, boot changes, unresolved effects, explicit disconnect, Pause and genuinely unavailable transport still suppress successor actions. An uncertain dispatch remains single-send until exact historical receipt recovery and explicit Resume. + +Predictions if false: the failure is caused by an actual body outage, already-closed ownership, a stale overlapping probe or an invalid body state; the repair hides loss, delays invalidation of known unsafe identity/epoch, grants admission on stale health, retries an effect or silently resumes a paused or uncertain job. + +Design: retain the previous paused product and SIM11 evidence. Inspect historical event timing and source independently for health-decision, transport/locking and probe-cost explanations. Record direct-body versus link probe timings and narrowly scoped instrumentation if needed. Commit a concrete causal prediction before its reproduction and repair. Reproduce through real sockets, preserve original failing output, repair the smallest demonstrated mechanism and run affected outage/reboot/uncertainty/pause tests. Freeze measured source before actual Codex calls. Drive two successive jobs in the browser with deterministic delayed health replies using an owned diagnostic harness; include explicit loss and exact receipt recovery. Independently verify placement, measurement, contact reset, model images, command sends and complete process cleanup. + +Primary metric: two independently verified actual-model jobs complete with zero unrequested pauses or cancelled decisions caused by the tested healthy health delay; injected health-delay intervals cannot admit commands without fresh pinned proof. + +Secondary checks: real unavailable transport still blocks effects; fast reboot invalidates old decisions; stale probes cannot overwrite newer controls/results; unknown commands remain uniquely indexed; receipt recovery stays read-only and requires Resume; UI accurately distinguishes waiting for health proof from disconnected or uncertain work. + +Guardrails: no retries of effect commands; no stale-image or stale-health authority; no relaxed identity/source/lease/freshness limits; no threshold increase to mask an unexplained delay; bounded owned probes and processes; no new remote exposure or hardware actuation; preserve failed attempts; commit predictions before substantive tests and freeze paid runs. Report uncertainty about the historical incident separately from a reproduced mechanism. + +Decision rule: adopt only after the original healthy-delay reproduction passes without unrequested Pause, recovery guards pass and the frozen browser/model trial independently passes. Otherwise retain evidence and report the exact unresolved boundary. + +Scope: same-Mac loopback simulation. The observed SIM11 health timeouts are historical evidence; their precise physical scheduling cause is not assumed. + +Result: held for the tested mechanism and scope. Frozen source `37ebfdb3054b977149d7b60b46254ddd852b749a` completed two actual image-guided jobs under 1.2-second auxiliary health delays, with zero unexpected halts or cancelled model calls. Independent scoring verified fresh pinned proof before all 83 commands, exact receipt recovery without resend, native placement, measurement and contact reset. Original runtime and probe-backlog failures remain retained; 117 distinct conformance checks passed. The historical scheduling cause remains unresolved. See [report](../../results/workbench-health-20261012/report.md). diff --git a/jevdual/docs/experiments/SIM13-workbench-hard-crashes.md b/jevdual/docs/experiments/SIM13-workbench-hard-crashes.md new file mode 100644 index 0000000..855aeac --- /dev/null +++ b/jevdual/docs/experiments/SIM13-workbench-hard-crashes.md @@ -0,0 +1,21 @@ +# SIM13: Hard crashes at command boundaries + +Hypothesis: the durable command identity, request, admission and receipt records let the simulated body and workbench recover from abrupt process death without replaying effects. A crash that lacks conclusive receipt evidence must remain blocked, regardless of what the camera appears to show. + +Predictions if true: SIGKILL at controlled in-flight and uncertain-command boundaries preserves the original command identity and single send. Completed retained receipts can be read after restart and leave the workbench paused until explicit Resume obtains fresh evidence. Incomplete native/effect persistence cannot silently become successful or autorun. Restart invalidates earlier leases and decisions, owns or fences interrupted child processes and does not reuse cached health or images as authority. + +Predictions if false: stage association disappears, requests or outcomes diverge, an action is resent, orphan processes continue writing across restarted ownership, incomplete evidence is promoted to success, a corrupt root starts runnable or the browser cannot explain the blocked recovery boundary. + +Design: inspect body and workbench persistence and process ownership first. Commit causal predictions before targeted crash regressions. Preserve the previous paused product. Build an owned diagnostic harness that synchronizes with durable command boundaries, records exact pre-crash expectations and sends SIGKILL only to its registered process or proven owned process group. Retain every meaningful failed attempt. Freeze measured source before actual Codex calls. Drive actual image-guided jobs and crash/recovery through the browser, with an independent post-stop scorer checking exact commands, sends, receipts, native results, child cleanup and visible paused/uncertain/blocked transitions. + +Primary metric: every tested hard-crash boundary has zero replayed actions and zero successor admissions before sufficient historical proof and explicit Resume; recovery succeeds only where proof exists. + +Secondary checks: retained command identity and request bytes; changed boot invalidates stale lease/decision; no surviving owned planner/native writer crosses the restart boundary; missing or inconsistent proof remains visibly blocked; completed jobs retain measurement and reset evidence. + +Guardrails: loopback simulation and fresh owned roots only; no hardware or existing project processes are crash targets; no retries of effect commands; no inferred success from camera position; no relaxed source, identity, freshness, lease or uncertainty checks; narrowly scoped diagnostic synchronization cannot grant authority; no manual pose correction; actual-model runs use committed source; independently score stopped evidence before live handoff appends events. + +Decision rule: adopt only independently verified recovery behavior. A safely blocked incomplete command is a valid safety result and an explicit usability limitation, not successful recovery. Do not claim power-loss durability or arbitrary crash-point qualification from a finite set of process-kill tests. + +## Result + +Held for four selected boundaries on frozen source `4e2dd74d27ec07f0b5a42039e0f30016402cf050`. Three actual image-guided jobs recovered their exact historical receipts after SIGKILL and completed measurement/reset; the saved-native-state-before-receipt case remained blocked. All 127 commands in the qualified runs had one wire send and fresh admission proof; 41 model decisions were bound to exact TCP images and lower intents/results. Twenty-two new conformance checks passed. Two additional native-gap attempts were not qualified and remain reported: a missing-database scorer failure and a capture-root mismatch. An event-publication crash window also remains an explicit recovery limitation. See [report](../../results/workbench-hard-crashes-20261012/report.md). diff --git a/jevdual/docs/experiments/SIM14-reusable-body-recovery.md b/jevdual/docs/experiments/SIM14-reusable-body-recovery.md new file mode 100644 index 0000000..e7dff13 --- /dev/null +++ b/jevdual/docs/experiments/SIM14-reusable-body-recovery.md @@ -0,0 +1,39 @@ +# SIM14: reusable body contract and interrupted host recovery + +## Hypothesis + +The workbench can repair a journal event published before its manifest index without replaying an effect. Durable planner ownership and a restart fence can retire a planner left by host SIGKILL before admitting fresh planning. A reusable body envelope contract can be separated from bench operations while preserving existing wire validation and recovery guards. + +## Predictions + +If true, an owned event-publication SIGKILL restarts paused with a contiguous validated journal, preserves command identity and native evidence, and resumes using a fresh observation. An active-planning SIGKILL retains the unfinished job, prevents its old result from dispatching, retires its recorded process ownership, and requires explicit Resume. Known uncertain commands recover by their exact receipt with one send; incomplete lower evidence remains blocked. A second minimal operation contract validates through the shared envelope seam without importing simulation modules, while invalid bench commands remain refused. + +If false, journal files collide or disappear, partial data grants action authority, an abandoned planner survives with writable call ownership, an old decision becomes dispatchable, a command is replayed, or extraction changes body identity/lease/receipt semantics. + +## Design + +Use fresh run-owned roots under tools/physical-workcell/output. Preserve the previous product roots. Reproduce the event/index gap before changing its regression expectation. Exercise an actual child planner at a deterministic barrier with external host SIGKILL; cover malformed ownership, process identity mismatch, and incomplete journal tails without killing unrelated processes. Run focused existing body/client/operator/process suites, plus new negative contract checks. Freeze committed source before actual Codex trials. Drive publication and active-planning recovery in the browser and retain UI, journal, command-send and process ownership evidence. Recheck an uncertain receipt case and the native-receipt-gap refusal on the changed implementation. Diagnostic controls must not bypass effect admission. + +## Primary metric + +Zero repeated effect dispatches and zero stale planner admissions across the selected interruption/recovery trials, with every original uncertain command retained and reconciled or blocked. + +## Guardrails + +No pose-based success inference, silent resubmission, automatic Resume, weakened source pinning, unrelated process termination, or mutation of previous trial roots. Retain failed trials. Loopback simulation and process SIGKILL only; no claim of physical safety, power-loss durability, remote authentication or arbitrary-instruction crash completeness. + +## Decision rule + +Adopt only if focused regression and malformed-evidence checks pass and both new browser recovery cases preserve their original history without replay. Report any unsupported boundary explicitly. Separate contract reuse qualification from hardware support. + +## Amendment before final-source qualification · 2026-10-12 22:45 + +The first publication run qualified its frozen source, and a planning run remains unqualified after a UI/readback timing race. The actual planning process tree included Codex shell-snapshot descendants outside the main planner group. Final qualification disables that startup feature and accounts for every recorded child identity and group before fresh planning. Existing single-send, exact-receipt, independent image and native measurement/reset criteria remain unchanged. The interrupted call and earlier attempts remain separately reported. + +## Result + +Adopted for the selected simulated boundaries. On committed source `c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54`, all three final browser cases passed independent post-stop scoring. Two completed measurement/contact-reset cycles and one intentionally blocked native receipt gap retained 29 actual image decisions, one interrupted reservation and 91 commands, each with one wire POST. Retained development/regression logs cover 152 unique passing tests. The shared client was also exercised with a second simulated camera-pointing vocabulary over TCP. + +The initial expected blocked-stdin failure did not reproduce on this Mac; the improved path was tested under partial input-delivery backpressure. Actual CLI startup included transient node/git groups after shell snapshots were disabled; every captured identity/group was absent before continuation. Unobserved detachment, arbitrary instruction windows, power loss and real hardware remain outside qualification. A prior passing publication predecessor and an unqualified planning UI/readback race are reported separately. + +Evidence: `jevdual/results/reusable-body-recovery-20261012/report.md`, `summary.json`, the three case score files and `attempt-ledger.json`. Saved qualification describes stopped trials before the separate live handoff. diff --git a/jevdual/docs/experiments/SIM2-image-guided-workcell.md b/jevdual/docs/experiments/SIM2-image-guided-workcell.md new file mode 100644 index 0000000..4402b64 --- /dev/null +++ b/jevdual/docs/experiments/SIM2-image-guided-workcell.md @@ -0,0 +1,37 @@ +# SIM2: Image-guided block placement and conditional computer action + +Mode: bounded simulated camera feedback with an AI agent, not hardware or general visual-model qualification. + +## Hypothesis + +An agent receiving only rendered RGB images, known camera calibration and work-order intent can locate the block, choose bounded pusher moves, observe a remaining placement error and correct it. A fresh image-derived placement check can gate the computer action without exposing simulator block coordinates to the planner. + +## Predictions + +Use the unchanged SIM1 dynamics/controller and add an isolated image-guided wrapper in tools/physical-workcell. A fixed top-down orthographic simulated camera provides a simple known pixel-to-table calibration. The current Codex agent views RGB images and supplies digest-bound structured proposals identifying the visible block box and a move or completion decision. No model API or credential setup is required for this agent-driven trial. + +Every proposal binds the run, image SHA256, generation and exact token. A conservative pixel detector checks that the proposed box matches the visible orange block and derives its image-plane center; this is narrow color-specific visual support, not a general recognizer. Images with missing, ambiguous, off-frame or occluded target evidence refuse. The motion controller admits only bounded forward corrections with fresh evidence and existing SIM1 workspace/force/step limits. Completion requires the image-derived center to be inside the target zone with a conservative margin, then also requires SIM1's native-state predicate and Footwork's saved-byte check. Ground-truth block poses remain private to the simulator and a post-run evaluation record, absent from the agent-facing image packet and action feedback. + +Predicts: at least one actual contact move leaves a visible residual error, followed by a fresh-image corrective move; final image-derived placement and separate native oracle agree. Early visual completion, stale/tampered images, wrong block boxes, no-contact motion, altered tokens and repeated/unknown actions cannot produce an unqualified successful computer result. Runs with ambiguous visual evidence stop rather than use hidden positions. + +Against: native coordinates supplied to the planner or used to choose its correction; preselected targets masquerading as AI image decisions; a color/box check accepting blank or mismatched images; completion without fresh visual evidence; lost or repeated proposal causing another effect; failure to demonstrate correction or agreement with the separate native oracle. + +## Design and checks + +Prediction is committed before source tests. Parent owns preregistration, notebook, source freeze and independent public AI-driven trial; project worker owns image wrapper, schema, conformance tests and README. Keep SIM1 source/model unchanged. The wrapper renders a separate calibrated view from retained native integration state, without changing physics. Retain image/request/decision/receipt hashes under private ignored output, single owner per workcell. No real camera acquisition, physical actuation, provider call or external project write. + +Primary metric: binary demonstration of image-located contact movement, observed residual error, image-informed correction, final fresh visual check and exactly one separately checked local work-order result. Verify public interface across fresh processes and scoped negative/failure paths. Freeze implementation/camera/detector/schema/settings before the measured run. No claims of visual-model accuracy from one agent-led trial; the detector uses known object colors and a known camera. + +Decision: adopt only as an experimental image-driven body interface if the complete loop and refusal boundaries pass. Keep perception/model intent, controller effects, image verification, native oracle and saved software evidence separate. On failure preserve the evidence and append a dated amendment before a new measured source version. + +## Result + +Held for the bounded agent-driven simulated route. The final version at `f96ac4a` passed a fresh parent trial: six viewed RGB frames, five image-bound moves, a visually short placement, a smaller corrective move and fresh visual completion. The separate post-planning native oracle agreed with the visual placement, and an independent public Footwork check verified the local work-order file. Wrong-object/stale/short-completion and injected packet cases refused; repeated commands left all run artifacts unchanged. The combined scoped suite passed 26 actual-engine/renderer tests. See `results/image-guided-workcell-20261010/report.md`, versioned trial summaries and verification. + +This establishes one known-color, calibrated simulation loop driven by the current AI session. It does not establish an unattended model runner, general recognition accuracy, real-camera calibration or hardware transfer. Version 1 is preserved separately; the dated amendment below records the source hardening before the final trial. + +## Amendment · 2026-10-10 15:54 + +Version 1 at `fbc9815` completed the parent AI image-guided trial: five bounded image decisions, residual placement error at generation 4, a smaller correction at generation 5, fresh visual completion and separate native/file checks. Preserve it as `results/image-guided-workcell-20261010/trial-v1.json`. + +Source review identified an additional cached-packet boundary: the reader validates known fields but returns the original dictionary, so an injected extra field could enter planner evidence. Before adopting version 2, reject unknown packet fields and any changed perception-scope marker. Add forged-oracle-field and altered-scope conformance cases; commit the amendment before these tests. Keep dynamics, camera, detector thresholds, control settings and target intent unchanged. Freeze the revised source and repeat the parent image-driven public trial on a new run; earlier run evidence remains version 1 and is not silently upgraded. This is schema hardening, not tuning from a failed placement. diff --git a/jevdual/docs/experiments/SIM3-repeatable-visual-work-orders.md b/jevdual/docs/experiments/SIM3-repeatable-visual-work-orders.md new file mode 100644 index 0000000..71afcca --- /dev/null +++ b/jevdual/docs/experiments/SIM3-repeatable-visual-work-orders.md @@ -0,0 +1,57 @@ +# SIM3: Repeatable visual work orders and a fresh host + +Mode: source-frozen small diagnostic comparison, not a statistical model benchmark or robot qualification. + +## Hypothesis + +Separating RGB acquisition from interpretation and binding independently specified episodes will let a fresh image-capable host use the public workcell tools on varied work orders. Both a fresh host and a deterministic RGB controller can complete reachable ordinary jobs; recognition/calibration challenges remain visible and unresolved rather than falsely completed. The comparison can identify whether movement needs an AI planner in this bounded setting. + +## Caller and scope + +The job is: move the known part into the requested staging region, inspect placement, and record completion only when supported; otherwise explain the blockage. Keep the implementation under tools/physical-workcell. Add immutable case specifications, an episode lifecycle/evaluation CLI and a deterministic RGB baseline. Do not add a model API, second agent framework, robot semantics to the production capability service, persistent controller process, application fixture, Rerun dependency or new hardware. + +The host is a fresh Codex agent context, with no parent conversation, previous trajectories, baseline code/results or native state. Supply public tool documentation, case paths/digests for opaque initialization, exact owned run/output paths and the task. It may inspect returned PNGs and pixel-based perception summaries, submit proposals and explain unresolved jobs. It must not inspect raw case configurations, native state/scene/source, evaluator data or earlier outputs. This is instruction-based separation on a shared filesystem, not an adversarial sandbox claim. The host owns planning; Footwork does not gain an internal model runner. + +## Cases and freeze + +The parent owns six qualification cases in tools/physical-workcell/cases/sim3-suite.json. Freeze them and their hashes before implementation measurements. The implementation worker must not inspect or tune against these cases; use separate development fixtures. Ordinary cases vary initial block x/y and requested zone. Challenge cases specify low illumination, a static render-only occluder or camera displacement. Initialization may set the case's initial state once; action must still move through native contact and must never teleport. Camera displacement invalidates the existing calibration and must not silently receive a corrected transform. + +Base dynamics, detector thresholds and controller limits remain unchanged. Model/case/source/order/calibration/action bounds bind the episode. Different case or source bindings refuse reuse; no implicit reset. Valid acquired images persist even when perception is ambiguous/unavailable. Recognition status is separate from image availability; unsupported evidence still blocks action. + +## Measurement + +After scoped development/conformance, freeze source before initializing six fresh baseline runs and six fresh-host runs. Use the same ordered cases, observations and admitted actions. One fresh host processes the set; within-set learning is allowed and disclosed. Run each case once per participant for this adoption; retain setup failures, refusals, blocked outcomes and interventions. No corrective hints, repeated episodes or tuning to qualification failures. Capture each participant's per-episode elapsed time from initialization to declared finish, including its runtime/tool latency; elapsed values are descriptive, not a controlled performance benchmark. + +Primary metric: completed ordinary physical-and-local-computer jobs per three attempted ordinary cases for each participant. Also report completed/all-six, unresolved, setup/driver failures, interventions and incorrect completion claims. Count every case. Independently score final native state and exact saved software artifact after the participant finishes. Capture the visual verdict durably before consulting native truth on each completion attempt, so an oracle-blocked visual false positive remains visible. Scene satisfaction alone does not create historical motion or software receipts. + +The deterministic baseline uses only the same RGB-derived boxes/calibration/task intent and forward bounds; it receives no native pose feedback. It is a serious comparator, not a replayed target sequence. The fresh host's actual image views, decisions, finish declarations and explanations are retained separately from conformance tests. + +## Checks and decision + +Predictions: all valid rendered frames can be inspected despite detector failure; each case/source binding is enforced; native poses never enter host packets; completion verdicts precede native checks; ordinary jobs can finish through contact and a saved result; failed perception/calibration cannot complete; repeated/unknown effects do not replay. Independent scorer catches a declared completion without qualified physical/software evidence. Cold-host tool discovery/readiness and refusal comprehension are observed, not inferred from source. + +Against: detector failure discards a valid frame; invalid calibration admits motion; author/native-state guidance chooses host targets; missing cases vanish from totals; simulator protection conceals visual false-positive verdicts; a satisfied current scene manufactures execution history; repeated effects or unsupported success claims. + +Conformance must cover capture with unavailable/ambiguous perception, binding/tamper/freshness, changed initialization, invalid calibration, pre-oracle verdict ordering, budgets, false participant claims and existing SIM1/SIM2 regressions. Source/case settings changes start a new measured version; preserve earlier failures. Check notebook and redact committed results. + +Decision: adopt only the bounded episode/evidence interface supported by the observed run. If the deterministic controller performs movement equally well, retain it for that role; do not infer AI value from successful movement alone. Independent controller recovery and real application handoff remain subsequent experiments. + +## Result + +Frozen runtime b9941ef: both participants completed 3/3 ordinary jobs and 3/6 total jobs. +Each declared the three challenged cases unresolved, retained inspectable images and +executed no motion on those cases. Independent scoring found no incorrect completion +claims or visual false positives in the twelve attempted episodes. These observations +support the bounded episode/evidence interface; they do not establish general reliability +or an AI movement advantage. + +The fresh host inspected 19 images and reported zero external interventions. Its first +doctor-output redirection failed because the destination directory did not exist. It +corrected that itself, but doctor actually ran after the first initialization, before +the first view. Strict doctor-before-init and clean-machine setup are unqualified. +Role separation was instruction-based on a shared filesystem, with a post-trial self-audit. + +All six cases were run once per participant with no qualification tuning or reruns. +Scoped conformance: 50 actual-engine/renderer tests and Ruff passed. Full evidence, +descriptive elapsed times, retained failures and subsequent scope are in +[the report](../../results/repeatable-work-orders-20261010/report.md). diff --git a/jevdual/docs/experiments/SIM4-independent-controller-recovery.md b/jevdual/docs/experiments/SIM4-independent-controller-recovery.md new file mode 100644 index 0000000..d42f5e6 --- /dev/null +++ b/jevdual/docs/experiments/SIM4-independent-controller-recovery.md @@ -0,0 +1,89 @@ +# SIM4: Independent simulated controller recovery + +Mode: headless local diagnostic and actual-process fault conformance; no robot qualification. + +## Hypothesis + +A separately running controller can finish bounded image-bound operations after its caller +disconnects, and a returning caller can reconcile durable history without repeating motion. +Cancellation requests can be distinguished from verified controller stop; restart changes +incarnation without inventing missing execution history from a currently satisfied scene. + +## Scope and caller + +Keep this under tools/physical-workcell in Python with the pinned native MuJoCo engine. +An initialized SIM3 episode is owned exclusively by one controller process. Local private +Unix-socket clients use health, observe, submit, status and cancel. Submission binds a safe +operation ID, exact retained proposal bytes/digest, frame generation and source/case identity. +Only the existing image-guided gate admits motion or conditional local software staging. +The service persists acceptance before acknowledging it and dispatch intent before execution. +The caller may disappear without owning the controller's lifetime. + +Use an owned private controller directory separate from the episode's initialization files. +Hold the existing episode lock for the controller's lifetime so legacy commands cannot race +the service. Keep observations free of native poses. Preserve SIM1-SIM3 behavior by default; +add narrowly scoped cooperative cancellation/pacing hooks rather than another physics policy. +Controller status reports historical outcome separately from current evidence. + +## Cancellation and restart + +Before dispatch, cancellation can prevent the operation. During motion it stops advancing the +target and holds the last commanded actuator target, then performs bounded native settling. +No block teleport, velocity assignment or state reset is allowed. A terminal cancelled result +requires retained state and stop evidence; failure to establish stop remains unknown. Terminal +completion wins a late cancellation race. Computer publication cannot be undone by cancellation. + +Each process has a new incarnation. On restart, terminal retained results remain historical; +an incomplete controller record may be reconciled only from an exact bound lower-layer +terminal receipt. Missing/unknown execution evidence remains unknown and blocks further +effects. Accepted-but-undispatched work is never silently replayed. Current placement alone +cannot manufacture a historical result. Source or case changes refuse reuse. + +## Checks and measurement + +Primary metric: required process-level lifecycle scenarios whose observed state and receipt +history match their expected outcome, with all attempts and failures retained. Cover: + +1. Disconnect after durable submit; reconnect to the same ID after completion; no replay. +2. Return while motion is active; observe reports busy and status remains nonterminal. +3. Cancellation before dispatch prevents motion and increments no native generation. +4. Cancellation during native movement reaches a bounded verified stop or stays unknown. +5. Lost controller reply/result after a lower-layer terminal receipt reconciles on restart. +6. Kill the controller during motion; new incarnation retains unknown history and refuses replay. +7. Current scene satisfies an order but execution receipts are unavailable; no success claim. +8. Normal image-guided contact and verified local artifact completion through the service. + +Challenge malformed/conflicting IDs and proposals, stale bindings, second owner, changed +source/case and terminal cancellation. Run existing physical-workcell regressions and lint. +Use separate private development cases. Freeze the runtime before the parent independently +drives a new diagnostic set through public clients; do not call these heldout/model benchmarks. +All sleeps/waits are bounded; retain owned process handles and clean up only those resources. +No visible desktop, live camera, model API, installs, production capability-service changes or +external application writes are required. Screensaver and sleep behavior are not qualifications. + +## Decision + +Adopt the controller lifecycle only if independent process runs demonstrate caller-independent +execution, no duplicate movement, truthful cancellation and conservative restart history. +Otherwise preserve failures and narrow claims. Actual application handoff and physical adapter +promotion remain separate subsequent packages. + +## Result + +Frozen runtime 5713173 passed 68 scoped tests and Ruff. Nine independent public-service +checks plus a separately preregistered native-step observation probe support all eight +required lifecycle scenarios. Caller loss did not repeat effects; cancellation retained +verified simulated stop evidence; restart reconciled intact lower receipts and kept missing +history unknown. Deliberate current-artifact tamper preserved historical staging while +blocking new effects. All owned trial processes exited and sockets were removed. + +The first development implementation failed targeted recovery/binding/ownership checks; +those failures and the corrected prediction are retained. The original timed crash trial +did not prove native stepping; a fresh supplemental case confirmed a real native step +before SIGKILL without changing source/dynamics. Controlled record/history/timing faults +are distinguished from live disconnect and kill. This is process-level simulation evidence, +not hardware, power-loss, AI superiority or application-workflow qualification. + +See [report and complete limits](../../results/independent-controller-20261010/report.md), +[qualified evidence](../../results/independent-controller-20261010/qualification.json) and +the engineering notebook for failed and revised predictions. diff --git a/jevdual/docs/experiments/SIM5-versioned-application-fulfillment.md b/jevdual/docs/experiments/SIM5-versioned-application-fulfillment.md new file mode 100644 index 0000000..f29cd06 --- /dev/null +++ b/jevdual/docs/experiments/SIM5-versioned-application-fulfillment.md @@ -0,0 +1,101 @@ +# SIM5: Versioned browser work-order fulfillment + +Mode: local application/physical-controller integration diagnostic, not hardware or market qualification. + +## Hypothesis + +A browser work order bound to an immutable application identity, order ID and revision can +be fulfilled through the image-guided simulator, then updated only when exact controller +evidence and the current application revision agree. Changed/cancelled orders and lost +application replies can remain truthful without duplicate fulfillment or invented physical +completion. + +## Scope and interface + +Keep a Python/SQLite local app, plain browser UI and a handoff CLI under tools/physical-workcell. +Reuse the frozen controller's public commands; do not change controller/body/vision semantics +or add robot capability-service protocols. The app binds only on IPv4 loopback and persists +an immutable app identity plus source/store binding. SQLite owns transactional order revision, +leases and idempotent fulfillment receipts. Order and mutation schemas are exact and finite. + +The browser reads an order and binds its current revision. A lease retains the exact work +order snapshot/digest, app identity, operation ID and assigned private episode/controller +paths. The adapter prepares a case from that snapshot and explicitly bound scene input, +starts or reconnects an owned controller and exposes observe/submit/guard/status through its +public interface. The host retains target planning; no internal model API is added. + +The browser fulfillment action references the lease and a stable completion operation ID. +The server independently checks the assigned controller's exact staged receipt, fresh +observation/task binding and current saved artifact evidence. Client-supplied success flags +or arbitrary receipt paths cannot grant completion. After physical verification, a SQLite +compare-and-swap requires the leased order to remain open at the expected revision. The order +update and software effect receipt commit in one transaction. Readback targets that same +app/order/operation identity and survives restart. + +Each browser-origin write checks loopback/Origin and exact fields. No remote service, +production data, credentials, paid model call or external application write is required. +Browser use is through the host's available browser driver; Footwork's controller/receipt +and saved-byte checks supply the physical connection. Do not bypass a Footwork judge or +claim this qualifies its production browser/capability governance. + +## Changes, cancellation and unknown replies + +Editing/cancelling an order increments its revision. Guard detects a changed/cancelled +lease, withholds further submissions and requests cooperative cancellation for an active +bound controller goal. Motion already admitted may partially execute; cancellation must +not be treated as rollback. Server completion still refuses a stale lease even if a host +misses the guard. No atomic physical/software transaction is claimed. + +A lost fulfillment reply is reconciled by exact operation readback. Identical operation +identity returns the committed receipt and current order; conflicting reuse refuses. +Missing current physical/software evidence stays unavailable. Historical application +fulfillment remains distinct from later physical/artifact integrity. + +## Checks and measurement + +Primary metric: required integration scenarios whose persisted app result and controller +history match the expected outcome, with all attempts and failures retained. Cover: + +1. Actual browser read/bind, RGB-guided contact fulfillment, browser completion and saved + application readback for the same identity/revision. +2. Order changed during admitted motion: guard stops further work, stale fulfillment refuses. +3. Order cancelled during admitted motion: partial movement remains explicit, app stays cancelled. +4. Lost application reply after committed completion: exact readback/repeat creates one effect. +5. Wrong order/app/lease, conflicting operation identity and unsupported physical evidence refuse. +6. Application/controller restart preserves binding/history without redispatch or implicit reset. +7. A staged receipt with altered/missing current artifact does not authorize new app fulfillment. + +Run meaningful backend/adapter conformance, affected physical-workcell regressions and lint. +Freeze source before independent integration trials; retain browser observation/screenshots +and backend persistence readback separately. Use owned private fixtures and processes, +preserve failures and qualify any driver gaps. Recheck screen lock before desktop-dependent +verification; continue implementation/headless work if it locks. Installation downloads are +already authorized. Do not alter system lock/power settings or use personal browser profiles. + +## Decision + +Adopt only the bounded local application handoff supported by independent evidence. Keep +the deterministic controller for movement and the agent responsible for orchestration. +Hardware transfer, real operator demand and production adapter promotion remain subsequent work. + +## Result + +The first implementation failed the wrong-app mutation requirement in development; +the failure is retained and notebook SIM5-R revises its prediction. Corrected frozen +implementation `bd896e1` passed the actual isolated-browser read/bind, image-guided native +contact, verified browser fulfillment and persisted readback. All seven independent +public-API integration scenarios passed on first attempts with unchanged source. +The 95 affected checks, source fingerprints, failed development attempts, separate +browser evidence, SQLite effect counts and cleanup are recorded under +`results/versioned-application-fulfillment-20261010/`. Adopt the bounded local experimental +handoff; hardware, persistent shared-body orders and production capability promotion +remain outside this result. + +## Amendment · 2026-10-10 19:38 · before independent qualification + +Development conformance found reply-only application identity checking insufficient: a +missing or foreign expected app identity could mutate an order before the browser checked +the reply. Require one exact `X-Footwork-App-ID` on every mutation, validated before any +effect. Original and corrected development evidence are retained; notebook SIM5-R revises +the failed first implementation prediction. Freeze corrected runtime before independent +browser/public-API trials. The seven scenarios, primary metric and decision remain unchanged. diff --git a/jevdual/docs/experiments/SIM6-reusable-bench.md b/jevdual/docs/experiments/SIM6-reusable-bench.md new file mode 100644 index 0000000..7329d7f --- /dev/null +++ b/jevdual/docs/experiments/SIM6-reusable-bench.md @@ -0,0 +1,39 @@ +# SIM6: successive physical-computer experiments on one simulated bench + +## Question + +Can one persistent MuJoCo body support image-guided preparation, a separately executed computer measurement, contact-driven reset, and a subsequent job, including recovery from an unexpected block movement? + +## Hypothesis + +Separating persistent bench identity and state generation from job identity, and requiring fresh image-bound effects, permits successive jobs without reinitializing the body. A color-specific RGB host can reobserve after a diagnostic force disturbance and resume preparation instead of acting on stale placement evidence. + +## Predictions + +If true, two successive jobs retain one bench ID, monotonic native time and generations, each record one independently readable image measurement, and reset through native actuator/contact steps before the next job. A stale move or measurement following a disturbance refuses without its requested effect, then fresh RGB guidance completes and resets the disturbed job. + +If false, reset requires state assignment/reinitialization, a disturbed job cannot recover within its bounded action budget, a stale packet authorizes measurement or motion, or job two loses the state/history of job one. + +## Design + +Add an isolated persistent bench under `tools/physical-workcell/`, reusing the pinned MuJoCo model, bounded native motion, PNG codec and color-specific RGB detector. Preserve the older episode/controller/application experiments. The host sees image packets and task intent, never private native pose guidance. Job phases separate preparation, measured result, resetting, and completed reset. A measurement subprocess computes the orange silhouette footprint and centroid from the exact image and records an idempotent job-bound SQLite result. This is a simulated optical measurement, not a calibrated physical instrument. + +Reset routes the pusher around the block, pushes it back to the home region through contacts, then returns the pusher home. Initialization is allowed once; no reset may assign block pose or call `mj_resetData`. A bounded native external-force diagnostic produces an unexpected displacement, persists a new state generation, and is separately labeled intervention rather than planner motion. + +Use private development runs and focused conformance tests. Freeze source before an independent public-interface qualification. Retain all attempts and cleanup evidence. No paid models, hardware, real camera access, cloud deployment or broad UI change. + +## Primary metric + +Number of successful complete job cycles in the preregistered sequence: ordinary job, disturbed job. Success requires exact measurement receipt, independently scored placement and home reset, continuous retained body identity/state, and no unsupported effect replay. Required result: 2/2 cycles and stale-evidence rejection in the disturbed cycle. + +## Guardrails + +One active job and exclusive bench writer; exact source, bench, job, generation and image identity; bounded motion/force/steps and action budget; historical measurement distinguished from current placement; unknown interrupted effects block new dispatch. Duplicate exact effect requests read history, conflicting IDs refuse. No implicit reset on restart or corruption. The independent scorer may inspect native state; the planner may not. + +## Decision rule + +Adopt only as an isolated experimental tool if the primary metric and guardrails hold. A recovery failure is retained and narrows the supported disturbance envelope. This qualification does not establish model-driven planning, arbitrary manipulation, hardware transfer, power-loss safety or production Footwork capability governance. + +## Result + +Frozen source `0091e5f832a3aef24ef73fdc9f1ee76bd64b7e46` passed 2/2 first-attempt cycles with one persistent bench identity and generations 0 to 29 to 70. A 0.1333 m axial disturbance invalidated the previous image; stale movement and measurement refused before fresh RGB guidance recovered placement. Separate native-state and read-only SQLite scoring verified both measurements and contact resets. Fourteen focused final-source conformance tests passed. See `results/reusable-bench-20261011/report.md` and `qualification.json` for evidence and limitations. Adopt only as the isolated experimental bench. diff --git a/jevdual/docs/experiments/SIM7-agent-visual-bench.md b/jevdual/docs/experiments/SIM7-agent-visual-bench.md new file mode 100644 index 0000000..48bb9e5 --- /dev/null +++ b/jevdual/docs/experiments/SIM7-agent-visual-bench.md @@ -0,0 +1,31 @@ +# SIM7: an AI host controls the reusable simulated bench + +## Question and hypothesis + +Can the multimodal AI in this Codex session choose actions from native RGB images, complete two successive prepare-measure-reset jobs on one bench, and recover after an unexpected block displacement while the existing controller guards remain unchanged? + +The hypothesis is that an external visual AI host can use a typed observation/action handoff instead of the deterministic RGB coordinate planner. It receives images, camera calibration, task regions, pusher dimensions and bounded action descriptions. It receives no detector coordinates or private native poses. Deterministic bench code continues to own effects and verification. + +## Predictions + +If true, both jobs complete with one measurement each and contact-driven reset, one bench identity and continuous integration state. A disturbance between the AI's observation/proposal and dispatch causes the original proposal to refuse. The AI inspects a new image, proposes corrective motion and completes the second job. + +Against: the host needs the baseline coordinate planner or native poses; stale evidence dispatches; either job cannot complete within 64 motion actions; recovery requires a reset/teleport or controller guard changes; computer success is claimed without independent persistence readback. + +## Design + +Add a small source-bound external-agent session interface beside the existing bench. The agent observes a saved image, writes a typed proposal with a short action intent and exact observation digest, and dispatches it. Retain proposals, receipts, refusals and fresh observations. Never add an automatic coordinate planner, model-result success flag, provider API or fallback motion path. The existing Codex conversation supplies the actual AI; this is not an unattended API-backed service. + +Freeze the interface source before the AI trial. Run job one with target x in [0.035, 0.105], y in [-0.025, 0.025]; job two with target x in [0.045, 0.115], the same y bounds. After the AI prepares a measurement proposal for job two, the diagnostic harness applies a bounded native external force before dispatch. The stale proposal must refuse. The AI then receives a fresh image and chooses recovery actions. A separate scorer may inspect native states and read-only SQLite only after both jobs finish or the attempt stops. + +## Primary metric and decision + +Primary metric: complete job cycles, required 2/2 on the first frozen trial attempt. Each requires independently verified measurement placement, exact saved result, and contact reset. Disturbance recovery and stale refusal are required guardrails. Retain a failed attempt; any subsequent repair is a separately preregistered version, not a hidden retry. + +## Guardrails and limits + +Keep bench.py, measurement.py, native controller and image gate source unchanged. One writer, typed finite actions, exact observation/source/job binding, retained intent before dispatch, no replay of unknown effects, and independent effect readback. No keys, paid provider calls, real cameras, hardware, production application changes or cloud deployment. The trial establishes this AI session's behavior, not a repeatability rate, a deployed policy service or general robotics. + +## Result + +Frozen `9a5d2f8bfac8088b13c63052887e0c0c2e9d5c7e` passed both first-attempt cycles with the Codex conversation AI authoring 29 proposals from presented images. A prepared measurement refused after a 0.13303 m native-force displacement; the AI observed the changed image, corrected placement, measured and reset. Post-trial native/SQLite scoring confirmed both cycles and exactly two saved measurements. Native guards remained unchanged; 29 focused checks passed. See `results/agent-visual-bench-20261011/report.md` and its structured evidence. Adopt the external visual-agent handoff as an isolated experimental interface, not an unattended policy service. diff --git a/jevdual/docs/experiments/SIM8-bench-operator.md b/jevdual/docs/experiments/SIM8-bench-operator.md new file mode 100644 index 0000000..de1a46a --- /dev/null +++ b/jevdual/docs/experiments/SIM8-bench-operator.md @@ -0,0 +1,43 @@ +# SIM8: unattended visual control and a usable bench operator + +## Question and hypothesis + +Can a local bench-operator product run two successive image-guided jobs using the installed, authenticated Codex CLI for decisions, retain the existing simulation guards, recover from an injected displacement, and expose truthful progress, measurement results and pause controls in the browser? + +The hypothesis is that the external AI handoff can be driven by an independently running model process, with deterministic scheduling, call budgets and result verification. The browser can provide task creation and supervision without creating another physical-authority path. + +## Predictions + +If true, two queued jobs complete on one retained bench, each with one saved measurement and contact reset. A disturbance after a saved second-job measurement decision refuses that stale action; a subsequent image-based model decision recovers. Browser reload preserves job/result state. A pause request during model inference prevents the returned decision from dispatching; resume obtains fresh evidence. Neither the browser nor a model-generated completion flag can bypass the bench. + +Against: chat supervision or a deterministic coordinate planner is needed to complete jobs; stale images dispatch; stopped model replies execute; interrupted effects replay; the UI reports completion without verified reset and saved measurement; native guard source must be weakened. + +## Design + +Add an isolated product beside existing physical-workcell tools: a bounded Codex CLI visual-policy adapter, a durable serialized job runtime, a loopback-only HTTP app and a responsive browser operator. Reuse the current agent and bench APIs unchanged. The model receives the exact PNG, task/calibration and a bounded history of chosen actions/outcomes. Strip detector coordinates and native poses. Codex runs ephemerally with user configuration/hooks, shell/action tools and web access disabled; saved CLI authentication is used, without opening or provisioning keys. Validate structured model output before proposing an action. + +Persist call reservation and dispatch intent before each boundary. Count unavailable/cancelled model calls against the budget; never retry a possibly dispatched physical effect. One active job, at most 32 model calls per job, bounded call timeout, and pause at action boundaries. Pause does not promise mid-motion hardware cancellation. Unknown physical/handoff outcomes block resume; unavailable model output pauses for operator attention. Source drift and restarts do not implicitly replay unfinished work. + +Use stdlib Python and plain HTML/CSS/JavaScript with the existing MuJoCo environment. No new dependencies, cloud deployment, hardware or API-key writes. Operator jobs request an image-footprint measurement with a target region. Preserve all attempts and model logs privately; commit derived reports rather than raw model traces. + +## Qualification and primary metric + +After development conformance, freeze measured source before live model calls. Drive the actual browser to enqueue an ordinary job (region x=[0.035,0.105]) and a disturbance job (x=[0.045,0.115]), both y=[-0.025,0.025]. Inject one bounded native force between the second job's saved first measurement decision and dispatch. Score native measurement/reset states and read-only SQLite after the run, separately from the model. Record every model call, proposal, refusal and cleanup outcome. + +Primary metric: completed queued job cycles, required 2/2 on the first frozen qualification attempt. Stale refusal and fresh-image recovery are required, as are truthful browser readback and pause semantics. Conformance can use an explicitly labelled fake policy to deterministically test failures; that is not model-control evidence. A failed frozen run is retained and reported; any repair needs a new preregistered version rather than a hidden retry. + +## Amendment · 2026-10-11 · version 2 + +Version 1's actual AI/browser workflow completed both cycles, but independent scoring refused its PNG byte size and did not produce a complete score. Preserve that qualification as inconclusive. A read-only review also found a paused queued-job admission race, with cancelled-motion dispatch still guarded. Version 2 repairs the admission check and scorer, explicitly verifies the diagnostic force, more than 2 cm native displacement and exact saved-proposal stale-refusal chain, then repeats the same two-job qualification on fresh committed source. Stop the owned browser/server before scoring to avoid contention. The primary requirement remains 2/2 on the first version 2 qualification attempt; retain all prior calls and failures. + +## Decision and limits + +Adopt only as a local simulated bench-operator prototype if the complete flow and guards pass. Record model/CLI identity, call count, token usage when reported and elapsed time; subscription usage does not supply an independently verified monetary price. This does not establish hardware transfer, broad model repeatability, production security, physical power-loss recovery, billing accuracy or demand. + +## Result + +Version 1 completed both actual AI/browser cycles but its independent scoring failed on live ownership contention and PNG byte size; retain the frozen qualification as inconclusive. The pause admission regression failed before repair, with motion dispatch still guarded. A preregistered version 2 repaired admission and independently checked force/displacement/stale binding. + +Version 2 attempt 1 passed 2/2 on source freeze `1415f594ccc91dbc6f7856ba9b2ab0252d45cfdb`. Native/SQLite scoring verified measurements and contact resets on one body, and the exact immutable chain established a 5.61 cm native displacement, refusal of a saved stale measurement without effect, and fresh-image AI recovery. Actual inference cancellation dispatched nothing; browser reload and server restart retained results without replay. Thirty-one reserved CLI calls produced 30 typed decisions, 29 executed decisions, one stale refusal and one cancelled call. All model processes were reaped. Fifty-four focused checks passed; native guard source is unchanged. + +Adopt as a local simulated product prototype. Evidence, all-attempt ledger and limits: [bench-operator results](../../results/bench-operator-20261011/report.md). Raw model logs and native run directories remain private; hardware and demand remain unqualified. diff --git a/jevdual/docs/experiments/SIM9-network-body-adapter.md b/jevdual/docs/experiments/SIM9-network-body-adapter.md new file mode 100644 index 0000000..6262c78 --- /dev/null +++ b/jevdual/docs/experiments/SIM9-network-body-adapter.md @@ -0,0 +1,23 @@ +# SIM9: Persistent body over a network + +Hypothesis: a durable TCP adapter can separate visual planning from the persistent simulated body while preserving local effect authority under transport failures. + +Predictions if true: exact duplicate commands retain one result and one effect; lost replies require read-only reconciliation; expired leases fence late commands; restart retains body identity and state without replay; insufficient lower-level evidence remains unknown and blocks new actions. + +Predictions if false: network ambiguity permits duplicate motion, stale observations reach effects, restart erases uncertainty or changes body identity, or recovery requires direct native-state manipulation. + +Design: one loopback HTTP body service, separate durable client and fault proxy on this Mac. Keep existing Bench and AgentBench guards unchanged. Pin adapter/body/source identities; persist command intent before dispatch; use server-monotonic leases revoked on every boot. Images cross TCP and are hash checked before model input. This qualifies a same-host network boundary, not authentication, WAN operation or hardware. + +Primary metric: all 12 required mechanical cases pass: successive cycles; bounded delay; drop before acceptance; drop after effect; exact concurrent duplicate; conflicting duplicate; stale observation; delay past lease expiry; identity/source/exclusive lease fencing; service restart after terminal result; restart after lower receipt before transport receipt; unresolved accepted intent plus client restart remains blocked. + +Secondary qualification: two successive actual Codex image-guided jobs on one network body, with at least one lost dispatch reply reconciled without resending. Independently inspect retained native states, measurement SQLite and contact reset receipts after stopping run-owned processes. Conformance planners do not count as AI evidence. + +Guardrails: no direct move endpoint, no teleport, no native pose supplied to the model, no automatic uncertain-command retry, no source edits during frozen qualification. Admission leases do not promise interruption of an already accepted bounded action. Preserve all failures and attempts; paid/model runs start only at committed measured source. + +Decision rule: adopt the adapter as a loopback simulated prototype only if the mechanical matrix and independent model qualification pass. Report partial or inconclusive evidence explicitly; lower-receipt absence remains unknown. + +References: tools/physical-workcell/agent_bench.py; tools/physical-workcell/bench.py; SIM8-bench-operator.md. Local LeRobot reference b9cb121cb7d4e3e26ec5c906d08dda68d151ba52, src/lerobot/async_inference/policy_server.py and robot_client.py, informs remote observation/planning separation. Its observation queue is not used as an effect receipt protocol. + +## Result + +2026-10-11: final-source mechanical matrix passed 12/12; 13 server, 11 client and 29 existing core checks passed. The first frozen actual-model attempt at e8c7e9b passed 2/2 successive jobs with 27 image decisions, 21 movements and 83 bound transport commands. Native/SQLite scoring verified placements, measurements, contact resets and one-body continuity. A lost dispatch reply was reconciled with one GET and no repeat POST; exact concurrent duplication and delayed reply also completed. Accepted intent without lower proof remains blocked after service/client restart. All owned trial processes exited; source remained unchanged. Retained failures and scope limits are documented in results/network-body-adapter-20261011/report.md. Adopt as a loopback simulated adapter. diff --git a/jevdual/docs/experiments/U2-independent-onboarding.md b/jevdual/docs/experiments/U2-independent-onboarding.md new file mode 100644 index 0000000..bee6148 --- /dev/null +++ b/jevdual/docs/experiments/U2-independent-onboarding.md @@ -0,0 +1,33 @@ +# U2: independent first use and permission onboarding + +## Scope + +Prepare an independently runnable qualification packet for the reviewed capability wheel and a concrete disposable macOS VM plan. The user authorized continued qualification and a temporary two-hour caffeinate hold. The environment choice is pending. Existing host permissions, local U1 success and pregranted GitHub runner permissions are not first-use evidence. + +Actual U2 execution requires an independently provisioned Mac or a new SIP-enabled macOS guest, a visible logged-in session and a named responsible launch host. No host permission reset, TCC database edit, SIP change, global install, VM download/creation, model spend, publication or merge is authorized by this preparation. A VM plan is reviewable before those environment actions. Preserve PR2's reviewed artifact and earlier evidence. + +## Hypothesis and predictions + +The wheel and a bounded set of existing model-free qualification helpers can be transported without the Footwork runtime source tree or development environment. A fresh responsible host initially lacking Accessibility is blocked before fixture creation/input; an explicitly approved grant and host restart permit the same installed configuration, native append, repeated-token restart and conservative completion report. + +If true: the packet inventory/checksums and wheel RECORD verify; helpers import under a compatible installed environment without research modules or provider calls; initial strict doctor records an Accessibility blocker; the denied lane has no admitted action; after a human grants the required permission to the actual host, real bind/read/append/restart/report and independent one-effect/one-record readback pass. Any required additional permission or cleanup consent is recorded rather than hidden. + +If false: missing helpers or source dependencies prevent entry-point use, a pregranted environment is mislabeled first use, a denied route dispatches input, host responsibility changes between checks, or permission approval does not produce the verified effect/recovery/report behavior. + +## Design and primary criterion + +Freeze the packet before execution. Include the unchanged reviewed wheel, MIT license, installation guide, U2 runbook, six existing qualification helper modules and a manifest with source/artifact/member hashes. The helpers remain test/oracle code; product processes load only the installed wheel. No script runs automatically when the packet is opened or built. + +Run from the guest's interactive Terminal first. Record OS/build, SIP status, interpreter, installed origins, launch host and permission status before granting anything. Require a denied Accessibility baseline; if already granted, mark the onboarding run ineligible and retain its installation result separately. Permission decisions remain with the user in the guest UI. Screen Recording is advisory for the current DOM/AX slice; do not infer capture permission from an AX success or grant extra permissions silently. Restart the responsible host when needed and keep the same host identity. + +Primary criterion: one valid independently provisioned lane with an observed denied baseline and explicit permission transition completes the existing installed native append/restart/report oracle with one operation and one effect, plus qualified cleanup. Synthetic/mocked permissions can test bookkeeping but cannot meet this criterion. No heldout or paid agent runs. + +## Decision rule + +Close packet preparation after artifact verification, helper import/launch checks and Claude review. Keep U2 runtime qualification open until the independent lane passes all required observations. Preserve failures under a new run version; do not reset the host's grants to manufacture a baseline or regrade existing U1/A2 results. + +## Result · 2026-10-11 + +Full first-use qualification does not pass. A fresh SIP-enabled guest cold-installed the frozenv3 packet and exhibited a valid denied Accessibility baseline. The grant actor/time was not captured, the first fixture failed during normal Chrome verification, and automatic document cleanup could not be confirmed after Automation was declined. Preserve results/u2-independent-onboarding-20261011-v1. + +Separately preregistered development continuation UO5 passes after ordinary Chrome verification: unchanged helpers and installed wheel, one native effect/ledger row, actual isolated detached worker, same-token server restart and accepted structured completion report. Owned process/listener cleanup passes; exact document close required the operator and persistence remains not_checked. Claude independently reviewed the raw receipts/journal and confirmed these limits. See results/u2-independent-onboarding-20261011-v2-dev. This continuation does not regrade the initial failure. Future packet fixes and a captured fresh transition remain required before a complete first-use claim. diff --git a/jevdual/docs/experiments/U3-packet-v4-permission-journey.md b/jevdual/docs/experiments/U3-packet-v4-permission-journey.md new file mode 100644 index 0000000..8f95455 --- /dev/null +++ b/jevdual/docs/experiments/U3-packet-v4-permission-journey.md @@ -0,0 +1,37 @@ +# U3: packet v4 permission-state qualification + +Registered2026-10-12 before packet freeze and guest execution. This is a development qualification after the original U2 failure, not a regrade of that measurement or a heldout retry. + +## Hypothesis + +Exact-document AX cleanup and separate bounded browser startup/identity phases permit the installed public path and fully automatic owned-resource cleanup with Accessibility alone, following a captured denied-to-granted transition. + +## Predictions + +If true: the frozen packet repeats byte-for-byte and matches its immutable source; strict denied doctor exits1, fixture refusal creates no directory; the approved same Terminal host transition is captured off-to-on; after restart the installed public trial confirms one native effect, one ledger row, same-token replay and an accepted completion report; automatic exact-document/browser/listener/worker cleanup is confirmed without Automation or operator close. + +If false: a required denial/transition/native gate is absent, another resource is acted on, launch health substitutes for identity, cleanup needs another permission/operator, or a source/development environment supplies the runtime. A required gate cannot be replaced by an assertion or generic success receipt. + +## Design + +Preserve original U2 v1 and v2. Freeze v4 from committed source with the already reviewed wheel unchanged. Record preparation checks, source/packet/wheel hashes and Claude review. Use the existing independently provisioned SIP-enabled disposable guest, after approved supported per-host Accessibility reset. Label the run as fresh permission state on a previously provisioned OS with browser prerequisites already installed; do not claim a new cold OS or first Chrome launch. Extract v4 into a new root, verify pristine members, and cold-install public dependencies into a new venv/private home/cache. No SSH, clipboard/host shares, pregranted seed, provider keys, TCC database edits or SIP changes. + +Run denied diagnostics/refusal from the guest interactive Terminal; retain and visibly hash baseline bytes before granting. Capture actual approval, switch actor/time and restart. Run the generated isolated installed MCP command and detached worker, then Ctrl-C fixture cleanup. Preserve raw receipts and the read-only journal. Export only after execution/cleanup via a bounded fixed-artifact VM-bridge transfer, with hash readback and receiver shutdown. If the guest locks or requests authentication, the human supplies credentials; never log them. + +## Primary metric and guardrails + +Primary metric: all required permission, installed native and automatic cleanup gates pass in this one frozen run. Any required miss marks the run failed or unverified according to observed evidence. Disk persistence is not_checked. No extra discovery, Automation or Screen Recording grant is supplied for this AX slice; decline unexpected prompts and preserve them. No repeated input to mask uncertainty; same-token replay uses the durable protocol. No unrelated process/window close or evidence deletion. + +## Decision rule + +Accept only this scoped permission-state journey when every required gate and independent Claude receipt review pass. Preparation checks alone do not qualify runtime. Preserve failures, create new development scenarios and a new frozen packet before testing revised behavior. A passed rerun does not change U2's original failed cold-start/uncaptured-transition record. + +## Evidence location + +Preparation: results/onboarding-fixture-v4-preparation-20261012. Guest run: results/u3-packet-v4-permission-20261012-v1. Raw archives remain under ignored output. Notebook: docs/notebook/2026-10-12-onboarding-fixture-v4.md. + +## Result + +2026-10-12: original U3v1 on packet eef36325/source21599c6 failed automatic document confirmation; its native and other cleanup gates passed. The failure and raw receipts remain in `results/u3-packet-v4-permission-20261012-v1`. The separately frozen repair packet205577ee/source445a348 retained the unchanged wheel. UF5 was interrupted by a VM stop and an uncaptured post-boot grant; it remains inconclusive. + +Final UF6/U3v3 passed every preregistered scoped gate on the repaired artifact. A new supported Terminal-only reset supplied a hashed denied baseline, followed by the approved agent click and human authentication, observed OFF-to-ON transition, actual Terminal relaunch, installed native effect/replay/report and automatic exact-resource cleanup. Claude independently reviewed raw evidence and approved the result, reporting Opus5.5. See `results/u3-packet-v4-permission-20261012-v3` for the gate ledger, sanitized receipts, artifact digests, review records and limits. This is permission reset then captured grant on a previously provisioned guest; saved-file persistence, pixel routes and other hosts remain unqualified. diff --git a/jevdual/docs/experiments/WB1-project-workbench.md b/jevdual/docs/experiments/WB1-project-workbench.md new file mode 100644 index 0000000..f0d9545 --- /dev/null +++ b/jevdual/docs/experiments/WB1-project-workbench.md @@ -0,0 +1,29 @@ +# WB1: Project-linked workbench observations + +Mode: local engineering/workflow feasibility, not autonomous task execution or usefulness qualification. + +## Hypothesis + +An already reviewed camera note can become a project-bound workbench record with an explicit question, interpretation, proposed next check and acceptance criterion, preserving the distinction between physical evidence and engineering inference. Footwork can verify the saved record without inventing a native session or performing its proposed action. + +## Predictions + +The first live desk note binds to the user's explicitly selected Footwork project using exact namespaced project ID and canonical checkout, with the repository revision and selected code-reference hashes recorded. A saved JSON/Markdown packet retains approved source text and hashes, a source evidence trail, typed record purpose and clearly proposed work/check status. Public Footwork check-artifact validates each generated record. Repeating a binding does not create another record. Changed project, source, metadata or approval digest cannot reuse an existing binding. Unknown write outcomes do not replay. Later repo changes mark context drift separately from saved artifact integrity. + +Against: guessed name-only project matching; note instructions executed as commands; a proposed check claimed as completed; incorrect/tampered/missing artifact claimed verified; duplicate/replayed writes; raw OCR or a stale repo context promoted into current evidence. + +## Design + +Add an isolated tools/workbench-notes package, reuse the desk source's current saved-status/provenance checks and Footwork exact-artifact checker. Only exact registered project metadata is accepted from an explicit bounded catalog. The initial real project is Footwork, selected by the user; records live in this active repository's ignored private output, with no writes into other repositories or cloud workspaces. + +Caller-authored structured JSON supplies kind (observation/question/experiment/decision/blocker), question, interpretation, proposed next check, expected result and optional relative code references. These are attributed to the caller, not inferred authority from the photo. Keep immutable source-note identity/text and context snapshots, validate reference containment and secret/excluded-file patterns, bound reads and paths, use one intent per run and create-only output. Provide project-filtered listing and read-only status. Preserve historical recording and current file checks; repository/reference drift is a separate context state, never a claim that a check ran. + +Primary metric: one explicit Footwork workbench packet saved once with all saved bytes independently matching bound expected sources (binary), plus accurate source/context/proposal labels. + +Checks: public link/status/list path; source/project/metadata conflicts; duplicate labels and namespaced IDs; invalid catalog/reference escape/secret file; source/packet tamper; interrupted record/no replay; project HEAD/reference drift separated from bytes. Use one existing human-approved note; no new camera capture required. + +Decision rule: qualify local project association and artifact verification only. A useful agent handoff must name an executable next check and expected observation, but this experiment does not execute it, create remote tasks, publish evidence or infer physical completion. Daily benefit requires later trials against ordinary project note-taking. + +## Result + +Local project-bound handoff verified on 419a677 using the previously reviewed real paper note: exact Footwork identity, separate interpretation/proposed not_run check, JSON/Markdown independent byte checks, preserved repeat artifacts and one project-listed record. All 15 tests plus 11 subtests and Ruff pass. See results/workbench-20261010/report.md. The user subsequently chose a physical/controller simulation as the next active direction; this package performs no action from its proposal. diff --git a/jevdual/docs/independent-onboarding.md b/jevdual/docs/independent-onboarding.md new file mode 100644 index 0000000..16f8a7d --- /dev/null +++ b/jevdual/docs/independent-onboarding.md @@ -0,0 +1,90 @@ +# Independent first-use qualification + +This packet prepares U2; it is not a passed first-use result. It contains the reviewed standalone wheel, MIT license and qualification helpers, with hashes in MANIFEST.json. No code runs automatically. Product processes load Python runtime files from the installed wheel; helper code supplies the fixture and independent oracle. A runtime source checkout, development environment, provider credentials and evaluation results are absent. + +Use an independently provisioned Mac or a new SIP-enabled macOS guest with a visible logged-in session. A pregranted CI runner or cloned granted seed cannot establish the initial permission transition. Record the actual environment and launch host; do not reset this MacBook's permissions to simulate a fresh machine. A rerun on a previously provisioned disposable guest must be labeled a fresh permission-state run, not a new cold-OS installation. Only after explicit approval, the supported guest command `tccutil reset Accessibility com.apple.Terminal` can restore that host's denied state; never reset other hosts or edit the TCC database. Retain all earlier run directories. + +## Verify and install + +Compare the archive SHA256 with its accompanying preparation report before extracting. The manifest identifies the source commit and hashes every payload member. Work inside the extracted footwork directory. Provision compatible Python and uv through their normal installers on the independent environment; the packet does not install them or macOS. + +The guest also needs TextEdit and Google Chrome at /Applications/Google Chrome.app, the executable used by the frozen fixture helper. Record the browser version and provision it through its normal installer before fixture execution. Launch Chrome once through Finder, let normal macOS verification finish, then quit it. Record this prerequisite separately; do not bypass Gatekeeper or count launch readiness as resource identity. Keep the fixture test profile unsigned-in. Missing Chrome is a browser prerequisite failure, not a permission denial. + +Open an interactive guest Terminal and define the packet directory: + +~~~sh +cd /absolute/extracted/footwork +PACKET_ROOT="$(pwd -P)" +QUALIFICATION_PYTHON=/absolute/python3.12 +command -v python3 uv +"$QUALIFICATION_PYTHON" --version +uv --version +"$QUALIFICATION_PYTHON" -I "$PACKET_ROOT/jevdual/scripts/verify_onboarding_packet.py" \ + --packet-root "$PACKET_ROOT" +cd jevdual +"$QUALIFICATION_PYTHON" -E -s scripts/capability_clean_install.py \ + --python "$QUALIFICATION_PYTHON" \ + --wheel "$PACKET_ROOT/artifacts/jevdual-0.0.1-py3-none-any.whl" \ + --run-dir "$PACKET_ROOT/output/install-v1" +~~~ + +Set QUALIFICATION_PYTHON to the absolute executable installed for this run, not /usr/bin/python3. The system Python and Git commands can trigger a developer-tools installation dialog on a fresh Mac. The standalone helpers read their source commit from MANIFEST.json and do not need Git. Record the command paths and version output above. Verify members before installation creates output directories; stop on a nonzero verification exit. The archive hash from the accompanying report authenticates the manifest; member hashes detect changes after extraction. + +The installer uses a fresh environment, empty cache, private state and public binary packages. Preserve failures as their own version; a new attempt needs a new run directory. Existing dependencies, source code, Rust and model accounts must not satisfy missing prerequisites. + +## Record denied first use + +Record macOS version/build with sw_vers, SIP status with csrutil status, and the interactive responsible host's identity/version. Before changing permissions: + +~~~sh +"$PACKET_ROOT/output/install-v1/venv/bin/python" -I -m jevdual.capability.cli doctor --strict \ + > "$PACKET_ROOT/output/doctor-before.json" +"$PACKET_ROOT/output/install-v1/venv/bin/python" -E -s scripts/capability_fixture.py \ + --run-dir "$PACKET_ROOT/output/denied-fixture-v1" +~~~ + +Record each command's exit code. With missing Accessibility, strict doctor must exit1 and report its blocker. The fixture must refuse before creating its directory, browser or document. If Accessibility is already granted, preserve the installation result and mark this lane ineligible for first-use permission qualification. A locked display, missing dependency or failed probe is not an observed permission denial. + +The human approves the required permission for the actual responsible host in the guest's System Settings. The human or explicitly authorized operator changes that exact host's switch. Capture its off state, approval, actor and time, then its on state; separated denied/granted diagnostics without the actual transition do not pass this gate. Record any required host restart. Do not grant arbitrary helper paths or change SIP/TCC databases. Run subsequent commands from the same host. Screen Recording status is advisory for this DOM/AX slice; capture needs its own evidence. Record any additional permission required by an actual route or fixture cleanup. + +Repeat isolated strict doctor into doctor-after.json. Met prerequisites alone do not prove resource access or execution. + +## Drive the installed public path + +In one Terminal, start and retain the owned fixture: + +~~~sh +"$PACKET_ROOT/output/install-v1/venv/bin/python" -E -s scripts/capability_fixture.py \ + --run-dir "$PACKET_ROOT/output/fixture-v1" +~~~ + +The helper waits up to180seconds for its owned browser to expose a working private-profile CDP endpoint, then starts a separate30second unique-window identity phase. Startup health confers no scope authority. A typed failure is retained in fixture-status.json; stop on a nonzero exit and use a fresh directory for a later attempt. + +Only after its ready response identifies the owned scope, use another Terminal from the same responsible host: + +~~~sh +"$PACKET_ROOT/output/install-v1/venv/bin/python" -E -s scripts/capability_fixture_config.py \ + --scope "$PACKET_ROOT/output/fixture-v1/scope.json" \ + --run-dir "$PACKET_ROOT/output/job-v1" +"$PACKET_ROOT/output/install-v1/venv/bin/python" -E -s scripts/capability_installed_trial.py \ + --python "$PACKET_ROOT/output/install-v1/venv/bin/python" \ + --scope "$PACKET_ROOT/output/job-v1/scope.json" \ + --host-config "$PACKET_ROOT/output/job-v1/host.json" \ + --journal "$PACKET_ROOT/output/job-v1/operations.sqlite" \ + --isolation-root "$PACKET_ROOT/output/install-v1" \ + --run-dir "$PACKET_ROOT/output/native-v1" +~~~ + +The fixture configuration helper copies observed identifiers into the public config command, which generates a fresh job task. It confers no permission or execution authority. The installed trial exercises the exact generated isolated MCP command, real detached worker, exact append, same-token restart and journal-validated report. Its independent AX oracle checks the owned document and one journal operation. No model is invoked. + +Use only the entry points listed here; the other bundled modules supply imported qualification functions. They are not consumer tools or additional tasks to execute. + +## Close and retain + +Press Ctrl-C once, then wait for the shutdown response. The helper uses Accessibility to close only the window with the exact fixture document path. It may press a unique discard button only in that window's observed attached save sheet. Only identified English save sheets are supported; other dialogs refuse. It requests no TextEdit Automation grant. Retain cleanup.json and the fixture exit code. Confirm the owned browser process and loopback listener are gone, the exact document window is closed and no owned worker remains. A refusal, unknown result or manual close is a failed automatic-cleanup gate; preserve it and independently inspect only the exact owned resource. Do not substitute killing all Chrome/TextEdit processes. cleanup.json retains bounded file size and SHA256 observations before and after close; these are file snapshots, not a persistence certificate. Record the guest close-confirmation preference without changing it. Cleanup does not certify whether TextEdit saved the edit before closing. + +Retain installation receipts, before/after diagnostics and exit codes, host/OS/SIP identity, consent observations, native receipt/journal/runtime contexts and cleanup evidence. Sanitize observations before sharing. Mark U2 passed only when an observed denied baseline, explicit permission transition and every native/postcondition/cleanup gate pass. This proves the scoped historical append/recovery/report behavior; it does not prove saved-file persistence, arbitrary apps or the host's entire goal. + +## Recorded qualification + +The repaired packet from source `445a348` passed the supported Terminal permission-reset journey on a previously provisioned macOS 26.6.2 guest, including captured grant, installed native action/replay/report and automatic cleanup. Independent Claude review approved the raw evidence. See [the U3v3 report](../results/u3-packet-v4-permission-20261012-v3/report.md) for the exact artifact and limits. Original failed and interrupted runs remain preserved. Other agent hosts, pixel capture and disk persistence need their own qualification. diff --git a/jevdual/docs/installed-agent-host.md b/jevdual/docs/installed-agent-host.md new file mode 100644 index 0000000..22e336f --- /dev/null +++ b/jevdual/docs/installed-agent-host.md @@ -0,0 +1,23 @@ +# Use the candidate from an agent host + +Install the wheel as described in [installation](capability-packaging.md). Keep the environment and job directory private. Run doctor from the same responsible host that will launch the MCP server; a grant observed from another terminal does not establish this host's access. + +For a disposable qualification task, the bundled helpers create an owned browser page and TextEdit document. Use absolute paths for every command. Verify an extracted qualification packet before creating output; a release bundle has its own manifest and is a different artifact. + +1. Run `jevdual/scripts/capability_clean_install.py` with an existing Python, the bundled wheel and a new output directory. Its empty cache and private state test fresh dependencies on the existing OS. +2. From that installed environment, run `jevdual/scripts/capability_fixture.py --run-dir /absolute/owned/output/fixture`. Wait for its ready response; keep this Terminal open. The helper records exact source/destination identity and waits for cleanup. +3. In another Terminal from the same responsible host, run `jevdual/scripts/capability_fixture_config.py --scope /absolute/owned/output/fixture/scope.json --run-dir /absolute/owned/output/job`. This invokes public configuration and creates `host.json`, `run.json` and `report-schema.json` for one job. + +The generated `host.json` is the complete ephemeral MCP registration for a compatible host. It uses the installed environment's absolute Python path, `-I`, the exact scope and the durable journal. Import that registration per job rather than changing global agent settings. The configuration does not grant OS permission or execution authority. + +For Claude Code, the bounded tested recipe uses `--strict-mcp-config`, `--mcp-config /absolute/owned/output/job/host.json`, `--tools ""`, and `--allowedTools` containing `mcp__footwork__bind,mcp__footwork__observe,mcp__footwork__act,mcp__footwork__status,mcp__footwork__report`. Disable hooks and settings inheritance with `--setting-sources "" --settings '{"disableAllHooks":true}'`, and disable slash commands and session persistence. Choose a model and budget supported by your host; this candidate's measured model and CLI version are in the evidence summary. Do not copy credentials into the job. + +Ask the agent to read the invoice total from the specified source and append exactly that value to the identified document, preserving existing characters. Require a structured final report matching the generated schema, with every admitted operation and `persistence: "not_checked"` unless separate persistence evidence exists. Claude's `--json-schema` accepts the schema object and may expose a `StructuredOutput` finalizer alongside the MCP tools. That finalizer formats the report and grants no resource access. + +After the host stops, write only its structured claims to a private JSON file. Invoke the exact `report_adapter` recorded in `run.json`, adding `--input /absolute/owned/output/job/host-report.json`. Publish only the adapter's `trusted_summary`, including rejected claims; never substitute raw host prose. Exit0 accepts claims, exit1 rejects them and exit2 means validation is unavailable. The report certifies recorded historical operations as of its snapshot, not current GUI content or saved-file persistence. + +Finish the fixture with one Ctrl-C and inspect its `cleanup.json` and exit status. Confirm the exact document window, owned browser/listener and detached worker are gone. A missing confirmation does not qualify automatic cleanup. Preserve unknown or refused results and reconcile before any further action. + +This recipe needs existing compatible Python/uv/Chrome, applicable macOS permissions and working agent-host authentication. The dependency installation and the model call use their respective networks. Only the product runtime is provider-independent. Fresh dependencies, guest permission-state qualification and actual agent execution are separate evidence lanes; the release manifest records what passed. + +The measured host trial additionally applied product-only private HOME/config/cache/temp overrides, blanked ambient variable names ending in _KEY/_TOKEN/_SECRET, and enabled runtime audit in a private output directory. The host kept its existing opaque authentication. These diagnostic isolation settings do not come automatically from the generated registration; the latter supplies isolated imports and telemetry settings. The evidence records this distinction. diff --git a/jevdual/docs/notebook/2026-10-10-desk-paper-inbox.md b/jevdual/docs/notebook/2026-10-10-desk-paper-inbox.md new file mode 100644 index 0000000..ce560ed --- /dev/null +++ b/jevdual/docs/notebook/2026-10-10-desk-paper-inbox.md @@ -0,0 +1,35 @@ +# Desk paper inbox experiment + +## DI1 · prediction · 2026-10-10 14:52 +Hypothesis: local Apple Vision OCR and an explicit digest-bound review can turn a small visible paper note into one private inbox artifact whose exact content Footwork independently verifies. +Test: implement isolated desk tool plus read-only Footwork artifact oracle, freeze source, run real OCR on known synthetic text, exercise the public prepare/review/accept/status boundaries, then attempt one real paper note if the user can position it. Preserve synthetic/live distinctions. +Predicts: accepted text is saved once and Footwork confirms exact bytes; repeats reuse one note; empty/changed/unapproved/tampered evidence refuses or reports unresolved without extra effects. No fake native/browser carries or provider calls. +Against: OCR alone dispatches an action, changed approval/candidate becomes saved text, bytes differ while verification passes, repeated token duplicates a note, or fixture proof is presented as live usefulness. +Refs: docs/experiments/DI1-desk-paper-inbox.md; tools/camera-capture-experiment/; /Volumes/External SSD/referencerepos/cua/libs/lume/src/Utils/VNCDriver.swift:272; /Volumes/External SSD/referencerepos/rerun/examples/python/live_camera_edge_detection/live_camera_edge_detection.py + +## DI2 · prediction · 2026-10-10 14:54 +Hypothesis: an explicit horizontal-mirror setting, applied to OCR orientation and review presentation while retaining the original source bytes, can handle a mirrored camera feed without silently changing capture evidence or operation identity. +Test: expose --mirror-input at preparation, bind it in source/request provenance, and test real Vision against known normal/mirrored printed fixtures. The user's live note will include LEFT 123 RIGHT to determine the feed's direction. +Predicts: the appropriate explicit orientation yields the original literal line order/text; a changed orientation cannot reuse a prior candidate as though it had the same source request. Original PNG/hash remain intact, and preview names the orientation. +Against: normal text is reversed under its matching setting, mirrored proof silently overwrites source bytes, OCR claims imply mirror classification, or candidate lookup ignores changed orientation. +Refs: docs/experiments/DI1-desk-paper-inbox.md; tools/desk-inbox-experiment/; https://developer.apple.com/documentation/vision/vnimagerequesthandler + +## U-DI1 · update · 2026-10-10 15:05 +Resolves: DI1 = held +Observed: frozen source 6e93c35 passes 31 focused tests and Ruff. Real Vision exactly recognizes the known printed fixture; blank fixture yields no candidate/note. The user positioned a live handwritten paper, confirmed a separately corrected transcription after seeing raw OCR errors, and authorized saving it. Public Footwork check-artifact independently confirms all 187 approved bytes; fresh-process accept/status preserve the same manifest and one note. Raw OCR and source remain unchanged. Wrong digest refuses; a tampered saved fixture retains historical save but current unknown and repeat acceptance does not overwrite it. +Inferred: reviewed physical-to-digital note feasibility is established on this Mac and one real note; unreviewed handwriting accuracy and daily utility remain unqualified. +Next: close DI1; assess capture/review friction before further workflow expansion. +Refs: results/desk-inbox-20261010/verification.json; results/desk-inbox-20261010/source.json; results/desk-inbox-20261010/report.md; tools/desk-inbox-experiment/output/live-paper-v1/ + +## U-DI2 · update · 2026-10-10 15:05 +Resolves: DI2 = held +Observed: real Vision on normal and horizontally mirrored known fixtures yields the same exact expected three lines under explicit up/upMirrored settings. Original source hashes remain intact; changed mirror request refuses reuse. The live calibration line reads normally in the raw captured frame, so no flip was selected. +Inferred: explicit orientation handling works on these fixtures and the observed feed; this does not classify arbitrary camera mirror state or infer direction from confidence. +Next: keep mirror choice explicit and tied to retained calibration evidence. +Refs: results/desk-inbox-20261010/verification.json; results/desk-inbox-20261010/fixtures.json; tools/desk-inbox-experiment/recognize.swift + +## S-DICLOSED · summary · 2026-10-10 15:05 +Established: one complete live capture/OCR/review/save/readback handoff, exact approved bytes, repeat lookup and preserved source/corrections; real printed/mirror/blank OCR proof remains distinct from fixture conformance. +Uncertain: raw handwriting quality, daily usefulness, capture-to-page identity, exposure freshness, authenticated review and any autonomous or robotic actions. +Decision: keep an isolated desk tool plus read-only Footwork artifact checker; prioritize image/word review and legibility before task execution. No native recovery, guard threshold or model-default changes were made. +Refs: results/desk-inbox-20261010/report.md; docs/experiments/DI1-desk-paper-inbox.md; 6e93c35 diff --git a/jevdual/docs/notebook/2026-10-10-image-guided-workcell.md b/jevdual/docs/notebook/2026-10-10-image-guided-workcell.md new file mode 100644 index 0000000..0f386b8 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-10-image-guided-workcell.md @@ -0,0 +1,41 @@ +# Image-guided physical and computer workcell + +## SIM2 · prediction · 2026-10-10 15:41 +Hypothesis: an AI agent using rendered RGB views and known camera calibration can locate the simulated block, move it, observe residual placement error and correct it before a visually gated local computer action. +Test: unchanged SIM1 dynamics with an isolated image wrapper, fresh generation/digest-bound proposals, actual RGB box/pixel checks, bounded native contact moves, at least one image-informed correction, final image placement and separate native oracle, exact saved result readback. Freeze implementation before parent-run public trial; exercise visual/stale/tamper/no-replay counterexamples. +Predicts: the planner needs no native block coordinates; final visual placement agrees with oracle; only fresh visual plus native physical evidence allows the computer result. Bad/ambiguous/stale evidence and unknown effects remain refused or unresolved. +Against: hidden-state-driven planning, prerecorded moves reported as AI visual decisions, wrong/blank image accepted, completion without fresh visual evidence, repeated effect or no corrective movement demonstrated. +Refs: docs/experiments/SIM2-image-guided-workcell.md; results/physical-workcell-20261010/report.md; /Volumes/External SSD/referencerepos/mujoco/doc/XMLreference.rst + +## SIM2-U1 · update · 2026-10-10 15:54 +Observed: parent AI trial on fbc9815 used six viewed frames and five bounded moves, observed a residual error near the target boundary and selected a smaller correction. Fresh visual completion passed with a separately observed native oracle and public Footwork file check. Source review also found that a cached packet dictionary could contain unvalidated extra fields. +Inferred: the clean generated image/control loop works, but schema hardening is needed before treating cached planner evidence as fully constrained. +Next: SIM2A; preserve version 1, harden packet admission and repeat the image-guided trial on version 2. +Refs: results/image-guided-workcell-20261010/trial-v1.json; docs/experiments/SIM2-image-guided-workcell.md + +## SIM2A · prediction · 2026-10-10 15:54 +Hypothesis: exact packet field admission and fixed perception-scope validation can prevent injected native-oracle fields reaching the planner while preserving the existing image-guided workflow. +Test: reject forged extra fields and altered perception scope on actual retained camera packets; freeze revised source, then repeat the AI RGB placement/correction/computer loop on a new run with unchanged dynamics, calibration, detector and motion settings. +Predicts: mutated packets refuse without exposing injected fields; valid generated packets still support fresh visual decisions, correction and a separately verified local work order. +Against: forged oracle fields returned, mutated packets authorize dispatch, or valid image-guided placement cannot complete after hardening. +Refs: docs/experiments/SIM2-image-guided-workcell.md; tools/physical-workcell/image_guided.py + +## SIM2-U2 · update · 2026-10-10 15:58 +Resolves: SIM2 = held +Observed: the parent AI session viewed six native RGB frames on frozen version f96ac4a and supplied five bounded moves. The frame at generation 4 showed residual horizontal error; its completion was refused, then a smaller image-informed correction produced a fresh accepted visual placement. Post-planning native observation and an independent public Footwork saved-byte check passed. Wrong-object/stale-generation requests refused; repeated motion/completion left all run artifact bytes unchanged. +Inferred: the agent-facing RGB/control/local-computer workflow supports feedback correction in this known scene without native poses in the planner interface. A single trial does not measure general visual-model quality. +Next: close SIM2; retain camera/control separation for future scene variation or real-camera calibration experiments. +Refs: results/image-guided-workcell-20261010/trial-v2.json; results/image-guided-workcell-20261010/report.md + +## SIM2A-U1 · update · 2026-10-10 15:58 +Resolves: SIM2A = held +Observed: the strict packet admission at f96ac4a rejected a forged extra-oracle field through both public view and apply, without returning the injected field or creating effect intents. Scoped conformance also rejected changed perception scope. The fresh parent RGB placement/correction/computer run passed after hardening; dynamics, camera/detector and motion settings were unchanged. +Inferred: exact cached-packet admission closes the reviewed planner-output gap for this local workflow. +Next: close the package. +Refs: results/image-guided-workcell-20261010/packet-admission-negative.json; results/image-guided-workcell-20261010/verification.json + +## SIM2-S1 · summary · 2026-10-10 15:58 +Established: actual AI-viewed images and structured decisions drove bounded contact motion, residual-error correction and fresh visual completion, followed by separate native and software verification. Versioned trial evidence is preserved; final scoped conformance and prior SIM1 regressions passed. +Uncertain: varied lighting/object identity/camera geometry, general vision accuracy, hardware transfer, global access control and unattended model orchestration remain unqualified. The RGB checker is specific to known colored silhouettes; the base SIM1 CLI remains an unguided route. +Decision: retain image_guided.py as an experimental Footwork body interface, with agent intent, native controller, RGB placement, native oracle and software persistence kept distinct. Do not change production model authority or add Rust without workload evidence. +Refs: results/image-guided-workcell-20261010/report.md; tools/physical-workcell/IMAGE_GUIDED.md diff --git a/jevdual/docs/notebook/2026-10-10-independent-controller-recovery.md b/jevdual/docs/notebook/2026-10-10-independent-controller-recovery.md new file mode 100644 index 0000000..db1f67d --- /dev/null +++ b/jevdual/docs/notebook/2026-10-10-independent-controller-recovery.md @@ -0,0 +1,74 @@ +# Independent simulated controller recovery + +## SIM4 · prediction · 2026-10-10 18:05 +Hypothesis: a separately running controller with durable acceptance, exact operation identity and lower-layer receipt reconciliation can survive caller loss without duplicate effects and can distinguish a cancellation request from verified stop. +Test: actual private Unix-socket service/client processes and native MuJoCo execution; preregister lifecycle scenarios, develop on private cases, freeze source, then independently drive disconnect, active return, cancellation, restart and conditional artifact completion. +Predicts: host loss does not end controller execution; repeated IDs return history without motion; queued cancellation dispatches nothing; running cancellation retains bounded stop evidence or stays unknown; restart uses exact historical receipts and never current placement as execution history. +Against: caller loss stops the body, duplicate dispatch, cancellation acknowledgement masquerades as stop, source/case reuse, legacy commands race ownership, replay after restart or success manufactured from a satisfied scene. +Refs: docs/experiments/SIM4-independent-controller-recovery.md; results/repeatable-work-orders-20261010/report.md; /Volumes/External SSD/referencerepos/dora/examples/action-example/server/src/main.rs; /Volumes/External SSD/referencerepos/openpnp/src/main/java/org/openpnp/spi/MotionPlanner.java + +## SIM4-U1 · update · 2026-10-10 18:12 +Observed: independent pre-freeze source review found idle health calling current state load before returning a valid historical goal, so missing current state could suppress history. Startup also depended on current state. Initial accepted-file creation synced file contents but not its parent directory; this limits acceptance durability claims. Development process conformance is underway, with live socket loss/SIGKILL distinguished from simulated retained-write faults. +Inferred: historical status and current integrity need separate failure handling; acceptance needs an owned-directory sync before acknowledgement. These are source findings, not yet observed qualification failures. Native-only terminal receipts with an unfinished image wrapper intentionally remain unknown. +Next: fix the history/current seam, add a current-corruption regression and sync acceptance before acknowledgement; finish development checks, then freeze runtime before independent lifecycle driving. +Refs: tools/physical-workcell/controller.py; tools/physical-workcell/test_controller.py; c78d2a4 + +## SIM4-U2 · update · 2026-10-10 18:14 +Observed: the first development process run stopped with nine startup-only harness failures and exit 130. The driver looked for a fixed socket filename instead of the service's advertised socket_path; no goals were submitted and owned processes were cleaned up. The original test source/hash, failure paths and summary remain private. The driver now reads the declared path; a current-state corruption/history-only restart regression is added while runtime repairs are completed. +Inferred: this attempt supplies no controller lifecycle evidence. The mismatch was in observation setup rather than a demonstrated motion or recovery failure. +Next: rerun the corrected conformance after source stabilization; preserve this attempt separately from later diagnostic outcomes. +Refs: tools/physical-workcell/output/c4-initial-socket-failure/summary.json; tools/physical-workcell/test_controller.py + +## SIM4-U3 · update · 2026-10-10 18:17 +Resolves: SIM4 = failed +Observed: targeted process scenarios failed before fixes: a saved unknown controller fallback with intact lower terminal receipts did not reconcile on restart, and live health remained ready after retained controller-binding tamper. The corrected startup now attempts exact read-only reconciliation for unknown records, and live validation rereads retained binding. The earlier fifteen-process run had fourteen passes plus a harness enum mismatch: blocked observation after SIGKILL was conservatively correct but excluded by the test's accepted phase set. That assertion now requires no observation authority. All development attempts are retained; the revised seventeen-case conformance is running before freeze. +Inferred: the first implementation did not meet the original recovery/binding prediction. Independent ownership alone was insufficient: exact receipt reconciliation must include durable fallback uncertainty, and cached identity checks must include retained controller binding. The corrected runtime remains to be independently qualified. +Next: freeze the corrected source after regression checks, then independently drive all eight preregistered lifecycle scenarios and retained failure paths. +Refs: tools/physical-workcell/output/c4-unknown-fallback-before-fix/unittest.log; tools/physical-workcell/output/c4-live-binding-before-fix/unittest.log; tools/physical-workcell/output/c4-process-run-v2/unittest.log + +## SIM4R · prediction · 2026-10-10 18:17 +Revises: SIM4 +Hypothesis: with live retained-binding validation, historical/current evidence separation and exact lower-receipt reconciliation for uncertain fallback records, the corrected frozen controller will pass independent lifecycle driving without replay or invented stop/history. +Test: source-frozen independent public CLI/socket trials of all eight preregistered scenarios, including actual reply loss and SIGKILL; distinguish controlled journal faults, retain all failures and compare exact saved native/artifact digests. +Predicts: returning clients retrieve known completed history; actual in-motion cancellation retains a verified stop; controller death remains unknown unless exact terminal evidence supports recovery; stale bindings refuse; normal contact fulfills the conditional saved-byte-checked order. +Against: duplicate motion, queued cancellation dispatch, false stopped/fulfilled claims, lost historical result from unavailable current state, accepted stale binding or incomplete lower evidence promoted to success. +Refs: docs/experiments/SIM4-independent-controller-recovery.md; results/independent-controller-20261010/diagnostic-plan.json + +## SIM4-U4 · update · 2026-10-10 18:22 +Observed: all 67 scoped regressions passed on the pre-freeze version. A subsequent root review found shutdown releasing both ownership locks if its bounded join timed out while a non-daemon worker remained alive; the lower artifact checker timeout exceeded that join interval. A targeted actual-process slow-checker/short-join fault reproduced both locks becoming available before the writer ended (one failing scenario, 3.400 s). No native stop was claimed by that timing-fault test. +Inferred: a successful ordinary regression suite did not cover ownership during failed graceful shutdown. The controller must abort the whole process on join timeout rather than release locks under a live writer, retaining uncertainty rather than a stopped claim. +Next: fix the abort fallback, run the new ownership regression and full scoped suite, then freeze source before independent diagnostics. SIM4R remains untested by independent source-frozen trials. +Refs: results/independent-controller-20261010/pre-shutdown-fix-checks.json; tools/physical-workcell/output/c4-shutdown-before-fix/unittest.log; tools/physical-workcell/output/c416f26c/conformance.json + +## SIM4-U5 · update · 2026-10-10 18:29 +Observed: frozen 5713173 passed all 68 scoped tests and Ruff. Independent public service diagnostics report nine passing checks, covering eight required scenarios plus admission negatives. Live cancellation retains 156 advancing native steps plus 500 settle steps and velocity/state stop evidence. The crash trial killed after public running status plus 0.45 seconds; its retained native journal proves dispatch intent but cannot prove native stepping before death. Process cleanup and exact frozen hashes are retained. +Inferred: public response/history checks passed, but the strict during-motion kill requirement needs stronger observation than a timed running status. No runtime defect or source change is established by this evidence gap. +Next: preserve the original trial and run a fresh supplemental case with explicit observation-only tracing around the real native step; do not change controller behavior or reuse the earlier episode. +Refs: tools/physical-workcell/output/s4audit/verification.json; results/independent-controller-20261010/source-freeze.json; checks.json + +## SIM4P · prediction · 2026-10-10 18:29 +Hypothesis: an observation-only marker written after a real mj_step returns can establish native execution before process death while preserving the controller's frozen source, dynamics and proposal admission. +Test: new owned supplemental episode and real native-step wrapper that calls the original function unchanged, records its first return outside the episode, then signals the exact controller PID; restart uninstrumented and compare native generation/state/receipt history and stale-incarnation admission. +Predicts: native-step marker identifies the killed PID before SIGKILL; restart remains unknown with no extra generation/dispatch, old-incarnation effects refuse and all owned processes exit. +Against: trace cannot identify actual step completion, changes native inputs/outputs, kill occurs before marker, replay or fabricated execution/stop history, changed frozen source or leaked owned resources. +Refs: docs/experiments/SIM4-independent-controller-recovery.md; tools/physical-workcell/output/s4audit/06-running-kill/events.json + +## SIM4-U6 · update · 2026-10-10 18:33 +Resolves: SIM4R = held +Observed: frozen 5713173 passed 68 scoped tests and Ruff. Nine independent public-service response/history checks passed; the fresh supplemental crash probe closes the native-progress gap for the required during-motion kill. All eight required lifecycle scenarios are supported, including conservative unknown/blocked outcomes. Parent read-only scoring checked retained cancellation state/velocity, real contact fulfillment, saved original artifact bytes and unchanged native generations on replay attempts; run-file hashes were unchanged. Deliberate current-artifact tamper remains a retained negative result. All diagnostic processes exited and automatic sockets were removed. +Inferred: the corrected isolated controller supports caller-independent execution, truthful simulated cancellation and exact-receipt recovery for these selected cases. Small diagnostics, controlled saved-record faults and observation-only timing instrumentation do not establish broad reliability, power-loss/hardware recovery or AI planning value. +Next: close SIM4 with its failed first implementation and complete evidence limits; separately preregister versioned application fulfillment before adding a real software workflow. +Refs: results/independent-controller-20261010/report.md; qualification.json; checks.json; source-freeze.json; development-attempts.json; 5713173 + +## SIM4-U7 · update · 2026-10-10 18:33 +Resolves: SIM4P = held +Observed: a new supplemental episode forwarded the original native mj_step inputs/return unchanged and recorded its first completed call from the motion worker (PID 11039) before SIGKILL. Restart used the uninstrumented frozen source, a new incarnation and unknown history. Same-ID submission returned that unknown without another native generation; stale-incarnation cancellation refused. Marker, commands, source/driver hashes and cleanup are retained. +Inferred: this provides explicit native execution-before-death evidence absent from the original timed running-kill trial, without identifying any unretained final pose. Tracing adds observation overhead and is disclosed. +Next: retain both probes and do not promote the first timed trial into direct native-progress evidence. +Refs: tools/physical-workcell/output/s4parent/supplemental-verification.json; results/independent-controller-20261010/qualification.json; 578d73d + +## SIM4-S1 · summary · 2026-10-10 18:33 +Established: independent process ownership, exact image-bound operation identity and incarnation admission, reconnectable history without duplicate effects, native held-command cancellation with retained stop evidence, conservative restart and history/current-state separation. All eight selected lifecycle scenarios are supported; 68 scoped tests and Ruff passed. Initial recovery/binding/shutdown defects and observation-driver mistakes are preserved. +Uncertain: hardware/power-loss/realtime guarantees, general perception, every lower-layer interruption window, application identity and fulfillment, production capability-service governance, and measured AI value. Screensaver/sleep behavior was not tested; no visible desktop was required. +Decision: adopt the controller under tools/physical-workcell, retain Python/native MuJoCo and the deterministic RGB movement helper, and make versioned application handoff the next separate experiment. +Refs: results/independent-controller-20261010/report.md; qualification.json; docs/experiments/SIM4-independent-controller-recovery.md; 5713173 diff --git a/jevdual/docs/notebook/2026-10-10-independent-onboarding.md b/jevdual/docs/notebook/2026-10-10-independent-onboarding.md new file mode 100644 index 0000000..a61de85 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-10-independent-onboarding.md @@ -0,0 +1,118 @@ +# Independent onboarding qualification + +## UO1 · prediction · 2026-10-10 19:20 +Hypothesis: packaging the existing model-free consumer/native helpers with the final wheel and explicit runbook makes the independent qualification transportable without including Footwork's runtime source or development dependencies. +Test: build a frozen packet twice, verify every manifest member and the wheel against source, unpack outside the checkout, and import the documented helper modules with an already qualified installed interpreter. Exercise help only; no fixture/native input, permission change or provider call. +Predicts: packet bytes repeat, runtime source and secrets/evals/results are absent, all helper imports and documented help entry points work from the unpacked directory, and tampered inventory or bytes are rejected. This qualifies preparation, not first use. +Against: a helper imports unavailable source/research code, the manifest accepts tampering, the builder executes a helper or the runbook mistakes existing grants for a denied baseline. +Refs: docs/experiments/U2-independent-onboarding.md; scripts/capability_installed_trial.py; results/capability-delivery-20261010/report.md + +## UO2 · prediction · 2026-10-10 19:20 +Hypothesis: a new SIP-enabled macOS environment launched through one responsible interactive host exposes a truthful denied-to-approved Accessibility transition and permits the frozen installed native oracle afterward. +Test: U2 independent environment first-use lane, with initial strict diagnostics, human permission decisions, host restart/readback and one-effect/repeated-token/report/cleanup checks. +Predicts: initial missing Accessibility blocks before input, the approved host's installed native flow passes and grants are attributed to that host rather than inherited from the current Mac or a golden seed. +Against: the initial baseline is already granted, the blocked route sends input, host attribution changes, or approved permission still cannot satisfy the actual native oracle. +Refs: docs/experiments/U2-independent-onboarding.md; local Cua macos-lume SIP-on permission-flow lane; local Arc Accessibility-only setup + +## UOU1 · update · 2026-10-10 19:34 +Observed: frozenf39b76a packet builds match at13members, helpers import outside the checkout on installed3.12.6/3.14.7, four help entries pass, placeholder-resource config stays private/fresh and creates no journal, malformed/reused configs refuse, and modified/extra archive members reject.17 selected tests and Ruff pass. The Claude request remains unread in the mailbox; no independent verdict is available. Self-review finds a missing explicit Chrome prerequisite and a verifier coupling its recorded source commit to currentHEAD, which would invalidate a frozen packet after evidence-only commits. +Changes: UO1/UO2 heading times were entered19:20 while the terminal clock was19:19; preregistration order is established by committed aa6be28 preceding frozenf39b76a and all resolving tests. No prediction text is rewritten. +Next: retainv1, clarify source-checkout/browser prerequisites and qualify explicit immutable source binding in a separate packet version. UO2 remains unexecuted pending environment selection. +Refs: output/u2-packet-smoke-v1/receipt.json; output/u2-packet-v1; Claude request1791674683421027000 + +## UO3 · prediction · 2026-10-10 19:34 +Hypothesis: reading the packet's fixed helper/doc inventory from an explicitly selected immutable Git commit lets a frozen artifact remain verifiable after evidence-only commits without trusting unrecorded working-tree bytes. +Test: verify oldv1 from this later commit with an explicit frozen source ref; build a new version from committed source, verify it against that ref, and reject mismatched refs/member bytes. Keep the reviewed wheel's current source verifier and external archive hash. +Predicts: explicit frozen-ref verification succeeds despite currentHEAD differing; the caller's expected ref, exact allowed Git blobs and wheel/member hashes still reject foreign or altered contents. +Against: verification still depends on currentHEAD, uses dirty payload bytes, silently accepts another ref, or helper/runtime inventory changes outside the declared preparation scope. +Refs: scripts/build_onboarding_packet.py; output/u2-packet-v1/footwork-onboarding-u2.zip + +## UOU2 · update · 2026-10-10 19:39 +Resolves: UO1 = held +Observed: frozenaa971782 packetv2 repeats and verifies13members. Both installed3.12.6/3.14.7 profiles import seven unchanged helpers and pass four documented help entries outside the source checkout. The configuration preparation/refusal gates and packet tamper/foreign-ref checks pass. Chrome and runtime-source-checkout wording are explicit. This resolves transport preparation only; Claude's request remains queued/unread and UO2 has no independent environment. +Next: retain all versions, hand off the tested packet and await the environment choice and independent review. +Refs: results/independent-onboarding-preparation-20261010; output/u2-packet-smoke-v2/receipt.json + +## UOU3 · update · 2026-10-10 19:39 +Resolves: UO3 = held +Observed: the old verifier rejectsv1 after committed16c13ac changedHEAD; frozenaa971782 verifies it with the expectedf39b76a ref and rejects a foreign ref forv2. Fixed payloads are read from immutable Git blobs, not uncommitted current files. An earlier staging-path failure leftHEAD unchanged, so that baseline attempt is retained as unqualified and not used to resolve this prediction. +Next: freeze preparation evidence; keep UO2 open for the actual independent permission transition. +Refs: output/u2-frozen-verifier-baseline-committed.log; results/independent-onboarding-preparation-20261010/packet-v2.json + +## UOS1 · summary · 2026-10-10 19:39 +Established: self-checked transportable packet, immutable source verification, generated private fixture configuration and explicit first-use/VM runbooks. The reviewed product wheel and PR2 remain unchanged; the temporary user-authorized caffeinate hold is active. +Uncertain: independent environment, actual denied-to-approved TCC/host transition, native U2 postconditions and Claude's queued review remain unavailable. Internal7.9GiB free disk constrains a local guest; no launcher/image/guest was installed. +Decision: deliver this bounded preparation on a local branch, keep UO2 open, and require an independent Mac or selected VM/storage environment before its runtime lane. Do not present source/helper checks as first-use qualification. +Refs: results/independent-onboarding-preparation-20261010/report.md; aa971782 + +## UOU4 · update · 2026-10-11 16:22 +Observed: the independently provisioned guest is at the desktop after human license/account setup. Root observed SIP enabled, the exact read-only qualification volume, four payload checksum passes and guest Terminal com.apple.Terminal2.15. Packetv3 is freshly extracted and uv0.12.5 runs. The human approved the Python3.14.7 license at its actual Agree dialog; root accepted it. Installation now requires guest administrator authentication, handed to the human to keep credentials out of tool logs. Chrome image is mounted read-only with signature assessment pending. No installed diagnostic, TCC grant or native oracle has run; UO2 remains open. +Next: resume at installer authentication, then pristine packet verification and cold dependency install before the denied Accessibility baseline. +Refs: results/u2-independent-onboarding-20261011-v1; guest /Users/tom/footwork-u2; root native CUA tool images in this chat + +## UOU5 · update · 2026-10-11 16:22 +Observed: Chrome guest assessment completed with deep strict codesign valid, Google LLC Team EQHXZ8M8AV, stapled ticket and spctl accepted Notarized Developer ID. The bundle version is155.0.8059.40. Copy to the previously absent guest Applications destination is running; installation completion is not yet observed. Python remains at administrator authentication. +Next: leave the guest authentication prompt for human entry and resume the remaining UO2 gates afterward. +Refs: results/u2-independent-onboarding-20261011-v1; guest /Users/tom/footwork-u2/chrome-signature.txt + +## UOU6 · update · 2026-10-11 16:36 +Observed: the human completed guest installer authentication. Guest Python3.14.7 executes; pristine packet verification exits0 with14members, source33e1afb and the reviewed wheel hash. Frozen cold installer exits0 with115packages, all seven command stages return0. Initial isolated strict doctor exits1 with accessibility_not_granted; Screen Recording is not_granted and advisory. The frozen fixture exits2 with the exact Accessibility refusal, and its run directory is absent. No grant or admitted native action has occurred. Pure-Python fallback emits a noisy unavailable-core warning but executes the permission gate. An initial post-install command reached Finder while Installer opened its folder; root restored Terminal focus and the actual verifier then ran successfully. +Next: record the human approval and exact Terminal Accessibility grant in guest settings, restart the responsible host and read back strict doctor before the native oracle. +Refs: results/u2-independent-onboarding-20261011-v1; guest /Users/tom/footwork-u2/packet-verification.txt; guest packet output/install-v1/receipt.json; guest packet output/doctor-before.json; guest denied-fixture.txt and exit files; root native CUA tool images in this chat + +## UOS2 · summary · 2026-10-11 16:38 +Established: actual independent SIP-enabled guest Python/prerequisite setup, pristine packet verification, frozen115package cold install and a valid installed Accessibility-denied baseline with no fixture directory. Guest Settings visibly attributes the off Accessibility switch to Terminal. Evidence is retained in the guest and observed CUA tool images, with sanitized partial host record at413686b. +Uncertain: human Terminal permission decision, host restart/readback, installed native append/restart/report and owned cleanup remain unexecuted. Full raw guest receipt export is pending. Claude has the concrete baseline review request but no new verdict has arrived. +Decision: keep UO2 open and pause dependent native execution at the exact guest Terminal Accessibility grant confirmation. The permission is scoped to the disposable guest, and host Mac permissions remain untouched. +Refs: results/u2-independent-onboarding-20261011-v1; Claude request1791751059206860000; root native CUA tool images in this chat + +## UOU7 · update · 2026-10-11 16:56 +Observed: the human now explicitly authorizes root to enable the exact guest Terminal Accessibility switch. On inspection the guest is auto-locked; root woke its login display without entering credentials, and no switch was changed. Claude message1791751104201824000 accepts the actual denied baseline, with C1 attribution and C2 receipt-export clarifications. No system control-permission prompt was observed in captured UI states; the Settings list was first inspected only after cold installer diagnostics, strict doctor and fixture refusal, so the exact command that first inserted Terminal cannot be determined retrospectively. Root never manually added it. Source permission_status uses nonprompting AXIsProcessTrusted and CGPreflightScreenCaptureAccess; fixture refuses before importing/creating its CuaDriver. Local Cua source distinguishes those probes from explicit prompting functions, but its currentHEAD is not proof of every transitive effect in the installed0.28.2 build. No permission reset or frozen-wheel change was performed. +Inferred: the observed off entry establishes responsible-host attribution, not the causal claim that a particular command requested a prompt. C1 remains an explicit evidence limitation. +Next: human unlock is needed under the supplied no-credentials-in-logs instruction; thereafter preserve grant authorization, hold guest awake, print denied-receipt SHA256 values, establish a bounded verifiable export route before post-grant execution, then enable Terminal and restart/read back. +Refs: results/u2-independent-onboarding-20261011-v1; Claude message1791751104201824000; python/jevdual/capability/setup.py; scripts/capability_fixture.py; local Cua5458c77b04a6e5e7a099929e0c4d4366e83dd403 libs/cua-driver/rust/crates/cua-driver-sdk/src/lib.rs; root native CUA tool images in this chat + +## UO4 · prediction · 2026-10-11 17:14 +Hypothesis: a post-run, bounded guest-to-host upload preserves raw qualification evidence without borrowing a host environment or changing the permission baseline. +Test: before the grant, retain and visibly print SHA256 hashes of the denied doctor, fixture refusal and install receipt. After native execution and cleanup, build an allowlisted receipt archive in the guest, print its hash, and upload to one temporary receiver bound only to the VM bridge address. Receiver accepts one fixed path, a bounded archive size, exclusive output creation and a five-minute lifetime; no host file reads or commands. Verify archive and individual baseline hashes on the host before sanitizing results. No share, SSH, clipboard or receiver is enabled during qualification. +Predicts: exported bytes match the observed guest archive and pre-grant baseline hashes, and the receiver shuts down after receipt or timeout. +Against: hashes differ, a receipt is absent, transfer requires widening host access beyond the fixed upload, or the receiver remains active after its bound. +Refs: results/u2-independent-onboarding-20261011-v1; guest denied-baseline.sha256; Claude message1791751104201824000 + +## UOU8 · update · 2026-10-11 17:18 +Observed: human reports unlock complete. Root observes Terminal Accessibility already on upon reopening Settings; root never toggled it. The change occurred between the earlier off observation and this on observation, with explicit human consent, but its actor/time are not independently captured. Baseline receipt hashes were printed before the post-restart diagnostic, not demonstrably before the actual grant. Guest Terminal quit visibly, then relaunched; strict installed doctor exits0, Accessibility granted, Screen Recording not_granted and no blockers. The first fixture creates its exact TextEdit document but fails its30second identity deadline while guest Chrome first-launch verification is visibly still running. No scope is emitted and no product action runs. Root declines Python local-network discovery and Terminal TextEdit Automation prompts, closes only the unchanged fixture document manually and preserves the failed fixture directory/log. +Inferred: Chrome cold verification is a candidate startup cause, not yet isolated from SDK window visibility or other readiness causes. The permission transition is observed, but the initial complete first-use lane does not pass. The original denied baseline is retained without a permission reset. +Next: export original failure evidence and review it; run a separately recorded development continuation with the same frozen helpers only after vendor verification completes. Never regrade fixture-v1 as success. +Refs: results/u2-independent-onboarding-20261011-v1; guest fixture-console.txt; guest packet output/doctor-after.json; root CUA observations + +## UO5 · prediction · 2026-10-11 17:18 +Hypothesis: the unchanged fixture can identify owned windows once the official Chrome first-launch verification has completed; the initial failure is startup readiness rather than a broken approved AX/SDK seam. +Test: retain fixture-v1 and its refusal/timeout evidence. Observe normal vendor Chrome verification completion, with no Gatekeeper bypass or added permission. Launch the frozen fixture into fresh fixture-v2; if ready, run the existing installed native oracle in new job-v2/native-v2. Keep declined extra permissions and manually close the exact document when automatic cleanup cannot do so. +Predicts: fixture-v2 emits ready and the installed append/repeated-token/report oracle passes with one operation and one effect under the same Terminal Accessibility grant. +Against: Chrome remains unable to launch normally, scope identification fails again, native oracle refuses or duplicates, or additional permission is necessary for native execution. +Refs: docs/experiments/U2-independent-onboarding.md; guest fixture-console.txt; scripts/capability_fixture.py; scripts/capability_installed_trial.py + +## UOU9 · update · 2026-10-11 17:28 +Resolves: UO5 = held +Observed: unchanged fixture-v2 emits ready after ordinary Chrome verification completes. Config and installed native oracle exit0; AX oracle matches once, installed detached worker contexts are retained, same-token server restart returns the same operation, and read-only journal/report show one final applied row. Exact fixture/browser/worker processes are absent and loopback is closed after cleanup; exact document close requires the operator because Automation was declined. Claude independently verifies the raw archive/journal and accepts the bounded development pass without new runtime blockers. +Inferred: startup readiness is supported as the initial fixture failure explanation, but no isolated causal test proves Gatekeeper was the only cause. Additional TCC grants were unnecessary for product execution. +Next: retain this post-grant continuation separately; develop fixture/setup improvements in a new packet before another complete first-use qualification. +Refs: results/u2-independent-onboarding-20261011-v2-dev; Claude1791754065071525000; preregistration47f38de + +## UOU10 · update · 2026-10-11 17:28 +Resolves: UO2 = inconclusive +Observed: denied installed diagnostics and no-resource refusal are valid; post-restart doctor and the separate post-grant installed native flow pass. The actual grant actor/time remain uncaptured, the initial fixture times out and automatic document close remains unconfirmed. Claude flags the original transition pass condition as unmet. Earlier UOU8 phrase permission transition is observed means only separated off/on states, not a captured grant event; it cannot satisfy the transition gate. +Next: keep full first-use U2 unqualified. A new captured denied-to-approved lane requires an explicitly approved guest reset or fresh environment; no reset is performed here. +Refs: results/u2-independent-onboarding-20261011-v1; results/u2-independent-onboarding-20261011-v2-dev; Claude1791753660836044000 + +## UOU11 · update · 2026-10-11 17:28 +Resolves: UO4 = inconclusive +Observed: both allowlisted raw guest exports match the observed archive hashes; original denied doctor/refusal/install receipt hashes also match byte-for-byte. Both fixed-source-IP/path/size/hash bridge receivers terminate after one upload and are absent during qualification. However the baseline hashes were printed before post-restart doctor, not demonstrably before the actual grant, so the prediction's pre-grant timing claim is not established. +Next: preserve raw and sanitized receipts and use contemporaneous grant capture in the next first-use lane. +Refs: output/u2-export-v1; output/u2-export-v2; results/u2-independent-onboarding-20261011-v2-dev/summary.json + +## UOS3 · summary · 2026-10-11 17:28 +Established: fresh SIP-enabled guest cold install, truthful denied baseline, post-grant installed browser-to-desktop execution, isolated actual detached worker, same-token restart deduplication, journal-validated completion report and bounded receipt export; independently reviewed by Claude. Original first-use failure remains preserved. Only exact owned fixture resources were closed; document cleanup required the operator. +Uncertain: grant actor/time, complete first-use transition, automatic document cleanup and disk persistence. Discovery attribution remains a hypothesis. No new packet or permission reset has been tested. +Decision: close this measured package as a reviewed post-grant development pass and an unqualified full first-use lane. Next packet should use exact-document AX cleanup, distinct browser startup/identity phases and a separately tested discovery-flag hypothesis, grounded in Arc CUA, Chrome DevTools MCP and browser-use source. Keep frozenv3 and product wheel unchanged. +Refs: results/u2-independent-onboarding-20261011-v2-dev/report.md; Claude1791754065071525000; results/u2-independent-onboarding-20261011-v1 diff --git a/jevdual/docs/notebook/2026-10-10-native-camera-experiment.md b/jevdual/docs/notebook/2026-10-10-native-camera-experiment.md new file mode 100644 index 0000000..cb7d8e9 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-10-native-camera-experiment.md @@ -0,0 +1,67 @@ +# Native camera capture experiment + +## NC1 · prediction · 2026-10-10 14:19 +Hypothesis: the installed FFmpeg AVFoundation route can acquire one bounded frame from the observed MacBook Pro Camera and persist an operation-bound artifact without introducing camera dependencies into Footwork's native runtime. +Test: isolated public CLI doctor and capture; explicit observed physical camera selection, no microphone or screen capture; inspect the saved image with ffprobe and hash readback. Conformance checks cover repeated tokens, changed arguments, interrupted capture and tampered/missing artifacts. Camera permission refusal remains a missing prerequisite, not successful acquisition. +Predicts: doctor distinguishes the built-in camera from OBS Virtual Camera; when access is granted, capture produces one decodable image with an operation-bound receipt; repeat lookup does not reacquire; uncertain operations remain blocked rather than replayed. +Against: camera selection broadens to screen/virtual sources, microphone opens, an unobserved device is accepted, capture claims success without decodable saved bytes, repeated lookup acquires a second frame, or permission/lost evidence is promoted to verified acquisition. +Refs: tools/camera-capture-experiment/; docs/experiments/NC1-native-camera.md; /Volumes/External SSD/referencerepos/libgphoto2/examples/sample-trigger-capture.c; /Volumes/External SSD/referencerepos/rerun/examples/python/live_camera_edge_detection/live_camera_edge_detection.py + +## U-NCACCESS · update · 2026-10-10 14:22 +Observed: system camera inventory includes MacBook Pro Camera and OBS Virtual Camera; installed FFmpeg AVFoundation inventory matches exactly one MacBook Pro Camera. A separate Swift AVFoundation authorization query returns notDetermined. No camera has been opened and no image exists. +Inferred: built-in camera discovery is supported here; Swift permission state does not qualify the FFmpeg process identity or actual frame acquisition. +Next: complete isolated CLI conformance, freeze implementation, then explicit camera-only first-use capture under the user's authorized scope; OS permission and saved-image proof remain necessary. NC1 stays open. +Refs: results/native-camera-20261010/preflight.json; tools/camera-capture-experiment/ + +## U-NCMODE · update · 2026-10-10 14:30 +Observed: frozen source 2bccd45 passed 18 conformance checks and Ruff. Live-v1 FFmpeg dispatch rejected its default 29.970030 fps; private diagnostic lists device modes spanning 15 to 30 fps, and no PNG was saved. Repeat of the identical token returns the original unknown operation without a new intent. NC1 remains open. +Inferred: a mode prerequisite, rather than saved-image verification, prevented acquisition; actual permission and acquisition remain unqualified. +Next: preregister explicit 30 fps and preserve live-v1 unchanged; a distinct trial is a new capture operation. +Refs: results/native-camera-20261010/conformance.json; results/native-camera-20261010/live-v1.json; tools/camera-capture-experiment/output/live-v1/ + +## NC2 · prediction · 2026-10-10 14:30 +Hypothesis: explicitly requesting the device-reported 30 fps eliminates the unsupported-default frame-rate failure while preserving capture argument binding and unknown outcomes. +Test: add a fixed 30 fps input option and bind it into the request; retain conformance checks, freeze amended source, and perform one distinct live-v2 native-camera operation. +Predicts: command requests 30 fps; live-v2 no longer fails with unsupported 29.970030 fps. Successful acquisition additionally requires permission and a decoded saved artifact; another failure remains unknown. +Against: FFmpeg still rejects 30 fps as unsupported, the request omits the mode, changing a retained mode replays an operation, or failure becomes a success without artifact proof. +Refs: results/native-camera-20261010/live-v1.json; docs/experiments/NC1-native-camera.md + +## U-NC2 · update · 2026-10-10 14:33 +Resolves: NC2 = held +Observed: amended source 48b33a1 binds fixed 30 fps; all 18 conformance checks and Ruff pass. Live-v2 does not report unsupported framerate, but reaches its bounded timeout with no saved PNG. Status after process restart retains unknown and empty claims. No FFmpeg process remained. Swift still reports not_determined for its separate privacy identity. NC1 remains open. +Inferred: the 30 fps change removes the known mode failure but does not establish actual authorization or first-frame delivery. The unsupported default pixel-format message is not a sufficient cause: FFmpeg documents automatic fallback to its first supported format. +Next: establish the human's observation of any macOS camera prompt before further live acquisition; preserve both prior operations unchanged. +Refs: results/native-camera-20261010/live-v2.json; results/native-camera-20261010/conformance-v2.json; results/native-camera-20261010/report.md; https://ffmpeg.org/ffmpeg-devices.html#avfoundation + +## S-NCPARTIAL · summary · 2026-10-10 14:33 +Established: an isolated public capture/status CLI, exact local discovery, conservative unknown outcomes and restart lookups; fixture conformance and lint pass. A committed explicit 30 fps setting eliminates the observed unsupported-default frame-rate failure. +Uncertain: live native-frame acquisition, camera permission identity and first-frame delivery; exposure freshness, scene semantics and useful desk workflows remain unqualified. +Decision: retain the bounded prototype in tools/camera-capture-experiment, leave NC1 open, and hand off the permission observation prerequisite before another distinct live trial. No production native-runtime adoption is supported by these results. +Refs: results/native-camera-20261010/report.md; docs/experiments/NC1-native-camera.md; 48b33a1 + +## NC3 · prediction · 2026-10-10 14:40 +Hypothesis: the user being present to answer a first-use prompt, with a 60-second capture bound, allows the unchanged 30 fps route to establish frame acquisition if unattended authorization was the remaining prerequisite. +Test: the user explicitly requested another attempt after being AFK; preserve prior unknown operations, use a distinct live-v3 run and token, fixed source 48b33a1, and a 60-second bound. Verify any saved PNG independently, then repeat the token and status without new acquisition. +Predicts: if a prompt was the prerequisite and is allowed, the new operation yields one decoded saved PNG and retained receipt. Otherwise it remains unknown/refused, which does not diagnose permission as the cause. +Against: authorization is confirmed yet no frame is acquired within the bound, or repeated lookup reopens the camera or promotes missing evidence. +Refs: results/native-camera-20261010/report.md; tools/camera-capture-experiment/output/live-v3/; 48b33a1 + +## U-NC3 · update · 2026-10-10 14:41 +Resolves: NC1 = held +Observed: attended live-v3 on unchanged 48b33a1 completes acquisition. Independently executed ffprobe and FFmpeg decode one 1080 by 1920 PNG; hash and byte count match the receipt. Fresh-process capture repeat and status retain the original intent, receipt and artifact; ledger bytes are unchanged, exactly one PNG remains, and no FFmpeg process remains. NC1 frame-acquisition feasibility is established. The user has not described any actual permission prompt or action, so the proposed cause in NC3 is inconclusive. +Inferred: the observed camera route can acquire a retained frame with the current host state. Success does not identify the prior timeout cause or qualify exposure freshness, scene interpretation, hardware authentication or useful physical workflows. +Next: close this acquisition package; use its retained artifact seam for a separately scoped desk workflow experiment. +Refs: results/native-camera-20261010/live-v3.json; results/native-camera-20261010/report.md; c8f71e6; 48b33a1 + +## U-NCCAUSE · update · 2026-10-10 14:41 +Resolves: NC3 = inconclusive +Observed: live-v3 acquired a verified frame, but the actual prompt history and prior timeout cause were not observed; both user presence and timeout bound differ from live-v2. +Inferred: acquisition success does not establish the proposed permission mechanism. +Next: close feasibility without assigning a cause to the earlier timeout. +Refs: results/native-camera-20261010/live-v3.json; results/native-camera-20261010/report.md + +## S-NCCLOSED · summary · 2026-10-10 14:41 +Established: exact observed native-camera selection, one decoded saved frame with matching receipt, cross-process repeat/status lookup and cleanup on this Mac; conformance checks pass on the frozen implementation. Private frame and diagnostics remain excluded from Git. +Uncertain: why live-v2 timed out; actual prompt history, authenticated camera identity, exposure time, image utility and any robotic or document effects. +Decision: retain the isolated experiment under tools/camera-capture-experiment; frame acquisition is qualified here, while production adapter adoption and scene-driven effects need a separate experiment. +Refs: results/native-camera-20261010/live-v3.json; results/native-camera-20261010/report.md; docs/experiments/NC1-native-camera.md diff --git a/jevdual/docs/notebook/2026-10-10-physical-workcell.md b/jevdual/docs/notebook/2026-10-10-physical-workcell.md new file mode 100644 index 0000000..d0fceff --- /dev/null +++ b/jevdual/docs/notebook/2026-10-10-physical-workcell.md @@ -0,0 +1,21 @@ +# Simulated physical and computer workcell + +## SIM1 · prediction · 2026-10-10 15:28 +Hypothesis: native MuJoCo contact dynamics plus a bounded XY controller can move a simulated block into a task-defined zone, enabling a subsequent computer work-order update only after observed state qualifies. +Test: isolated agent-facing init/observe/act/complete public CLI on pinned MuJoCo 3.15.0, state persistence between processes, explicit tokens/generations/bounds, before/after physical poses, conditional saved result independently checked through Footwork. Freeze source/model before measured run; test counterexamples and no replay. +Predicts: contacts actually move the free block; software result appears once only after the in-zone predicate; false/stale/out-of-envelope/unknown requests cannot become success or replay. Simulator evidence is labeled separately from camera/real hardware. +Against: block teleport or implicit reset, no-op falsely verified, outside-zone computer completion, unbounded/stale dispatch, token replay or simulated evidence claimed as real-world control. +Refs: docs/experiments/SIM1-physical-computer-workcell.md; /Volumes/External SSD/referencerepos/mujoco/; /Volumes/External SSD/referencerepos/lerobot/src/lerobot/robots/robot.py; results/workbench-20261010/report.md + +## SIM1-U1 · update · 2026-10-10 15:37 +Resolves: SIM1 = held +Observed: frozen source/model ece54ae moved the free block by native contact into the bound zone, refused premature software completion, retained physics across public CLI processes, refused stale/out-of-envelope requests and did not redispatch repeated tokens. The conditional local order passed a separate public Footwork byte check. Actual native before/after render images were inspected. +Inferred: this bounded simulator/controller interface can couple observed physical fulfillment to a subsequent local computer effect. It does not qualify a visual policy or real hardware. +Next: close SIM1; camera-grounded observation is the next proposed physical-body experiment. +Refs: results/physical-workcell-20261010/independent-demo.json; results/physical-workcell-20261010/report.md; tools/physical-workcell/test_workcell.py + +## SIM1-S1 · summary · 2026-10-10 15:37 +Established: bounded contact action, persisted observation generations, conditional local work-order completion and no replay demonstrated on pinned native MuJoCo. The independent public run passed; scoped actual-engine conformance covers local refusal/failure paths. +Uncertain: learned planning, camera grounding, general manipulation/navigation, hardware transfer, controller timing and external software integration remain unqualified. +Decision: retain the prototype inside tools/physical-workcell and the project-linked notes as supporting evidence. Build camera-grounded observation before expanding to additional bodies; preserve separation between model intent, controller execution and physical/computer readback. +Refs: docs/experiments/SIM1-physical-computer-workcell.md; results/physical-workcell-20261010/report.md; results/workbench-20261010/route-profile.json diff --git a/jevdual/docs/notebook/2026-10-10-project-workbench.md b/jevdual/docs/notebook/2026-10-10-project-workbench.md new file mode 100644 index 0000000..6b699f9 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-10-project-workbench.md @@ -0,0 +1,35 @@ +# Project-linked workbench notes + +## WB1 · prediction · 2026-10-10 15:11 +Hypothesis: a reviewed physical note plus explicit caller-supplied project/question/check metadata can produce a truthful project-bound, independently checked workbench packet without taking the proposed action. +Test: implement isolated link/status/list with exact namespaced project binding, retained desk source and code/revision context; freeze source, run focused conformance and public integration using the user's first live note and chosen Footwork project. +Predicts: one JSON/Markdown record preserves evidence versus interpretation, labels its next check proposed, and verifies exact saved bytes; repeats reuse it; changed/missing/tampered bindings and uncertain writes refuse replay; repo drift is separate from saved bytes. +Against: forged source/carries, name-only project matching, silent different-project linkage, task execution from note text, false completed-check claims, duplicate records or false artifact verification. +Refs: docs/experiments/WB1-project-workbench.md; results/desk-inbox-20261010/report.md; tools/desk-inbox-experiment/; /Volumes/External SSD/referencerepos/openhtf/openhtf/core/test_record.py + +## WB2 · prediction · 2026-10-10 15:11 +Hypothesis: current on-demand note/hash/JSON work is small compared with public process startup and native OCR, so a Rust rewrite of the orchestration is not supported by the current workload. +Test: measure bounded local repeated file/hash/exact-artifact library work, public CLI/status startup, and one real Vision invocation on retained fixture; record medians/ranges and input hashes on fixed source. No new camera or provider calls. +Predicts: direct small-note validation and image hashing take materially less time than public command/native OCR stages; identify existing native work and process overhead separately. +Against: direct Python processing or hashing dominates the measured route, a sustained high-rate frame workload is found, or local native work was wrongly assumed to be Python. +Refs: docs/rust-boundary.md; crates/jevdual-core/Cargo.toml; tools/desk-inbox-experiment/recognize.swift; results/desk-inbox-20261010/source.json + +## U-WB1 · update · 2026-10-10 15:26 +Resolves: WB1 = held +Observed: frozen 419a677 links the previously approved real paper source to the exact user-selected Footwork project. Public independent checks confirm JSON/Markdown bytes; fresh-process repeats preserve source/manifest/artifacts and project-filtered list returns one record. Caller interpretation and proposed not_run check remain separate. All 15 tests plus 11 subtests and Ruff pass, including unknown/no replay and context drift cases. +Inferred: local project association and checked handoff are established, not autonomous physical/computer execution or daily utility. +Next: close the workbench package; following user steering, prioritize a physical body plus computer-action simulation. +Refs: results/workbench-20261010/live-link.json; results/workbench-20261010/source.json; results/workbench-20261010/report.md + +## U-WB2 · update · 2026-10-10 15:26 +Resolves: WB2 = held +Observed: local retained-frame profiling records direct note check below 1ms and 2.46MB image read/hash around 1.3ms median, versus fresh public CLI around 201ms and Swift process plus real printed OCR around 452ms. Hashing uses native _hashlib. No sustained-video or Rust alternative was measured. Native DOM paint/hash/evidence backends are observed; snapshot lookup is pure fallback. +Inferred: current on-demand file plumbing is not the dominant cost; helper/process overhead warrants attention before a Rust rewrite. Future streaming/control workloads remain unmeasured. +Next: retain Python/Swift/native media boundaries; reassess sustained frame/control loops separately. +Refs: results/workbench-20261010/route-profile.json; results/workbench-20261010/source.json; docs/rust-boundary.md + +## S-WBCLOSED · summary · 2026-10-10 15:26 +Established: explicit project-bound provenance, meaningful proposed check and independent saved-artifact verification; local route timings distinguish native work from orchestration. +Uncertain: daily utility, semantic interpretation quality, action authority and any physical actuator effects. +Decision: keep notes as supporting machinery and pursue the user's revised priority of physical actuation plus computer actions; user chose simulation with no robot available. +Refs: results/workbench-20261010/report.md; docs/plans/workbench-notes-directions.md diff --git a/jevdual/docs/notebook/2026-10-10-repeatable-visual-work-orders.md b/jevdual/docs/notebook/2026-10-10-repeatable-visual-work-orders.md new file mode 100644 index 0000000..1c18f34 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-10-repeatable-visual-work-orders.md @@ -0,0 +1,27 @@ +# Repeatable visual work orders + +## SIM3 · prediction · 2026-10-10 16:34 +Hypothesis: acquired RGB evidence can remain inspectable despite recognition failure, while exact case/calibration bindings and visual verdicts preserve conservative action admission across varied work orders. +Test: separate development fixtures and actual native renderer/engine conformance; freeze source, then six parent-owned case runs for a deterministic RGB controller and a fresh host with no parent trajectory. Record physical/software outcomes, refusals, setup failures, visual verdicts before oracle checks and interventions. +Predicts: ordinary jobs can finish; failed perception/calibration remains visible but cannot authorize completion; all twelve episodes remain counted; no native pose guidance or effect replay; independent scoring distinguishes declared versus supported outcomes. +Against: recognition drops frames, unsupported evidence acts, native/author-guided host targets, omitted failures, oracle-hidden visual false positives or duplicate effects. +Refs: docs/experiments/SIM3-repeatable-visual-work-orders.md; results/image-guided-workcell-20261010/report.md; /Volumes/External SSD/referencerepos/lerobot/src/lerobot/robots/robot.py + +## SIM3-U1 · update · 2026-10-10 16:52 +Observed: pre-freeze development conformance caught a frame-packet variable NameError; its failure summary and corrected tests are retained. Frozen b9941ef passed 50 scoped native/renderer, case, image, lifecycle and evaluator tests plus Ruff. The six once-run deterministic qualification episodes completed all three ordinary jobs and left all three challenge jobs unresolved with inspectable frames and no unsupported effects. The fresh host has started independently with public documentation and opaque case inputs, without baseline results or previous trajectories. +Inferred: the case/evidence interface works for the deterministic comparator; the fresh-host usability/completion question remains open until its full set is scored. Baseline movement already works without an AI planner in these ordinary cases. +Next: collect and independently score all fresh-host episodes, retaining failures, elapsed time and interventions; close only the supported comparison. +Refs: results/repeatable-work-orders-20261010/checks.json; baseline-v1.json; tools/physical-workcell/output/sim3-episode-development-conformance/initial-failure-summary.txt; b9941ef + +## SIM3-U2 · update · 2026-10-10 16:59 +Resolves: SIM3 = held +Observed: independent scoring of all twelve once-run episodes confirms each participant completed 3/3 ordinary jobs and left all three challenges unresolved with images retained and zero effects. No incorrect completion claims or visual false positives were observed. The fresh host inspected 19 images, executed 13 movements and declared zero external interventions. Its first doctor-output redirection failed before execution; self-correction ran doctor after first initialization and before viewing. Read-only host scoring preserved all run-file hashes; frozen source/case hashes remain unchanged. +Inferred: the bounded case/evidence interface works for these varied jobs, while movement completion supplies no observed AI advantage. The host's self-reported shared-filesystem role separation and one setup correction qualify fresh-use conclusions; strict doctor-before-init and clean-machine setup are not established. +Next: close SIM3 with complete denominators and retained deviations; separately preregister independent controller recovery before adding lost-reply reconciliation or a real application handoff. +Refs: results/repeatable-work-orders-20261010/report.md; comparison.json; fresh-host-v1.json; baseline-v1.json; b9941ef + +## SIM3-S1 · summary · 2026-10-10 16:59 +Established: immutable varied cases, acquisition independent of recognition, pre-oracle visual verdicts, bounded episode declarations and independent scoring; 50 actual-engine/renderer tests and Ruff passed. Both participants completed ordinary contact-and-saved-artifact jobs and retained all unresolved challenged episodes. +Uncertain: general perception accuracy, AI task value, clean-machine discovery, adversarial isolation, independent controller recovery, real camera/robot transfer, application fulfillment and production capability-service governance. +Decision: keep the experiment under tools/physical-workcell and the deterministic controller for bounded movement. Keep later controller/app work separate and measure AI value in interpreting orders, resolving exceptions or coordinating software before promoting a physical adapter. +Refs: docs/experiments/SIM3-repeatable-visual-work-orders.md; results/repeatable-work-orders-20261010/report.md; b9941ef diff --git a/jevdual/docs/notebook/2026-10-10-versioned-application-fulfillment.md b/jevdual/docs/notebook/2026-10-10-versioned-application-fulfillment.md new file mode 100644 index 0000000..331d529 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-10-versioned-application-fulfillment.md @@ -0,0 +1,65 @@ +# Versioned browser work-order fulfillment + +## SIM5 · prediction · 2026-10-10 19:17 +Hypothesis: an immutable leased order snapshot plus a current-revision compare-and-swap and exact controller evidence can connect simulated fulfillment to saved browser application state without stale or duplicate completion. +Test: local Python/SQLite app and public controller adapter, actual browser read/bind/complete, native RGB contact, changed/cancelled order during motion, lost reply and restart/conflicting identity/current artifact faults; freeze source before independent trials. +Predicts: only the same open app/order revision with supported current staged evidence commits fulfillment; changed/cancelled orders stay unfulfilled, exact repeated operation creates no second effect, and current/history remain distinct across restarts. +Against: client success flags or arbitrary paths authorize completion, wrong/stale order changes, duplicate update after lost reply, physical cancellation becomes rollback, missing current evidence becomes success, or restart loses identity and redispatches. +Refs: docs/experiments/SIM5-versioned-application-fulfillment.md; results/independent-controller-20261010/report.md; tools/physical-workcell/CONTROLLER.md + +## SIM5-ID · prediction · 2026-10-10 19:33 +Question: can a cached browser page mutate a replacement app at the same loopback origin before checking its reply identity? +Hypothesis: reply-only app identity checks leave bind/edit/cancel vulnerable to a same-origin replacement with matching order ID and revision. +Test: wrong or absent expected application identity on a real mutation; check unchanged SQLite order and lease rows, then require the immutable app identity before dispatch and rerun. +Predicts: the current server accepts a mutation carrying no or foreign app identity; an exact expected-app header gate refuses before any effect while valid writes remain supported. +Against: current mutation authority already includes the cached immutable application ID and refuses the request before changing state. +Refs: tools/physical-workcell/order_app.py Handler.gate; tools/physical-workcell/order_app.html api; tools/physical-workcell/test_order_app.py + +## SIM5-ID-U1 · update · 2026-10-10 19:34 +Resolves: SIM5-ID = held +Observed: test_order_app identity regression failed in both subcases before the fix: missing identity edited the order and a valid foreign application identity cancelled it. The retained before/after rows show the effects; the parent conformance result is failed. +Inferred: reply-only identity checking is too late for mutation authority. The prediction heading has a one-minute transcription error; commit 46aa8f4 settles its ordering before this test. +Changes: require a single exact expected application identity header before mutation, and send the cached immutable app identity from the browser. +Next: rerun this regression and the affected app/adapter conformance; freeze corrected source before independent qualification. +Refs: tools/physical-workcell/output/a56bb315a/conformance.json; 46aa8f4; tools/physical-workcell/test_order_app.py + +## SIM5-U1 · update · 2026-10-10 19:38 +Resolves: SIM5 = failed +Observed: the first implementation permitted mutations with missing or foreign expected application identity; SIM5-ID's failed conformance preserved the affected order rows. Corrected app conformance passed after adding a pre-effect identity gate. +Inferred: the initial implementation did not satisfy the wrong-app refusal prediction. Backend conformance does not yet establish the complete browser/physical handoff. +Next: freeze the corrected runtime and run the independent browser and integration trials under SIM5-R. +Refs: tools/physical-workcell/output/a56bb315a/conformance.json; SIM5-ID-U1; tools/physical-workcell/test_order_app.py + +## SIM5-R · prediction · 2026-10-10 19:38 +Revises: SIM5 +Hypothesis: the corrected pre-effect app identity gate, immutable revision lease, guarded public controller handoff and transactional receipt can complete a real browser/physical loop without stale or duplicate application fulfillment. +Test: freeze corrected source; root drives actual isolated-browser read/bind/fulfill with image-guided contact, and independent public-API trials cover all seven preregistered integration scenarios with separate persisted scoring and retained failures. +Predicts: the exact still-open leased app/order commits one fulfilled revision only with supported staged/current evidence; edits/cancellation interrupt further work, wrong identities refuse before mutation, and lost replies/restarts recover history without replay. +Against: any independent scenario accepts a wrong/stale mutation, creates a duplicate fulfillment, invents native stop/completion or fails the normal supported browser loop. +Refs: docs/experiments/SIM5-versioned-application-fulfillment.md; tools/physical-workcell/ORDER_APP.md; results/versioned-application-fulfillment-20261010/ + +## SIM5-DEV-U2 · update · 2026-10-10 19:39 +Observed: the first malformed-retained-receipt conformance attempt was refused before staging because an overlapping final HTML caption/cache change altered the bound app source. Its log is retained; it does not resolve the malformed-receipt check. +Inferred: runtime source identity invalidation behaved conservatively, but this attempt is not evidence for the intended receipt predicate. +Next: hold all app source stable, rerun the new check in fresh owned fixtures and the affected final-source suites, then freeze before independent browser/integration trials. +Refs: tools/physical-workcell/output/h5-receipt-before-type-fix/unittest.log; tools/physical-workcell/test_app_handoff.py + +## SIM5-DEV-U3 · update · 2026-10-10 19:40 +Observed: on a fresh stable-source fixture, controlled corruption of a retained application receipt/current-order row exposed three false-positive reconciliation subcases: boolean leased revision, cancelled current terminal state, and current revision preceding the fulfillment receipt. The actual HTTP responses were retained and the native history was not changed. +Changes: the adapter now requires an integer leased revision, exact fulfilled receipt identity/version, a fulfilled current order at or after its recorded revision, and the same canonical work-order digest. +Inferred: assigned IDs alone are insufficient for typed historical application-effect claims. This is a conformance fault on owned persisted data, not a hardware/security qualification. +Next: finish final-source adapter/backend suites, freeze all measured source, then run independent SIM5-R trials. +Refs: tools/physical-workcell/output/h5-receipt-before-type-fix-v2/unittest.log; tools/physical-workcell/test_app_handoff.py + +## SIM5-R-U1 · update · 2026-10-10 19:46 +Resolves: SIM5-R = held +Observed: frozen bd896e1 completed the isolated-browser order binding, four image-guided native-contact moves, staged artifact verification and browser fulfillment. Separate HTTP/SQLite scoring found exactly one receipt at fulfilled revision 2; browser reload preserved it. Independent public-API trials passed all seven first attempts with unchanged source, including native-step-confirmed edit/cancel stops, lost-reply reconciliation, identity/conflict refusals, restart history and current artifact tampering. +Inferred: the corrected bounded local handoff connects physical evidence to saved application state while preserving explicit stale/current/history boundaries. It does not establish general visual-policy performance or hardware transfer. +Next: close this package with retained failures, evidence and owned-process cleanup; consider persistent body identity across successive work orders separately. +Refs: bd896e1; results/versioned-application-fulfillment-20261010/report.md; results/versioned-application-fulfillment-20261010/summary.json; results/versioned-application-fulfillment-20261010/browser-result.json + +## SIM5-S · summary · 2026-10-10 19:46 +Established: Python/SQLite application revision leases and a public controller adapter completed a real browser/native-image/software loop, with 95 affected checks and seven independent diagnostic scenarios supporting the corrected implementation. Wrong-app mutation and malformed reconciliation development failures are preserved; all trial-owned processes/sockets and the temporary browser tab were cleaned up. +Uncertain: physical/application proof is not atomic; the host must invoke guard to interrupt stale jobs; each lease currently owns a separate simulated episode. General robotics, a physical shared-body scheduler, camera/hardware calibration, operator demand and production capability governance are unqualified. +Decision: keep the bounded local app/handoff as an experimental Footwork tool, with deterministic authority and host planning. A subsequent package should separate persistent body/resource ownership from changing task identity. +Refs: results/versioned-application-fulfillment-20261010/; docs/experiments/SIM5-versioned-application-fulfillment.md; tools/physical-workcell/ORDER_APP.md diff --git a/jevdual/docs/notebook/2026-10-11-agent-visual-bench.md b/jevdual/docs/notebook/2026-10-11-agent-visual-bench.md new file mode 100644 index 0000000..e3efe99 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-11-agent-visual-bench.md @@ -0,0 +1,27 @@ +# AI visual control of the reusable bench + +## SIM7-P1 · prediction · 2026-10-11 14:20 +Hypothesis: an external multimodal AI can complete two successive prepare-measure-contact-reset cycles from images and task metadata, without detector/native pose guidance or weakening the existing guards. +Test: freeze a typed agent handoff, use the AI in this Codex session to inspect each RGB frame and author proposals, interrupt a prepared second-job measurement with a diagnostic native force, then independently score state and SQLite results after the attempt. +Predicts: two complete cycles, one measurement per job, one continuous bench identity, stale proposal refusal and fresh-image corrective actions after disturbance, with unchanged native guard source. +Against: native poses or the deterministic coordinate planner guide actions, stale evidence dispatches, either cycle exceeds its motion budget, reset assigns block state or guards need weakening. +Refs: docs/experiments/SIM7-agent-visual-bench.md; tools/physical-workcell/BENCH.md; results/agent-visual-bench-20261011/ + +## SIM7-DEV-U1 · update · 2026-10-11 14:27 +Observed: source review required stored unknown host results to block future proposals even when the underlying bench was ready. A new native-move/lost-host-reply check exercised that gap and passed after the host guard was implemented. Public CLI responses now filter detector coordinates from saved measurement/status receipts while retaining full evidence privately. Fifteen final interface conformance checks passed. Four initial test errors reflected exception classes from separately loaded helper modules; their fixture evidence is retained and assertions were corrected without changing native guard behavior. +Inferred: effect readiness alone is insufficient to resolve a host's uncertain dispatch, and measurement output can accidentally become numeric perception guidance unless the model-input boundary filters it. +Next: freeze measured source, initialize a fresh bench/session, and run actual AI-authored image-based actions before independent scoring. +Refs: tools/physical-workcell/output/a7-suite-b8f88806f5/suite.log; tools/physical-workcell/test_agent_bench.py; tools/physical-workcell/agent_bench.py + +## SIM7-U1 · update · 2026-10-11 14:39 +Resolves: SIM7-P1 = held +Observed: frozen 9a5d2f8 completed two first-attempt jobs with the Codex multimodal AI authoring 29 proposals bound to presented images. A saved second-job measurement refused after a 0.13303 m native-force displacement. The next image informed corrective motion, followed by verified measurement and contact reset. Separate scoring after control found both target predicates, one SQLite result per job, both home predicates, continuous body identity/time and unchanged native guard source. Fifteen handoff and fourteen bench checks passed. +Inferred: the guarded observation/proposal boundary supports this actual AI host across successive jobs and a bounded displacement, without a deterministic coordinate planner or numeric detector guidance in model inputs. +Next: close this package with source, proposals, image-presentation audit, scoring, replay and process outcomes retained. A provider-backed unattended loop is a separate implementation and qualification. +Refs: 9a5d2f8; results/agent-visual-bench-20261011/report.md; results/agent-visual-bench-20261011/AI-action-audit.json; results/agent-visual-bench-20261011/native-sqlite-score.json + +## SIM7-S · summary · 2026-10-11 14:39 +Established: typed external-AI proposals, SHA-bound observations and source identity, durable dispatch and unknown-outcome blocking, filtered model inputs, actual image-based AI control of two jobs, stale proposal refusal and corrective recovery, and separate physical/software scoring. No native guard changes, provider calls or hardware effects were introduced. All CLI and measurement subprocesses returned; evidence remains retained. +Uncertain: unattended policy hosting, model-origin authentication, repeatability across fresh model sessions, blind/unseen disturbance recovery, arbitrary object perception, hardware transfer and production capability governance remain unqualified. The first narrow AI mechanism prediction held; no failed frozen trial was discarded or retried. +Decision: retain the visual AI handoff as an experimental Footwork tool, with the AI choosing actions and deterministic code owning effect authority. Use this interface for later policies or instruments without weakening the existing guards. +Refs: docs/experiments/SIM7-agent-visual-bench.md; tools/physical-workcell/AGENT_BENCH.md; results/agent-visual-bench-20261011/ diff --git a/jevdual/docs/notebook/2026-10-11-bench-operator.md b/jevdual/docs/notebook/2026-10-11-bench-operator.md new file mode 100644 index 0000000..f5d7a4e --- /dev/null +++ b/jevdual/docs/notebook/2026-10-11-bench-operator.md @@ -0,0 +1,77 @@ +# Unattended visual AI and the bench operator product + +## SIM8-P1 · prediction · 2026-10-11 15:24 +Hypothesis: a bounded independent Codex vision process can drive the existing proposal API across two queued prepare-measure-reset jobs, while a local browser product truthfully exposes progress and pause behavior without widening effect authority. +Test: build the policy/runtime/browser boundaries, run labelled deterministic conformance, freeze source before live model requests, enqueue two jobs through the actual browser including a saved-decision disturbance, then separately score physics, SQLite and UI reload readback. +Predicts: two verified cycles, one measurement per job, continuous bench state, stale refusal and fresh-image recovery, pause preventing late model-result dispatch, unchanged native guards and preserved evidence for all calls and outcomes. +Against: chat-directed targets or an automatic coordinate planner are required, stale or paused decisions dispatch, unknown effects replay, the UI invents completion, or native guards need weakening. +Refs: docs/experiments/SIM8-bench-operator.md; tools/physical-workcell/AGENT_BENCH.md; results/bench-operator-20261011/ + +## SIM8-CLI · prediction · 2026-10-11 15:33 +Hypothesis: the installed authenticated Codex CLI can return one schema-valid image-based action with action tools disabled, using the current bounded policy adapter without key provisioning. +Test: commit the policy source, render a fresh native preparation image, invoke one CLI decision with no physical dispatch, and retain exit/schema/usage evidence separately from the primary product qualification. +Predicts: a bounded completed model process returns a valid typed action and reported usage, with no tool events and no native state change. +Against: configuration or authentication fails, tools are invoked, the model cannot accept the image/schema, output is invalid, or the probe dispatches a bench action. +Refs: tools/physical-workcell/codex_policy.py; tools/physical-workcell/output/sim8-policy-smoke1/ + +## SIM8-CLI-U1 · update · 2026-10-11 15:36 +Resolves: SIM8-CLI = failed +Observed: the first committed-source probe exited 0 with a valid movement action but the adapter refused it as unexpected tool use. The emitted error items were two nonfatal startup diagnostics: disabled code-mode host and shortened skill descriptions. No tool call or native dispatch occurred; all raw output remains retained. +Inferred: the event classifier confused startup diagnostics with tool execution. This does not establish the complete accepted policy transport. +Next: retain this failure, allow only those exact diagnostic prefixes, keep arbitrary errors and actual tool items refused, rerun focused checks, commit the revised prediction and policy, then probe a fresh call before product qualification. +Refs: 3e6475e; tools/physical-workcell/output/sim8-policy-smoke1/model-call/outcome.json; tools/physical-workcell/test_codex_policy.py + +## SIM8-CLI-R · prediction · 2026-10-11 15:36 +Revises: SIM8-CLI +Hypothesis: separating the two known nonfatal startup diagnostics from tool events permits a valid bounded model action without admitting actual tool use or arbitrary provider errors. +Test: commit the corrected classifier and invoke one fresh no-dispatch decision, then check schema, usage, process exit and unchanged native generation. +Predicts: valid typed action accepted with reported usage, no actual tool items, process reaped and native generation zero; focused unknown-error/tool tests still refuse. +Against: actual tools or arbitrary errors are accepted, a normal action still fails, or the bench moves during the transport probe. +Refs: tools/physical-workcell/output/sim8-policy-smoke1/model-call-v2/; tools/physical-workcell/test_codex_policy.py + +## SIM8-CLI-U2 · update · 2026-10-11 15:42 +Resolves: SIM8-CLI-R = held +Observed: the revised committed-source probe accepted a schema-valid movement decision from the exact PNG, reported token usage, and reaped the CLI process after 7.46 seconds. Native generation remained zero with no dispatch. Focused conformance still refuses unknown errors and tool items. +Inferred: the installed authenticated CLI supports the bounded decision transport; full unattended job completion remains untested. +Next: freeze the complete operator product after conformance and browser preview, then run the primary two-job qualification through its UI. +Refs: 7867d96; tools/physical-workcell/output/sim8-policy-smoke1/probe-v2.json; tools/physical-workcell/test_codex_policy.py + +## SIM8-CONFORMANCE · summary · 2026-10-11 15:44 +Established: 19 deterministic operator checks, 3 policy checks, 15 agent checks and 14 bench checks passed. The browser preview showed the idle camera, new-job form and pause/resume. Review corrected the pause/resume control version, source/image identity, completion readback and incomplete token reporting. Existing native guard source is unchanged. +Uncertain: fake policy conformance establishes no live AI job completion. The first complete frozen model/product attempt remains open. +Decision: commit the product and separate post-trial scorer, freeze their exact bytes, and drive the two-job qualification through the browser without chat-directed motion. +Refs: tools/physical-workcell/output/sim8-final-runtime-tests.log; tools/physical-workcell/output/sim8-final-policy-tests.log; tools/physical-workcell/output/sim8-final-agent-tests.log; tools/physical-workcell/output/sim8-final-bench-tests.log; tools/physical-workcell/output/sim8-empty-preview/ + +## SIM8-U1 · update · 2026-10-11 15:53 +Resolves: SIM8-P1 = inconclusive +Observed: both queued AI jobs completed with measurements and resets; actual inference cancellation dispatched nothing; browser reload retained both results. Independent scoring first collided with the live ownership lock, then refused the PNG because the scorer used the smaller protocol-data byte bound. Review also found a queued-job admission race after a pause acknowledgement and a disturbance-scoring predicate that did not independently establish the force/displacement chain. +Inferred: the observed model/product workflow completed, but this frozen qualification lacks its required complete independent score. Cancelled-motion authority remained guarded; queue admission and scoring need repair before product handoff. +Next: retain version 1 unchanged as an inconclusive qualification, reproduce the admission race, repair the scorer with explicit media bounds and a native force chain, then freeze a second complete product qualification. +Refs: e7b716a; tools/physical-workcell/output/sim8-qualified-20261011-v1/; tools/physical-workcell/score_operator.py; tools/physical-workcell/operator_runtime.py + +## SIM8-P2 · prediction · 2026-10-11 15:53 +Revises: SIM8-P1 +Hypothesis: checking pause admission inside the step lock prevents a queued job start after pause; an explicit PNG bound and exact independent force/displacement chain permit complete scoring of the already bounded visual workcell workflow. +Test: demonstrate the paused-step fixture fails on version 1 and passes after the admission fix; repair scoring, run focused conformance, commit all measured source, and drive two new UI-queued model jobs with the same regions and saved-measurement disturbance. Stop the owned UI/server before post-trial scoring. +Predicts: no post-pause job start or call reservation; two verified cycles on one new persistent bench; a native displacement greater than 2 cm after a saved proposal, an exact stale refusal without measurement effects, fresh-model recovery, measurements and contact resets, truthful browser reload and owned process cleanup. +Against: the admission fixture still starts work, native/SQLite/model-boundary scoring fails, displacement or exact stale binding is absent, recovery needs chat guidance, or completion/readback cannot be substantiated. +Refs: tools/physical-workcell/output/sim8-qualified-20261011-v2/; tools/physical-workcell/test_operator_runtime.py; tools/physical-workcell/score_operator.py + +## SIM8-REPAIR · update · 2026-10-11 15:56 +Observed: the admission fixture failed on version 1 by starting a paused job and reserving one call. It passes after the in-lock check; 20 runtime checks pass. Two adversarial scorer checks reject a refusal/move sequence without native force and an unrelated out-of-zone refusal. Repaired retrospective scoring of version 1 verifies both native/SQLite cycles and a 3.98 cm force displacement with exact immutable stale-proposal binding and no measurement effect. Its overall source-unchanged flag remains false after the repairs, so it is not promoted to a passed frozen qualification. +Inferred: the admission fix and stronger post-trial scorer address the identified failures; version 2 still needs a fresh full qualification. +Next: freeze repaired source and enqueue two new actual model jobs through the browser, then stop the owned UI/server before independent scoring. +Refs: tools/physical-workcell/output/sim8-pause-admission-before-fix.log; tools/physical-workcell/output/sim8-pause-admission-after-fix-runtime.log; tools/physical-workcell/output/sim8-score-repair-tests.log; tools/physical-workcell/output/sim8-qualified-20261011-v1/qualification-score.json + +## SIM8-U2 · update · 2026-10-11 16:04 +Resolves: SIM8-P2 = held +Observed: version 2 attempt 1 completed both UI-queued actual AI cycles on one retained body with unchanged frozen source. Separate native/SQLite scoring passed placement, measurements, contact resets and continuous generation/time. Exact immutable binding and native force states show a 5.61 cm displacement, stale measurement refusal without effect, and a fresh-image model move followed by recovery. Actual inference cancellation left generation zero without a proposal/dispatch. Browser reload and same-origin server restart retained both results without replay. All 31 model processes exited and were reaped; 54 focused conformance checks pass. +Inferred: the repaired local product supports the bounded unattended image/action/computer loop on these recorded attempts. This establishes neither hardware transfer nor broad model repeatability. +Next: preserve derived evidence and prior failures, leave the user-facing server running idle, and require separate network/body-adapter qualification before hardware authority. +Refs: 1415f59; results/bench-operator-20261011/qualification-score.json; results/bench-operator-20261011/final-checks.json; results/bench-operator-20261011/conformance-summary.json; results/bench-operator-20261011/attempt-ledger.json + +## SIM8-S · summary · 2026-10-11 16:04 +Established: Footwork Bench provides UI job creation, persistent camera/queue/result state, autonomous visual decisions, separately saved optical measurement, contact reset, bounded pause/resume, source/freshness guards and independent evidence. The final first-attempt version 2 qualification passed 2/2; all earlier probe, scorer and conformance failures are retained. Native guard files remain unchanged. Model usage is reported with missing cancelled-call accounting explicit. +Uncertain: hardware, general manipulation, physical power-loss recovery, network fault tolerance, production security, customer demand and monetary pricing remain unqualified. +Decision: adopt as a local simulated prototype in the existing Footwork workcell package. Keep the current server intentionally open with no queued/pending model work. Next physical boundary is a qualified persistent body adapter; do not infer hardware readiness from these simulation runs. +Refs: results/bench-operator-20261011/report.md; tools/physical-workcell/OPERATOR.md; results/bench-operator-20261011/final-checks.json diff --git a/jevdual/docs/notebook/2026-10-11-network-body-adapter.md b/jevdual/docs/notebook/2026-10-11-network-body-adapter.md new file mode 100644 index 0000000..4a3743c --- /dev/null +++ b/jevdual/docs/notebook/2026-10-11-network-body-adapter.md @@ -0,0 +1,55 @@ +# Persistent network body adapter + +## SIM9-P1 · prediction · 2026-10-11 16:21 +Hypothesis: durable transport intent, short-lived admission leases and exact lower receipts preserve one-body authority under delays, disconnects, duplicates and process restart without replaying uncertain effects. +Test: implement a separate HTTP service/client around unchanged Bench and AgentBench; drive the twelve preregistered real-socket cases, then freeze source and run two actual image-guided Codex cycles with a lost dispatch reply. +Predicts: duplicate command results match without extra generation; read-only reconciliation settles proven effects; expired or revoked leases fence late requests; missing lower proof remains unknown and blocks dispatch; model cycles retain native/SQLite/contact evidence on one body. +Against: duplicate native effects, admitted stale/expired/wrong-body commands, automatic uncertain retry, erased unknown intent, body recreation on restart, private pose in model input, or independent cycle scoring failure. +Refs: docs/experiments/SIM9-network-body-adapter.md; tools/physical-workcell/output/sim9-network-body/; results/network-body-adapter-20261011/ + +## SIM9-U1 · update · 2026-10-11 16:29 +Observed: the first server conformance run passed five cases and failed seven because a stray simulation field made retained response envelopes fail their closed schema. The original log is retained. Review also identified that an unbound HTTP error after an effect must preserve client uncertainty, and that leases must be rechecked after potentially slow validation. +Inferred: strict envelope checks exposed a transport serialization bug before qualification. Neither the first conformance run nor the repairs establish complete network recovery. +Next: remove the stray field, retain unknown outcomes for unbound errors, recheck lease admission immediately before intent persistence, and rerun public-boundary checks before freezing the model trial. +Refs: tools/physical-workcell/output/sim9-body-server-tests-first.log; tools/physical-workcell/test_body_network.py; tools/physical-workcell/test_body_client.py + +## SIM9-JOURNAL · prediction · 2026-10-11 16:31 +Hypothesis: failure while committing the terminal intent pointer leaves the current implementation's cached record inconsistent with its durable unknown intent, even though admission rereads disk and remains fenced. Updating the cache only after successful persistence removes this inconsistency. +Test: inject an intent persistence failure after a real native dispatch and terminal file creation; assert unchanged cached intent pointer, unknown public state and blocked fresh admission. Preserve the first failure, repair mutation order, then restart and prove read-only lower-receipt recovery with no extra motion. +Predicts: the current cached-pointer assertion fails; after repair cache and disk both remain unknown, admission remains blocked, and restart recovers the indexed core result without replay. +Against: no cache inconsistency occurs before repair, the repair admits fresh actions or loses the effect receipt, or restart changes native generation while recovering. +Refs: tools/physical-workcell/body_network.py; tools/physical-workcell/test_body_network.py + +## SIM9-U2 · update · 2026-10-11 16:35 +Resolves: SIM9-JOURNAL = held +Observed: injected terminal-pointer persistence failure reproduced the cached digest inconsistency while disk-based admission remained blocked. Copying the record and persisting it before updating the cache passes the regression; thirteen server checks pass. The second complete twelve-case matrix passes with unchanged recorded source and all owned processes reaped; ten client checks and twenty-nine unchanged-core checks pass. Static serialization also shows a normal 256-entry client index exceeds the 64 KiB wire JSON decoder bound. +Inferred: transport authority fences hold on these cases, but the client journal needs its own bounded reader before the advertised 256-command capacity is consistent. +Next: retain failures, add a strictly bounded local journal decoder without changing wire limits, rerun affected checks, then freeze measured source for the actual-model trial. +Refs: tools/physical-workcell/output/sim9-terminal-pointer-before-fix.log; tools/physical-workcell/output/sim9-terminal-pointer-after-fix.log; tools/physical-workcell/output/n9-matrix-v2/unittest.log; tools/physical-workcell/output/sim9-client-tests-v3/unittest.log; tools/physical-workcell/output/sim9-core-regression.log + +## SIM9-CAPACITY · prediction · 2026-10-11 16:35 +Hypothesis: a separate 128 KiB strict decoder for the local client journal can retain the declared 256-command index while preserving the existing 64 KiB network-message bound and pending-command integrity. +Test: load an exact hash-bound full-capacity journal, reject duplicate keys and oversized local journals, refuse a fresh command at capacity without sending, and rerun client and network fault checks. +Predicts: the full journal reloads without effects, capacity refuses the next send, malformed journals refuse, and unchanged wire decoding still rejects oversized messages. +Against: 256 entries still cannot reload, malformed data or oversized wire messages are admitted, unresolved commands disappear, or network recovery changes. +Refs: tools/physical-workcell/body_client.py; tools/physical-workcell/test_body_client.py + +## SIM9-U3 · update · 2026-10-11 16:40 +Resolves: SIM9-CAPACITY = held +Observed: the full-capacity reload failed before repair at the 64 KiB reader. The separate strict 128 KiB journal reader passes full-capacity reload, refusal before send 257, duplicate/nonfinite/oversized journal rejection and unchanged oversized-wire refusal. Eleven client checks pass. The third twelve-case matrix passes on the final source, with all owned processes reaped and all recorded source hashes unchanged. Thirteen server checks and twenty-nine core checks also pass. +Inferred: the 65 conformance checks support the bounded same-host network adapter and its retained authority under these faults; they do not establish actual-model network completion. +Next: commit measured source, freeze its exact bytes, and run two actual Codex image-guided network cycles with lost-reply reconciliation, duplicate and delayed dispatches. Score native states, SQLite and exact model/transport receipts separately after process cleanup. +Refs: tools/physical-workcell/output/sim9-client-capacity-before/unittest.log; tools/physical-workcell/output/sim9-client-tests-v4/unittest.log; tools/physical-workcell/output/n9-matrix-v3/aggregate.json; tools/physical-workcell/output/sim9-core-regression.log + +## SIM9-U4 · update · 2026-10-11 16:44 +Resolves: SIM9-P1 = held +Observed: the first frozen actual-model attempt completed two network jobs on one body with 27 exact-image decisions, 21 movements and 83 bound commands. Separate native/SQLite scoring passed placement, measurement, contact reset and generation/time continuity. Proxy evidence binds one lost dispatch reply to one POST and one read-only GET, one concurrent duplicate to equal payloads/replies, and one delayed reply. All model/service processes exited and the proxy closed; source stayed unchanged. The final mechanical matrix and focused checks remain 65/65 passed, with earlier failures retained. +Inferred: the persistent adapter supports this simulated network camera/action/computer loop and preserves uncertainty under the recorded faults; the evidence does not qualify hardware or secure remote deployment. +Next: preserve derived scores and attempt ledgers, keep the previous browser product available, and use the adapter contract as the boundary for a future device-specific local controller. +Refs: e8c7e9b; results/network-body-adapter-20261011/qualification-score.json; results/network-body-adapter-20261011/final-checks.json; results/network-body-adapter-20261011/attempt-ledger.json + +## SIM9-S · summary · 2026-10-11 16:44 +Established: committed persistent body identity, server-owned admission leases, immutable command IDs and receipts, image transport, durable single-send client uncertainty, read-only recovery, diagnostic fault proxy and independently scored actual AI network cycles. Native/core guards are unchanged. All run-owned processes are stopped; the earlier local browser server remains idle. +Uncertain: authentication across machines, physical devices, emergency stopping of accepted motion, power-loss durability, general manipulation and broader model performance. +Decision: adopt the service/client API and qualification CLI inside the existing Footwork workcell package as a loopback simulated prototype. Keep raw/private evidence and failed development attempts; do not infer hardware readiness from this result. +Refs: results/network-body-adapter-20261011/report.md; tools/physical-workcell/NETWORK_BODY.md; docs/experiments/SIM9-network-body-adapter.md diff --git a/jevdual/docs/notebook/2026-10-11-network-workbench.md b/jevdual/docs/notebook/2026-10-11-network-workbench.md new file mode 100644 index 0000000..39caa3a --- /dev/null +++ b/jevdual/docs/notebook/2026-10-11-network-workbench.md @@ -0,0 +1,92 @@ +# Browser workbench on the persistent body + +## SIM10-P1 · prediction · 2026-10-11 17:15 +Hypothesis: a durable pending-stage record around the existing single-send BodyClient lets the browser operator retain queue progress across network loss and restart without inventing completion or replaying effects. +Test: build a separate network operator and retained diagnostic link; check real-socket uncertainty/restart cases, then freeze source and drive two actual Codex jobs through the browser with a lost dispatch reply, disconnect and explicit receipt recovery. +Predicts: the UI preserves identity and cached history, unknown commands block new effects, reconnect is transport-only, read-only receipt lookup applies the exact retained stage once, and explicit resume plans from a fresh TCP image before completing native/SQLite/contact verification. +Against: extra sends or native motion during recovery, silent queue resume, completion without lower proof, private pose used for planning, or independent scoring failure. +Refs: docs/experiments/SIM10-network-workbench.md; tools/physical-workcell/NETWORK_WORKBENCH_CONTRACT.md; results/network-workbench-20261011/ + +## SIM10-U1 · update · 2026-10-11 17:21 +Observed: the first diagnostic-link run passed eight checks; the duplicate-receipt check selected an observation command by lexical journal order instead of the exact dispatch command. Its mismatched result was a test-selector failure, with the original log retained. Read-only review also found the draft scorer treated missing dispatch evidence as a withheld decision without explicit cancellation; it now requires a cancelled model outcome and no associated dispatch. +Inferred: the link's first run does not establish the complete browser workflow. Exact command identity is required in both conformance and independent scoring. +Next: bind the conformance selector to its proposal, finish runtime integration, rerun affected checks and freeze all measured source before browser/model qualification. +Refs: tools/physical-workcell/output/sim10-link-tests-v1/unittest.log; tools/physical-workcell/test_workbench_link.py; tools/physical-workcell/score_network_workbench.py + +## SIM10-HEALTH · prediction · 2026-10-11 17:24 +Hypothesis: cached connection state alone leaves an idle upstream outage invisible; a bounded read-only health poll can detect it without delaying browser state reads, changing completed history or sending commands. +Test: stop the real body endpoint while the host is idle, check that the UI-facing connection becomes unavailable and state reads stay responsive, then restore the same endpoint and prove reconnect remains paused with unchanged command count. +Predicts: the original cached-state path misses the outage; after repair the health check changes only connection/admission state, preserves body identity and historical results, and cannot dispatch during recovery. +Against: the original path already detects an idle outage, the poll blocks browser reads for a body timeout, sends commands, clears uncertainty, or resumes the queue automatically. +Refs: tools/physical-workcell/network_operator_runtime.py; tools/physical-workcell/test_network_operator.py + +## SIM10-ADMISSION · prediction · 2026-10-11 17:27 +Hypothesis: persisting a checked host stage is a usable admission boundary for Pause over TCP: an admitted request can settle after Pause while all later commands remain suppressed, without holding browser state reads behind network I/O. +Test: pause before durable stage admission and assert no send; pause after stage admission while delivery waits, then release delivery and assert at most that admitted command settles and no successor is sent. Keep the body guards unchanged. +Predicts: the queue remains paused, known admitted results are applied once, unknown results remain blocked, and state reads remain responsive during the network wait. +Against: a later command sends, Pause silently resumes, an uncertain request is resent, or browser state waits behind the HTTP timeout. +Refs: tools/physical-workcell/NETWORK_WORKBENCH_CONTRACT.md; docs/experiments/SIM10-network-workbench.md; tools/physical-workcell/test_network_operator.py + +## SIM10-RESUME · prediction · 2026-10-11 17:25 +Hypothesis: Resume's readback gap can overwrite a newer Pause because it clears cancellation after HTTP without rechecking the control epoch. Binding its commit to the unchanged control epoch will preserve the newer control. +Test: delay the real HTTP status response while Resume waits, issue Pause, then release the response; retain the first failing result and rerun after the epoch check. +Predicts: the original Resume returns to running despite the newer Pause; the repaired path stays paused with no command POST and does not clear uncertainty or disconnect state. +Against: the original path already preserves Pause, the repaired path still resumes or sends effects, or the test does not exercise the unlocked readback gap. +Refs: tools/physical-workcell/network_operator_runtime.py; tools/physical-workcell/test_network_operator.py + +## SIM10-U2 · update · 2026-10-11 17:30 +Observed: the original idle connection path missed a stopped real TCP body endpoint. A delayed real HTTP status read also reproduced Resume overwriting a newer Pause. Both failing logs are retained; neither fixture sent model or physical commands. Nine diagnostic-link checks and six HTTP boundary checks now pass. +Inferred: the two control/status predictions are supported at reproduction, but the repairs still require their resolving checks. Host admission is explicitly defined at the durable stage write so Pause does not promise interruption of an in-flight request. +Next: finish bounded read-only health and epoch-checked Resume, check stage recovery and control boundaries, then freeze the browser/model trial. +Refs: tools/physical-workcell/output/sim10-idle-health-before-fix.log; tools/physical-workcell/output/sim10-resume-before-fix.log; tools/physical-workcell/output/sim10-link-tests-v2/unittest.log; tools/physical-workcell/output/sim10-http-tests-v1/unittest.log + +## SIM10-POLL · prediction · 2026-10-11 17:32 +Hypothesis: a background health response that overlaps a newly admitted RPC can mistake the body lock wait for an outage; binding the health result to an unchanged transport epoch prevents this stale status from cancelling a healthy action. +Test: overlap a delayed real TCP health read with a completed network command, then deliver the old failed health response; compare queue/connection status and command count before and after the epoch guard. +Predicts: the original stale response can pause the healthy host; the guarded response is discarded, normal command proof remains applied once, and the next idle read establishes current health. +Against: no false pause occurs before repair, the guarded result still changes current health, or discarding it loses an actual idle outage or command receipt. +Refs: tools/physical-workcell/network_operator_runtime.py; tools/physical-workcell/test_network_operator.py + +## SIM10-U3 · update · 2026-10-11 17:36 +Resolves: SIM10-HEALTH = held +Observed: the final fourteen-case runtime suite passed on real sockets. Idle outage and delayed Resume reproduce their original failures and pass after bounded read-only health and epoch checks. A known native dispatch advanced generation to one before an older health response falsely paused the original host; transport-epoch binding now discards that response. Pause before host admission sends nothing; after admission the exact command settles once without a successor. Two deterministic cycles with disturbance and all four uncertain RPC stages pass. Earlier broad-suite errors were retained fixture mistakes in the local reader and duplicate host initialization, corrected without relaxing product checks. +Inferred: the host control/recovery boundaries hold on these mechanical cases. This does not establish actual-model completion through the browser. +Next: finish unchanged-core/link/HTTP regression, commit measured source, and drive the frozen actual Codex browser trial. +Refs: tools/physical-workcell/output/sim10-network-runtime-final.log; tools/physical-workcell/output/sim10-network-runtime-tests-first.log; tools/physical-workcell/output/sim10-network-runtime-tests-second.log; tools/physical-workcell/output/sim10-stale-health-before-fix.log; tools/physical-workcell/output/sim10-health-resume-after-fix.log + +## SIM10-U4 · update · 2026-10-11 17:37 +Resolves: SIM10-RESUME = held +Observed: The delayed actual HTTP status collision now refuses stale Resume and leaves the newer Pause intact, with zero command sends. +Next: frozen source and actual browser/model qualification. +Refs: tools/physical-workcell/output/sim10-resume-before-fix.log; output/sim10-health-resume-after-fix.log + +## SIM10-U5 · update · 2026-10-11 17:37 +Resolves: SIM10-POLL = held +Observed: The older failed health response no longer pauses a host after a known native dispatch; the transport epoch binds readback to the observed interval. +Next: frozen source and actual browser/model qualification. +Refs: tools/physical-workcell/output/sim10-stale-health-before-fix.log; output/sim10-network-runtime-final.log + +## SIM10-U6 · update · 2026-10-11 17:37 +Resolves: SIM10-ADMISSION = held +Observed: Both pause boundaries pass: no command before durable admission, and only the admitted command settles after Pause with no successor. Cached state remains responsive. +Next: frozen source and actual browser/model qualification. +Refs: tools/physical-workcell/output/sim10-network-runtime-final.log + +## SIM10-U7 · update · 2026-10-11 17:37 +Observed: the final eighty unchanged-core, adapter, link and HTTP checks passed; combined with the fourteen runtime checks this is 94 passing checks. Ruff, JavaScript syntax, notebook checks and CLI preflight pass. No existing body/core/local-product source changed. One regression launch used an incorrect relative interpreter path and exited before tests; the corrected launch is retained separately. +Next: commit measured source and run the first actual browser/model trial without source edits. +Refs: tools/physical-workcell/output/sim10-core-link-http-regression.log; tools/physical-workcell/output/sim10-core-link-http-regression-v2.log; tools/physical-workcell/output/sim10-network-runtime-final.log + +## SIM10-U8 · update · 2026-10-11 17:44 +Resolves: SIM10-P1 = held +Observed: the first frozen actual Codex browser trial passed independent scoring after both owned trial processes exited. Two successive thirteen-call jobs advanced one body from generation zero to twenty with continuous native time, one verified SQLite measurement each and contact resets. The first dispatch reply was lost; the browser blocked the queue. Disconnect, reconnect and exact receipt lookup retained four command records without another send, and recovery remained paused until explicit Resume. Duplicate and 0.8-second delay controls were consumed once. Reload preserved both completed jobs and eighty commands. Every model image, typed proposal and lower receipt was independently bound; no private pose was supplied. The same roots and ports restarted with unchanged identities and history, paused with no active job or pending model request. +Inferred: browser-visible uncertainty and explicit recovery compose with the retained body adapter for this loopback simulation. This does not qualify hardware or authenticated remote deployment. +Next: hand off the running paused product and its runbook; preserve finite budgets and admission semantics. +Refs: results/network-workbench-20261011/report.md; results/network-workbench-20261011/qualification-score.json; results/network-workbench-20261011/ui-ledger.json; results/network-workbench-20261011/fault-observations.json; results/network-workbench-20261011/handoff.json + +## SIM10-S1 · summary · 2026-10-11 17:44 +Established: the browser workbench now queues actual image-guided jobs through a separate persistent TCP body. Local identity, freshness, lease and effect guards remain authoritative. Disconnect, uncertain replies and exact receipt recovery are visible product states, with explicit Resume and no hidden retry. +Evidence: 94 conformance checks and the first frozen actual-model browser trial passed. Two completed jobs, native/SQLite/contact proof, receipt and image binding, reload and paused restart are retained in the result package. Development failures remain separately recorded. +Uncertain: physical actuation, cross-machine authentication and long-running budget renewal remain outside qualification. An admitted bounded action can settle after Pause or Disconnect. +Decision: adopt the qualified loopback network workbench and hand off the running paused product with completed history retained. +Refs: docs/experiments/SIM10-network-workbench.md; results/network-workbench-20261011/report.md; results/network-workbench-20261011/attempt-ledger.json; tools/physical-workcell/NETWORK_WORKBENCH.md diff --git a/jevdual/docs/notebook/2026-10-11-onboarding-remediation.md b/jevdual/docs/notebook/2026-10-11-onboarding-remediation.md new file mode 100644 index 0000000..4ea1a56 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-11-onboarding-remediation.md @@ -0,0 +1,25 @@ +# Standalone packet review remediation + +## UOR1 · prediction · 2026-10-11 13:43 +Hypothesis: resolving provenance from the packet manifest before installation or native actions removes Git/Command Line Tools dependence and prevents a completed native trial from losing its receipt at the provenance stage. +Test: preserve the old packet's installer failure outside a repository with Git denied; test both receipt paths against a frozen unpacked v3 packet with Git denied, plus invalid manifest and checkout fallback cases. Exercise the guest member-verification command and refuse modified, extra and symlinked members. Rebuild twice and verify immutable source binding. No independent Mac/TCC claims from these checks. +Predicts: standalone receipt provenance equals the frozen manifest, no Git subprocess occurs in packet mode, bad provenance refuses before effects, and altered extracted payloads fail guest verification. +Against: either receipt still invokes Git, accepts an enclosing foreign checkout, fails after native effects for provenance, or guest verification accepts modified payloads. +Refs: Claude review1791739896683034000; scripts/capability_clean_install.py; scripts/capability_installed_trial.py; docs/independent-onboarding.md + +## UORU0 · update · 2026-10-11 13:43 +Observed: the prediction heading was entered13:43 while the terminal clock showed13:41. Its committed161e439 precedes the resolving tests; the prediction text is retained. PR2 merged exact7669d3d into268f0fc after four passing checks and an empty review-thread audit. User selected and authorized disposable VM provisioning on External SSD, including launcher/image downloads. +Next: reproduce the packet receipt defect, remediate and freezev3; independently provision a SIP-enabled guest without inherited grants. +Refs: https://github.com/Tom-R-Main/Footwork/pull/2; Claude review1791739896683034000 + +## UORU1 · update · 2026-10-11 13:48 +Resolves: UOR1 = held +Observed: the baseline installer fails at trapped Git; v3 unpacks and retains manifest provenance without Git on installed3.12.6/3.14.7. Both guest verifier invocations pass; both native invalid-manifest paths refuse before resources.23 focused tests and Ruff pass, repeated14member builds match frozen33e1afb, with the reviewed wheel unchanged. The smoke stops at an intentional uv boundary and does not establish dependency installation or native effects. Initial headings ran ahead of terminal clock by one or two minutes; committed prediction order is authoritative. +Next: request Claude exact-artifact review and keep UO2 open for independent guest runtime/permission evidence. +Refs: results/onboarding-remediation-20261011; output/u2-remediation-smoke-v3/receipt.json; PR2 merge268f0fc + +## UORS1 · summary · 2026-10-11 13:51 +Established: packetv3 is frozen, reproducible and independently approved by Claude1791740993609795000 for preparation; all three review findings closed. PR2 merged268f0fc and both postmerge workflows passed. +Uncertain: independent guest permission transition, final native receipt and cleanup remain unexecuted; verified SIP-on guest provisioning is underway in the user-approved SSD folder. +Decision: retain immutablev3 and execute UO2 only on the new guest after real setup, denied baseline and explicit permission transition. +Refs: results/onboarding-remediation-20261011; output/u2-vm-provision-20261011/provision-state.json diff --git a/jevdual/docs/notebook/2026-10-11-reusable-bench.md b/jevdual/docs/notebook/2026-10-11-reusable-bench.md new file mode 100644 index 0000000..cb315bf --- /dev/null +++ b/jevdual/docs/notebook/2026-10-11-reusable-bench.md @@ -0,0 +1,27 @@ +# Reusable simulated bench + +## SIM6-P1 · prediction · 2026-10-11 13:57 +Hypothesis: a persistent integration state and image-bound job transitions can support two prepare-measure-contact-reset cycles, including recovery after a bounded external-force disturbance, without recreating the block. +Test: implement an isolated bench and RGB host; run focused development checks, then freeze and independently exercise two successive jobs, stale evidence after disturbance, measurement readback and native reset scoring. +Predicts: one bench identity and increasing generations/native time span both cycles; each job has one measurement, physical contact reset, and the disturbed job rejects stale evidence before fresh observation guides recovery. +Against: stale evidence dispatches an effect, reset assigns body pose, job two begins before reset verification, the body is recreated, or disturbance recovery fails within the declared budget. +Refs: docs/experiments/SIM6-reusable-bench.md; tools/physical-workcell/; results/reusable-bench-20261011/ + +## SIM6-DEV-U1 · update · 2026-10-11 14:05 +Observed: the first source-stable development sequence completed two cycles on bench df2c545a1cee4405b9ce2bd3eedfc149. Generations advanced from 0 to 29 to 70. A native external-force intervention displaced the RGB block center by 0.1333 m; stale motion and measurement refused before fresh observations guided recovery. Both resets retained native contact evidence and both measurement results were independently persisted by separate processes. +Inferred: the axial RGB routing and contact reset mechanism work in this development fixture. This is not the frozen independent qualification and does not yet resolve SIM6-P1. +Next: complete retained measurement-integrity and reset evidence guards, run focused conformance, freeze source, then qualify a fresh two-job sequence with separate native/SQLite scoring. +Refs: tools/physical-workcell/output/sim6-root-dev1/ordinary-journal/; tools/physical-workcell/output/sim6-root-dev1/disturbed-journal/; tools/physical-workcell/output/sim6-root-dev1/ordinary.stdout.json; tools/physical-workcell/output/sim6-root-dev1/disturbed.stdout.json + +## SIM6-U1 · update · 2026-10-11 14:12 +Resolves: SIM6-P1 = held +Observed: frozen 0091e5f completed both first-attempt jobs on one bench with generations 0 to 29 to 70 and continuous retained native time. A 0.1333 m external-force displacement caused stale movement and measurement to refuse; fresh RGB-guided contact actions recovered preparation. Separate native/SQLite scoring verified historical placement, one measurement per job, both contact resets and final idle state. Fourteen final-source conformance checks passed; source hashes stayed unchanged during qualification. +Inferred: separating body identity from job state supports the requested bounded reusable simulation loop. This does not establish model policy quality or arbitrary/hardware recovery. +Next: retain the result and close this implementation package. Any model policy or instrument adapter is a separate experiment against this interface. +Refs: 0091e5f; results/reusable-bench-20261011/report.md; results/reusable-bench-20261011/qualification.json; results/reusable-bench-20261011/fresh-cli-status.json; results/reusable-bench-20261011/cleanup.json + +## SIM6-S · summary · 2026-10-11 14:12 +Established: persistent bench and job APIs, separate image-measurement worker, bounded RGB host, contact reset, diagnostic native force, focused corruption/replay/phase checks and a frozen two-cycle qualification. All started subprocesses returned and their outcomes and evidence remain retained. Existing episode/controller routes were preserved. +Uncertain: general model-driven manipulation, non-axial disturbances, continuous physical clocks, real cameras/actuators/instruments, power-loss recovery and production capability governance remain unqualified. The first mechanism prediction held on this narrow sequence; no failed physical qualification attempt was hidden or retried. +Decision: keep the reusable bench as an isolated experimental Footwork tool. Next work can expose the same prepare-measure-reset contract to an agent or instrument adapter without changing controller authority. +Refs: docs/experiments/SIM6-reusable-bench.md; tools/physical-workcell/BENCH.md; results/reusable-bench-20261011/ diff --git a/jevdual/docs/notebook/2026-10-11-u2-vm-provision.md b/jevdual/docs/notebook/2026-10-11-u2-vm-provision.md new file mode 100644 index 0000000..9f0f154 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-11-u2-vm-provision.md @@ -0,0 +1,101 @@ +# U2 disposable macOS provisioning + +## VM1 · prediction · 2026-10-11 13:42 +Hypothesis: a pinned upstream Lume launcher running with all configuration, cache, restore-image and VM storage under the approved External SSD folder can install a fresh Apple restore image without a global install, shared host credentials or pregranted guest permissions. +Test: inspect upstream installer/release and launcher help, verify downloaded release provenance, then create a 4 CPU/8 GB/64 GB SIP-default guest from the recorded Apple IPSW and launch its visible Setup Assistant; preserve paths, hashes, logs and status in output/u2-vm-provision-20261011. +Predicts: the release and image are identifiable, provisioning writes stay in the approved folders, the guest reaches initial Setup Assistant, and no unattended seed, host permission change or guest permission grant is needed for provisioning. +Against: the launcher requires a global service or writes elsewhere, hardware/storage blocks install, the image cannot be verified/provisioned, or the guest arrives pregranted instead of pristine. +Refs: docs/experiments/U2-independent-onboarding.md; docs/onboarding-vm-plan.md; output/u2-vm-provision-20261011 + +## VMU1 · update · 2026-10-11 13:57 +Observed: Lume0.6.1 asset digest/signature/notarization and SSD-only configuration checks passed. Source inspection found its native viewer unconditionally reads the host clipboard before guest SSH is available, and its VNC CLI exposes no bind-address control; the queued controller was stopped before any VM create. The fresh Apple26.6.2/25G83 IPSW download remains active and its advertised SHA256 is recorded. +Inferred: a supported privacy-bounded native viewer requires a different launcher; launch proof and VM1 remain open. +Next: qualify pinned Tart with its supported --no-clipboard flag, then reuse the Apple image only after full SHA256 verification. +Refs: output/u2-vm-provision-20261011/launcher-provenance.json; output/u2-vm-provision-20261011/provision-state.json + +## VM2 · prediction · 2026-10-11 13:57 +Hypothesis: pinned upstream Tart2.40.1 with TART_HOME and temporary state on the approved SSD and --no-clipboard can provision a pristine macOS guest and show Setup Assistant without host clipboard access, VNC listener, folder sharing, guest SSH or pregranted permissions. +Test: verify official release digest/signature/license and actual CLI help/source flags, create from the fully verified Apple IPSW, configure4CPU/8GB/64GB, and run its native viewer with clipboard/audio/USB passthrough disabled and no directory/SSH/guest-agent setup; root observes the GUI and owns any human-dependent setup. +Predicts: CLI/source and runtime state confirm the supported disabled clipboard route and isolated storage, guest installation succeeds, and a real visible Setup Assistant is available before permission grants. +Against: the flags are unsupported or still read host clipboard, Tart requires unapproved global paths or paid terms, installation fails, or Setup Assistant/host isolation cannot be established. +Refs: output/u2-vm-provision-20261011/tart-release.json; output/u2-vm-provision-20261011/tart-run-pinned.swift; docs/experiments/U2-independent-onboarding.md + +## VM3 · prediction · 2026-10-11 14:07 +Hypothesis: hdiutil makehybrid can produce an ISO containing only the approved packet and downloaded prerequisite assets without attaching a host volume, and Tart's supported --disk path:ro route can expose that immutable image inside the pristine guest without host clipboard or directory sharing. +Test: stage exactly packetv3, Python3.14.7, uv0.12.5, ChromeDMG and their manifest/checksums, build an ISO under the approved SSD folder, verify its inventory/hash, and attach read-only on the first isolated Tart run; root later observes guest volume and verifies payload hashes. +Predicts: ISO creation does not require hostmount or global changes, contains the five declared source files plus checksum/provenance metadata, and Tart's CLI/source passes readOnly=true for the attachment. +Against: ISO creation fails like host DMG attach, unexpectedly includes other hostfiles, or the supported attachment cannot be made/readback as read-only inside the guest. +Refs: output/u2-vm-provision-20261011/tart-run-help.txt; output/u2-vm-provision-20261011/tart-run-pinned.swift; output/u2-vm-provision-20261011/prerequisite-provenance.json + +## VMU2 · update · 2026-10-11 14:07 +Observed: VM3's test enumerates four assets, while its Predicts line inadvertently says five source files; the intended inventory is the packet, Python installer, uv archive and Chrome DMG plus provenance/checksum metadata. +Next: measure that explicit four-asset inventory; do not add an extra asset to satisfy the mistaken count. +Refs: output/u2-vm-provision-20261011/prerequisite-provenance.json + +## VMU3 · update · 2026-10-11 14:13 +Observed: the Apple byte-range downloader stopped with curl exit18 (partial transfer) at roughly90.7percent aggregate image bytes; completed segments and all partial files remain. The full-image SHA256 has not been checked and no guest exists. ISO creation/inventory verification succeeded without a hostmount; guest readback is pending. +Inferred: a bounded byte-range continuation can retain useful downloaded bytes, but partial transport success cannot certify the image. +Next: commit VM4 before resuming only incomplete ranges, preserving the failed original transfer logs. +Refs: output/u2-vm-provision-20261011/apple-range-00.log; output/u2-vm-provision-20261011/iso-verification.json + +## VM4 · prediction · 2026-10-11 14:13 +Hypothesis: Apple CDN supports precise Range continuation after a partial transfer, so a new downloader can finish only the missing bytes of retained ranges and produce the advertised complete-image SHA256 without downloading successful segments again. +Test: resume incomplete segments with Range beginning at their recorded absolute offset, require HTTP206 with matching Content-Range before appending, bound retries and preserve per-attempt headers/logs, then stream-assemble and compare the final whole-image SHA256 to Apple CDN metadata. +Predicts: retained segments remain unchanged, each range reaches its exact expected length, the complete image matches Apple SHA256885503b7f4b06609e9a512f2befd40f59730640a3f1233e3892d60affdd51c95, and only then the committed Tart provisioning test starts. +Against: the CDN ignores/rejects offsets, repeated partial transport prevents completion, existing bytes are altered, or final image hash differs. +Refs: output/u2-vm-provision-20261011/apple-ipsw-headers.txt; output/u2-vm-provision-20261011/download_apple_ranges.py + +## VMU4 · update · 2026-10-11 14:41 +Resolves: VM4 = held +Observed: five bounded continuation requests returned HTTP206 with exact absolute Content-Range; all missing bytes were retained in separate attempt files before append. Original completed ranges were not downloaded again. Single streamed assembly/fsync produced 19,772,231,540 bytes and Apple SHA256885503b7f4b06609e9a512f2befd40f59730640a3f1233e3892d60affdd51c95. The image is read-only with recorded file identity and private non-symlink parent; Tart creation started only after those guards passed. A redundant preliminary prefix-hash scan was stopped before network requests, preserving its script/context; no image was certified from that attempt. +Next: supervise Tart installation and hand its actual native viewer to root; VM2 and VM3 guest observations remain open. +Refs: output/u2-vm-provision-20261011/apple-ipsw-provenance.json; output/u2-vm-provision-20261011/provision-state.json; output/u2-vm-provision-20261011/tart-create.log + +## VMU5 · update · 2026-10-11 14:41 +Resolves: VM1 = inconclusive +Observed: pinned Lume integrity/signature/configuration checks passed, but native clipboard source behavior and absence of a supported VNC bind option caused its controller to stop before any guest creation. Actual provisioning now uses independently verified Tart2.40.1 with its supported disabled-clipboard route; the Lume guest hypothesis was not executed to completion. +Next: retain Lume findings as the launcher selection rationale; do not call them a Lume installation or first-use failure. +Refs: output/u2-vm-provision-20261011/launcher-provenance.json; output/u2-vm-provision-20261011/tart-launcher-provenance.json + +## VM5 · prediction · 2026-10-11 15:11 +Hypothesis: pausing only the Python controller before its create timeout while Tart continues verified disk work, then resuming after the create child exits, preserves the actual child exit status and allows existing configuration/viewer steps without restarting installation. +Test: inspect the active controller's Python/POSIX subprocess wait semantics and exact process relationships, pause only that controller if creation remains active near15:18, supervise the same child PID and scoped disk activity for at most15additionalminutes, then resume the controller after the child exits or becomes a zombie; preserve any timeout/failure without resetting the partial guest. +Predicts: Tart1958 continues writing while its supervisor alone is stopped, the completed child is reaped before timeout rejection in the resumed wait, and the same fresh guest reaches the queued native viewer with the selected isolation flags. +Against: pausing affects the child/process group, the resumed controller loses true exit status or times out despite completion, installation fails/stalls, or the15minute extension expires without a justified handoff. +Refs: output/u2-vm-provision-20261011/provision_tart_after_download.py; output/u2-vm-provision-20261011/python-popen-wait-source.txt; output/u2-vm-provision-20261011/installer-liveness-current.json + +## VMU6 · update · 2026-10-11 15:12 +Observed: VM5 was written at15:12EDT (clock19:12:02UTC), while its heading used15:11; the heading minute is approximate and git commit order remains the preregistration boundary. POSIX _wait polls waitpid before testing the elapsed deadline on each iteration, but a stop between that poll and deadline calculation would still permit a timeout on resume; the prediction does not assume away that narrow race. +Next: commit VM5 before any pause, prefer observing the controller sleeping in its wait loop, and report any resumed timeout honestly rather than inferring successful child status from a percentage. +Refs: output/u2-vm-provision-20261011/python-popen-wait-source.txt + + +## VMU7 · update · 2026-10-11 15:20 +Resolves: VM2 = held +Observed: Tart creation returned success at15:14EDT before its timeout; get/list report the same owned guest running with4CPUs,8192MB RAM and64GB logical disk. Actual argv disables clipboard/audio/USB passthrough, captures system keys and attaches the verified ISO read-only. Root bound exactPID25080/window footwork-u2-20261011 and observed fresh welcome then actual Setup Assistant Language page/English/license-use notice in native CUA tool images. Root owns all GUI input; human license approval is pending. +Inferred: provisioning reached a visible pristine setup boundary; this does not establish guest SIP, initial TCC denial, ISO guest readback or the U2 permission journey. +Next: retain the running guest for human setup and root's independent onboarding qualification; VM3 remains open. +Refs: output/u2-vm-provision-20261011/delivery-record/summary.json; output/u2-vm-provision-20261011/provision-state.json; root native CUA tool images in this chat + +## VMU8 · update · 2026-10-11 15:20 +Resolves: VM5 = inconclusive +Observed: installer completed normally before the planned15:18pause boundary, so no controller pause, resume, swap or extended timeout was executed. +Next: preserve the proposed recovery and narrow wait-race caveat as untested; do not call VM5 held. +Refs: output/u2-vm-provision-20261011/tart-create.log; output/u2-vm-provision-20261011/provision-state.json + +## VMS1 · summary · 2026-10-11 15:20 +Established: pinned launchers/artifact provenance, safe supported Tart flags/storage, bounded authenticated Apple image recovery, exact ISO inventory/payload verification, successful fresh guest creation/configuration and root-observed visible Setup Assistant. No global launcher, host permission change, guest account/SSH/TCC grant or SIP change was performed. +Uncertain: guest SIP runtime status, denied Accessibility baseline, actual ISO guest read-only/hash readback and the complete installed U2 effect/restart/report/cleanup journey. Tart Gatekeeper diagnostics reported Unnotarized Developer ID even though normal CLI/create/run succeeded without policy overrides. +Decision: hand the running guest and all GUI ownership to root at the explicit human setup/license boundary; keep VM3 and UO2 open. +Refs: output/u2-vm-provision-20261011/delivery-record/report.md; output/u2-vm-provision-20261011/delivery-record/summary.json + +## VMU9 · update · 2026-10-11 16:18 +Observed: the user reports accepting the macOS license and creating the disposable guest account. Root observed the unlocked desktop and launched the guest interactive Terminal, com.apple.Terminal version2.15. Guest sw_vers reports macOS26.6.2 build25G83; csrutil reports SIP enabled. diskutil reports both media and volume read-only at /Volumes/FOOTWORK_U2 with the exact six staged filenames. Guest payload checksum verification is running; VM3 remains open until those results are observed. Native CUA modifier-key input is not reliably forwarded by the VM; unmodified menu navigation and printf octal-decoded commands execute in the same Terminal without clipboard sharing. No Accessibility grant or product installation has occurred. +Next: finish guest media verification, install verified prerequisites, then capture the denied installed diagnostic and fixture before any permission grant. +Refs: root native CUA tool images in this chat; guest /Users/tom/footwork-u2/baseline.txt; guest /Users/tom/footwork-u2/media-hashes.txt + +## VMU10 · update · 2026-10-11 16:18 +Resolves: VM3 = held +Observed: the guest read-only qualification volume contains the exact six staged files. The guest shasum check reports OK for all four executable/archive payloads, including packetv3. Root observed the completed output in the native viewer; guest media-hashes.txt preserves it. The prior host ISO parser verified the metadata files and payload bytes; the guest readback now establishes transport to the same independent environment. +Next: install verified prerequisites and execute UO2; preparation and media verification do not establish first-use permission or native effect success. +Refs: guest /Users/tom/footwork-u2/media-hashes.txt; output/u2-vm-provision-20261011/iso-verification.json; root native CUA tool images in this chat diff --git a/jevdual/docs/notebook/2026-10-12-installed-agent-release.md b/jevdual/docs/notebook/2026-10-12-installed-agent-release.md new file mode 100644 index 0000000..e3aac89 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-12-installed-agent-release.md @@ -0,0 +1,58 @@ +# Installed agent and release package + +## IA1 · prediction · 2026-10-12 16:03 +Hypothesis: a fresh dependency installation of the qualified9bb0440d wheel, attached by its generated isolated MCP command to real Claude Code, can perform one bounded browser-to-TextEdit append and produce structured completion accepted by the durable report adapter without source imports or duplicate input. +Test: extract approved205577ee packet, verify pristine members, cold-install new private venv/cache, establish same responsible host prerequisites, start one owned fixture, generate per-job config. A committed harness offers only the five public tools to one fresh Claude Code session (Opus5.5 high,1USDsoftcap,2USDreserve,180secondstimeout). Require actual tool/model inventory, exact AX oracle, one durable operation, installed server/worker origins and private HOME, worker absence, structured claims validated by emitted adapter after host exit. Preserve raw transcript and any failure; no native or paid retry to improve outcome. Then one automatic fixture cleanup and independent resource absence. Test harness parsing/claims isolation before committing it. +Predicts: actual host discovers the installed public catalog, performs exactly one scoped effect, claims historical completion honestly with persistence not_checked, and adapter validates against the same durable task/journal. Cleanup confirms exact resource absence. +Against: source/development runtime supplies product imports, host uses other tools, native input repeats, report is absent/rejected, environment or cost is unavailable, or cleanup lacks confirmation. +Refs: results/u3-packet-v4-permission-20261012-v3; docs/capability-packaging.md; scripts/capability_comparison_trial.py; original A1 failed answer acceptance preserved + +## IA2 · prediction · 2026-10-12 16:03 +Hypothesis: a finalized local release bundle can carry the already qualified wheel/helper bytes, deterministic inventory, installation/agent guide and evidence digests without implying broader release qualification. +Test: after installed-host qualification and Claude raw review, freeze release instructions and qualification metadata in a committed source, build two bundles from an explicit allowlist, verify equal hashes and each member digest, independently verify wheel RECORD/runtime and helpers against qualified artifacts. Mark local candidate only; no registry publication, tag, push or merge in this package. +Predicts: repeatable bundles bind product and helper identities to both guest permission evidence and real-host task evidence, include MIT license/setup guidance, and disclose OS/host/persistence/pixel limits. The previous qualification packet remains immutable. +Against: unqualified runtime bytes replace the candidate, evidence claims exceed their scope, extras/secrets enter inventory, the bundle cannot verify, or repeated builds differ. +Refs: source445a348; wheel9bb0440d; scripts/build_onboarding_packet.py; docs/experiments/A2-installed-agent-release.md + +## IAU1 · update · 2026-10-12 16:15 +Observed: pristine14member v2 verification precedes its seven-stage fresh installation of115binaries. Strictdoctor0 confirms existing host permission prerequisites. Committed harnessd52d32d passes19root boundary tests and Ruff, then one actual Claude Code2.1.296 Opus5.5 session offers exactly five MCPtools plus StructuredOutput. It performs oneact op741d45670d09, exact independent AXoracle passes, one durable final/applied/sent row remains, and emitted installed reportadapter accepts final structuredclaims afterhostexit. Runtime server/worker origins/privateHOME and absence pass. Cost0.0990072 is retained. OneSIGINT confirms automaticdocument/browser/listener cleanup with fixtureexit0; independent fresh AXWindows succeeds with0matches, exactownedprocesses absent andlistenerrefused. An additional readonly adapter check rejects claims omitting the admitted operation. +Changes: operator verifierflag error v1 and relativeconfigpath refusal v2 are retained before the one measured task; corrected setup uses pristineverification and absolutepaths. No native/model retry or newpermissiongrant occurs. Claude raw review pending; IA1 remainsopen. +Next: independent review, then freeze the local release docs/evidence and build/check the candidate twice. Original A1 failures remainunchanged. +Refs: results/installed-agent-release-20261012-v2; output/installed-agent-release-20261012-v2/evidence-manifest.json; docs/experiments/A2-installed-agent-release.md; d52d32d + +## IAU2 · update · 2026-10-12 16:17 +Resolves: IA1 = held +Observed: Claude independently verifies all13raw evidence digests, exact offered/called tools/model/version, installed contexts, readonly single journal row, structured report/adapter-only publication and independent automatic-cleanup absence; approves with no blockers in1791836103010895000. Root readonly missing-operation report check rejects with exit1 and operation/disposition mismatches. Product445a348/wheel9bb0440d provenance is recorded separately from harnessd52d32d. +Changes: release summaries add explicit host/OS/single-sample limits already established by the existing records; no receipt or prior failed outcome changes. Guest summary receives its existing Terminal/macOS26.6.2 scope metadata for artifact consumers. +Next: commit release builder/docs/approved summaries, build twice, verify payload/RECORD/extraction and have Claude inspect the actual candidate. IA2 remainsopen. +Refs: results/installed-agent-release-20261012-v2; results/u3-packet-v4-permission-20261012-v3/report.md; Claude1791836103010895000 + +## IAU3 · update · 2026-10-12 16:25 +Observed: first local bundle5031b50 builds twice byte-identically,30members341107bytes/SHA47d87922; allpayloadmanifest/RECORD/source and18relative links verify, consumerextractedverifier passes and10qualifiedpayloads unchanged. Claude independently reproduces artifactchecks and approves localcandidate in1791836643188530000. Its N1 finds189absolutehostpaths in six legacyJSONattachments; these are not secrets but expose user/workspace layout before external sharing. +Next: derive sanitized attachment copies in a new frozen candidate, keep originals and approvedv1 immutable, test path removal without changing qualifiedruntime, then repeat finalartifactchecks/review. IA2 remainsopen until finalderivedcandidate closes. +Refs: output/release-candidate-20261012; results/release-candidate-20261012; Claude1791836643188530000 + +## IA3 · prediction · 2026-10-12 16:25 +Hypothesis: deterministic sanitization of derived evidence copies removes identifying host home/worktree paths from the distributable candidate while preserving original evidence, qualified wheel/helpers and meaningful qualification claims. +Test: replace absolute /Users/ and /home/ path prefixes in derived legacyJSONstrings with stable placeholders, marking sanitization/provenance in qualification metadata. Before changing builder, reproduce old leaked-hostpath boundary; test nested paths, multiple identities, module/oracle fields, nonpath text and source-preservation. Build two new candidates from committedsource; require equalhashes, noidentifyinghomepaths acrossalltextmembers, unchanged10qualifiedpayloads, workinglinks/verifier and Claude exactartifactreview. No install/native/model rerun. +Predicts: derivedJSON remainsvalid, source blobs unchanged and no host username/worktree layout appears in distributable evidence. Provenance records originals separately from derived digests. +Against: an identifyinghomepath remains, legitimate nonpath fixture claims change, original evidence is edited, qualifiedpayloadbyteschange, or newbuild/verifier fails. +Refs: ClaudeN1in1791836643188530000; scripts/build_release_candidate.py; IA2 + +## IAU4 · update · 2026-10-12 16:32 +Resolves: IA3 = held +Observed: old leaked-homepath boundary fails beforefix; corrected19releasecases and rootRuff pass. Finalderivedv2 sourced931559 builds twice byte-identically as8dc81c72,30members342599bytes. Root scans alltextmembers andnestedwheel with zeroidentifyinghomepaths, confirms18links and10unchangedqualifiedpayloads; consumerextractedverifier passes. Claude independently reproduces hash/extraction/qualifiedbytes/pathscan/JSONparse and all10original-versus-deriveddigestpairs; closesN1in1791837075792802000. Original evidence and47d87922v1 remainunchanged. +Next: close IA2 on the reviewed localcandidate and retain the external-publication boundary. +Refs: output/release-path-redaction-before.txt; output/release-path-redaction-after.txt; results/release-candidate-20261012; Claude1791837075792802000 + +## IAU5 · update · 2026-10-12 16:32 +Resolves: IA2 = held +Observed: finallocalpackaging8dc81c72 is reproducible, manifest/RECORD/sourceverified, extractedconsumerverified and independentlyapproved. It carries exactwheel9bb0440d/helperproduct445a348, frozenrelease sourced931559, setup/agentguide and truthful separateguest/realhost qualification limits. The actualhosttask/adapter/cleanup gatepassed once; no producteffect rerun occurred duringpackaging. +Next: deliver the localartifact; registry/tag/push/merge andwiderqualification require a separate request. +Refs: results/release-candidate-20261012/report.md; results/installed-agent-release-20261012-v2/report.md; results/u3-packet-v4-permission-20261012-v3/report.md + +## IAS1 · summary · 2026-10-12 16:32 +Established: one fresh realClaudeCode2.1.296 Opus5.5 session used the installed candidate, one scopedappend, one durableoperation and acceptedposthoststructuredreport; exactAXoracle/automaticcleanup andindependentabsence pass. Finalrelease8dc81c72 repeats byte-for-byte, has30verifiedmembers and18workinglinks, unchangedqualifiedwheel/helpers, derivedlegacyprivacy/provenance and independentClaudeapproval. Root19hostchecks/19releasechecks andRuff pass. +Uncertain: this does not qualify realhostinterruption/restart in thislane, otherhosts/models/OS/MDM, arbitraryapps, pixelcapture, coldOS or diskpersistence. Dependencies andagentauthentication remainseparate prerequisites. +Decision: close installed-host/localpackaging task as verified within its statedscope. Deliver localcandidate without publishingregistry/tag or changing Git remote. Preserve originalA1/U2/U3failures, interruptedjourneys and priorlocalbundle. +Refs: results/installed-agent-release-20261012-v2; results/release-candidate-20261012; d52d32d; d931559; Claude1791837075792802000 diff --git a/jevdual/docs/notebook/2026-10-12-onboarding-fixture-v4.md b/jevdual/docs/notebook/2026-10-12-onboarding-fixture-v4.md new file mode 100644 index 0000000..c781b85 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-12-onboarding-fixture-v4.md @@ -0,0 +1,139 @@ +# Onboarding fixture v4 + +## UF1 · prediction · 2026-10-12 13:56 +Hypothesis: closing the unique fixture document by exact AXDocument identity and its AXCloseButton, with any discard button confined to its observed attached save sheet, removes the Automation grant while preserving unrelated documents. +Test: first reproduce the old cleanup limitation in boundary scenarios; cover exact-path duplicates, same-name other paths, changed identity, attached versus unrelated sheets, unsuccessful AX presses and verified window disappearance. Then run the frozen new helper on the disposable guest with Automation declined and an edited owned document; check its receipt and independent owned-resource absence. +Predicts: the exact owned window closes automatically with only existing Accessibility, no Apple Event/Automation route executes, and ambiguous/changed/unobservable targets refuse without closing another window. Cleanup remains independent of browser/listener shutdown. +Against: another window or app-level dialog is acted on, the helper claims closure without observed disappearance, leaks the owned browser/listener on AX failure, or needs an extra permission. +Refs: results/u2-independent-onboarding-20261011-v1; results/u2-independent-onboarding-20261011-v2-dev; local Arc CUA6b353039 benchmarks/workflows.py:533; Claude1791753660836044000 + +## UF2 · prediction · 2026-10-12 13:56 +Hypothesis: separately bounded owned-browser startup readiness followed by the unchanged unique-window identity deadline classifies slow first launch without weakening resource identity. +Test: first preserve a slow-start/ambiguous-identity failing scenario against the old helper. Exercise delayed readiness, browser exit, startup timeout, missing/ambiguous windows and successful unique identity; freeze packetv4 and run its installed public path after normal Chrome verification. Keep every failure in distinct fresh run directories. +Predicts: delayed startup gets its own phase and bounded typed receipt/exit, identity ambiguity still refuses after its own deadline, a stopped browser never emits ready, and setup failures run bounded owned cleanup without traceback-only reporting. +Against: a late or foreign browser/window is adopted, ambiguous identity becomes authorized, timeout remains unclassified, or launch failure bypasses cleanup. +Refs: scripts/capability_fixture.py; local Chrome DevTools MCPf08dbe152502d66e75fa07fb2588dc0feb42bc20 src/BrowserManager.ts; UO5 in docs/notebook/2026-10-10-independent-onboarding.md + +## UF3 · prediction · 2026-10-12 13:56 +Hypothesis: a source-frozen packetv4 with the unchanged reviewed wheel permits a separately recorded denied-to-granted guest Terminal journey and fully automatic owned cleanup. +Test: build twice and verify exact inventory/source binding, review the artifact with Claude, then run an independently recorded guest permission journey with denied doctor/refusal, captured authorized grant, host restart, installed append/replay/report and automatic cleanup. Use the existing disposable guest only after an approved supported per-host Accessibility reset; preserve its v1/v2 evidence and label this as fresh permission state on a previously provisioned guest, not a newly cold OS. +Predicts: packet hashes repeat, denied fixture creates no resources, captured same-host grant and post-restart strict doctor pass, native oracle holds one effect/row, and cleanup confirms the exact document/browser/listener/worker without operator close or Automation grant. +Against: packet depends on runtime source/Git, grant remains uncaptured, native effect duplicates, cleanup is manual or needs extra access, or earlier failed evidence is regraded. +Refs: docs/experiments/U2-independent-onboarding.md; docs/independent-onboarding.md; results/u2-independent-onboarding-20261011-v2-dev + +## UFU1 · update · 2026-10-12 14:19 +Observed: the old Automation-denied main-boundary scenario leaves the exact owned AX window open; the new helper closes that window and preserves a same-name other-path window. Root reviews the worker diff and69focused helper/onboarding/packaging checks pass, with Ruff and notebook valid. Source3d0984d includes review notes on synchronous AX read deadlines, second-SIGINT cleanup protection and bounded actual file snapshots. Both final packet builds match14members and03dc0079; installed3.12/3.14 help/import/member/provenance preparation smokes pass without Git, stopping at an intentional uv stub. Claude approves initial23e5e16f artifact; final03dc0079 confirmation is pending. Guest unlock, SIP/prerequisite provenance and bounded awake hold are observed; no v4 guest effect or permission reset has occurred. +Changes: initial developer validation used a stale editable environment, then a relative source path failed the subprocess CLI check; absolute worktree PYTHONPATH corrected the probe without product/test changes. Guest close-preference defaults key is absent and GUI search yields no result; effective preference remains unverified rather than inferred off. +Next: complete final Claude artifact confirmation, transfer pristine v4, cold-install new guest dependencies, obtain explicit guest Terminal-only reset approval, then capture denial/grant/restart and installed automatic-cleanup evidence. UF1/UF2/UF3 remain open pending their real guest boundary. +Refs: results/onboarding-fixture-v4-preparation-20261012; output/u2-fixture-v4-dev/baseline-automation-behavior.txt; output/u2-packet-v4-final; Claude1791828646123834000; U3-packet-v4-permission-journey.md + +## UFU2 · update · 2026-10-12 14:20 +Observed: Claude confirms exact03dc0079 artifact for U3 but identifies an N3 API semantic error: AXUIElementSetMessagingTimeout applies only to the supplied element, not new equal application references created by product lookup. Apple documentation independently confirms the system-wide element is required for this process's global AX timeout and setting0 resets its default. Current pre/post checks refuse late identities but the prior inferred one-second native RPC bound is not established. No v4 guest effect/reset has occurred. +Inferred: the process-local system-wide timeout, scoped to identity collection and reset to default during teardown, should cover those internal references without changing OS permissions; this remains under existing UF2 and needs corrected boundary verification before a new freeze. +Next: correct the setter scope and cleanup restoration, retain old artifacts and refreeze the reviewed qualification candidate. Do not claim restoring an unknown previous custom timeout. +Refs: Claude1791829172719781000; https://developer.apple.com/documentation/applicationservices/1459345-axuielementsetmessagingtimeout; source3d0984d; UF2 + +## UFU3 · update · 2026-10-12 14:32 +Observed: corrected process-global AX timeout checks pass with47helper cases and73combined checks. Claude approves the exact final packet eef3632506680d74b21b511cec905d73de2324f951e924b0716f2309490f590d, frozen at21599c6, with no blockers. The one-shot VM sender has closed after transferring312685bytes; guest hash and pristine verification match14members and the unchanged wheel9bb0440d. A new private guest environment completes all seven cold-install stages with exit0 and an actual passed receipt. The guest is unlocked and no v4 fixture/native effect/reset has occurred. +Changes: final-r2 is the sole guest qualification candidate; earlier preparation artifacts remain preserved. This is fresh installation state on the existing macOS26.6.2 guest, not a newly cold OS. Existing Terminal Accessibility has not yet been cleared; explicit supported per-host reset approval is pending. +Next: after reset approval, capture denied strict doctor and resource-free fixture refusal, retain their hashes before the actual grant, then record the same-host permission transition and restart before installed native and automatic-cleanup checks. UF1/UF2/UF3 remain open. +Refs: results/onboarding-fixture-v4-preparation-20261012; output/u2-packet-v4-final-r2; Claude1791829509202848000; guest footwork-u4/packet-verification.txt; guest footwork-u4/footwork/output/install-v1/receipt.json + +## UFU4 · update · 2026-10-12 14:39 +Observed: after specific user approval, supported guest Terminal-only Accessibility reset exits0 at18:34:59Z. Installed strict doctor reports accessibility_not_granted and exits1; frozen denied fixture exits2 with its explicit pre-resource refusal, and its directory is absent. At18:38:15Z the eight denial/install baseline hashes are retained and visibly printed before grant. The actual guest Accessibility pane shows Terminal OFF; action-time grant confirmation is pending. +Changes: a long public shell-input command was truncated with repeated characters by the VM input transport and was never executed. The owned shell was closed, its caffeinate hold ended, and another same-host shell now uses shorter commands and a new bounded awake hold. A preliminary hash glob included its own manifest; baseline-hashes.txt corrects this with explicit denial file groups, preserving the preliminary file. No product effect, permission grant, or qualifying fixture has occurred. +Next: capture the approved same-host ON transition, restart Terminal, then run installed strict doctor, fixture, native/replay/report and automatic cleanup. UF1/UF2/UF3 remain open. +Refs: guest permission-reset.txt; guest baseline-hashes.txt; guest doctor-before-exit.txt; guest denied-fixture-console.txt; guest denied-resource-check.txt; CUA Accessibility OFF observation; U3-packet-v4-permission-journey.md + +## UFU5 · update · 2026-10-12 14:51 +Observed: user approves the actual Terminal Accessibility grant. Codex clicks that exact guest switch after the18:40:09Z clock reading; refreshed UI visibly shows ON. No authentication prompt appears. Terminal is visibly quit and relaunched before installed strict doctor exits0, reporting Accessibility granted and Screen Recording not_granted. Frozen fixture-v1 reports ready in its hold phase; exact installed config generation exits0. The installed native trial has now started once; its outcome and automatic cleanup remain unobserved. +Changes: public-command transport truncation recurs on a long diagnostic readback; unexecuted input is discarded by closing only the owned shell. Short octal commands and explicit shell variables now preserve exact paths and arguments, which are visibly read back before the native trial. Plain special-character input was also refused without launching the intended hold; a short octal hold succeeds. These are operator transport events, not permission or product failures. +Next: retain trial result without repeating an uncertain effect, signal the exact owned fixture once, inspect automatic cleanup and absence, then export raw evidence for independent Claude review. UF1/UF2/UF3 remain open. +Refs: guest permission-grant.txt; guest terminal-restarted.txt; guest doctor-after-exit.txt; guest footwork/output/doctor-after.json; guest footwork/output/fixture-v1/fixture-status.json; guest config-exit.txt; U3-packet-v4-permission-journey.md + +## UFU6 · update · 2026-10-12 14:55 +Resolves: UF1 = failed; UF3 = failed +Observed: installed native-v1 exits0 and reports passed with its actual installed worker observed. Exact fixture PID52621 receives one SIGINT. The document disappears visually without an operator close; cleanup nevertheless records ax_attribute_refused:AXRole:-25202 and exits6. Owned browser52622 and listener cleanup are confirmed. File snapshots change from15to24bytes. Thus the required automatic confirmation gate fails; original frozen run-v1 remains failed and no new grant or native effect is retried. +Inferred: after successful close, a previously observed AX window or child becomes invalid while cleanup is inspecting its sheet tree. Invalid-element evidence must trigger bounded fresh observation, never substitute for absence. +Next: export immutable raw-v1 evidence; reproduce the close-versus-read race before correcting it, obtain Claude review, and qualify a separate frozen repair run. UF2 awaits final source/receipt review. +Refs: guest native-exit.txt; guest native-console.txt; guest footwork/output/fixture-v1/cleanup.json; guest fixture-exit.txt; guest cleanup-signal.txt; CUA document disappearance + +## UF4 · prediction · 2026-10-12 14:55 +Hypothesis: a narrow bounded retry of invalid AX element errors after AXPress permits fresh same-application window enumeration to confirm actual disappearance, without allowing read errors or changed identity to certify cleanup. +Test: reproduce the observed stale AXRole child/window race against21599c6; then cover a fresh readable absent window, unreadable AXWindows, persistent invalid element, renamed/reused identity and unrelated windows. Correct only fixture/helper tests, review with Claude and run a separately frozen fixture/native/automatic-cleanup trial on the same granted guest. Retain failed U3v1; a post-grant repair trial is not a new first-use pass. +Predicts: stale reads cause no extra action; confirmation requires a subsequent fresh readable AXWindows snapshot proving the original identity absent. Persistent errors remain bounded unknown/error, foreign identity refuses, and real repaired fixture closes automatically. +Against: an AX error alone confirms closure, a stale element is pressed again, an unrelated window is closed, retry exceeds the cleanup deadline, or live cleanup still lacks confirmation. +Refs: UFU6; scripts/capability_fixture.py close_owned_document/discard_button; frozen21599c6 + +## UFU7 · update · 2026-10-12 15:03 +Resolves: UF3 = failed +Observed: UFU6 records two resolutions on one field, but the notebook reader recognizes only its first prediction. This single resolution makes the already observed failed U3v1 journey machine-readable. Worker boundary reproduction has five failures and one pass against21599c6; its narrow post-close invalid-observation retry passes55helper cases. Root independently reviews the diff and runs81combined checks, Ruff check/format and notebook validation successfully. AXPress errors remain terminal; confirmation still requires fresh readable absence and unchanged identity. +Changes: only fixture/helper tests change. The failed guest archive is being exported before a separately frozen repair packet; no original receipt is changed and no native effect is replayed. +Next: freeze the repair, obtain Claude review of source/artifact and original guest evidence, then perform the preregistered post-grant repair trial. UF4 remains open. +Refs: output/u2-fixture-v4-dev/uf4-before.txt; output/u2-fixture-v4-dev/uf4-after.txt; UFU6; tests/test_capability_fixture.py + +## UF5 · prediction · 2026-10-12 15:08 +Revises: UF4 +Hypothesis: the reviewed stale-observation repair permits all U3 gates in one separately frozen complete permission-state journey, avoiding a composite of different artifacts. +Test: use packet205577ee/source445a348 in a new guest root, verify pristine bytes and cold-install anew. With existing explicit guest permission authorization, reset only Terminal Accessibility through supported tccutil, hash denied doctor/resource-free refusal before the action-time approved switch, quit/relaunch Terminal and execute one installed append/replay/report trial and automatic cleanup. Retain failed U3v1. This replaces UF4's planned post-grant trial with a stricter full journey on the repaired artifact. +Predicts: the same frozen artifact supplies every denial/grant/restart/native/cleanup gate, stale AX reads never replay a close, and cleanup confirms fresh window absence plus browser/listener/worker absence without manual close or extra grants. +Against: any required gate fails, a read error substitutes for absence, action repeats, an unrelated resource is affected, or the artifact changes during measurement. +Refs: U3-packet-v4-permission-journey.md; UF4; Claude1791832031158825000; results/u3-packet-v4-permission-20261012-v1 + +## UFU8 · update · 2026-10-12 15:15 +Observed: Claude independently verifies original raw evidence and its sole cleanup failure, then approves exact205577ee for full UF5. New guest root footwork-u4-repair verifies14pristine members and cold-installs all seven stages,115packages. Supported Terminal-only reset succeeds19:11:29Z. Denied installed doctor/refusal are observed with no fixture directory. Corrected same-command exit capture retains doctor1/refusal2/absence0, with nine baseline hashes visibly printed before grant. No repaired native effect has occurred. +Changes: the public input wrapper invokes printf before eval, so separate later echo of the prior exit is invalid; initial exit files remain as operator logging errors. Only resource-free denial probes repeat in distinct v2 names, capturing actual exits in the same eval. A preliminary manifest glob includes itself; qualified-baseline.txt uses explicit groups and is the authoritative baseline. These do not change product source or permission state. +Next: actual same-host grant confirmation, Terminal relaunch, installed native and automatic cleanup, export and independent receipt review. UF4/UF5 remain open; prior failed U3v1 is unchanged. +Refs: guest footwork-u4-repair/qualified-baseline.txt; guest doctor-before-v2-exit.txt; guest denied-fixture-v2-exit.txt; Claude1791832201147781000 + +## UFU9 · update · 2026-10-12 15:25 +Observed: user approves the repaired run's exact Terminal Accessibility grant. Before reaching the switch, CUA scroll/reconnection attempts time out. Fresh inventory then reports Tart not running and the isolated launcher reports the named guest stopped. No repaired grant action or native trial has been issued. Root restarts the same guest with its original no-clipboard/no-audio/no-USB flags and read-only qualification ISO; the VM viewer is observable again at boot. +Changes: the cause of the VM stop is unavailable. Its prior denied baseline and installed files require guest readback after boot; an unobserved state cannot count as grant or cleanup evidence. +Next: observe guest login/session, preserve and verify pre-stop baseline bytes, then apply the already specifically approved grant once and continue same frozen artifact if eligibility remains established. Credentials remain with the human. +Refs: CUA timeoutReached then inventory; tart list stopped/running; isolated tart run session80357; guest qualified-baseline.txt; UF5 + +## UFU10 · update · 2026-10-12 15:31 +Resolves: UF5 = inconclusive +Observed: human logs in after guest restart. All eight authoritative pre-stop baseline hashes match (UFU8's nine was a count error). Installed post-boot doctor exits0 reporting Accessibility granted, and Settings shows Terminal ON before root issues any grant click. Grant actor/time are unavailable. Thus this interrupted full journey cannot satisfy captured-transition qualification. No repaired native effect has occurred and no original result is regraded. +Changes: preserve pre-stop and post-boot files. The already specifically approved repaired-run grant has not been performed by root; its authorization is retained. Establish another supported same-host denied baseline in distinct v3 names before that one approved click. +Next: UF6; retain interruption separately and do not infer a TCC rollback cause or human action from ON state. +Refs: guest post-boot-baseline.txt; guest doctor-post-boot.json; CUA Terminal ON observation; UF5 + +## UF6 · prediction · 2026-10-12 15:31 +Revises: UF5 +Hypothesis: the same reviewed205577ee artifact can qualify the complete scoped permission journey when denial and the explicitly approved grant are captured consecutively without an intervening VM stop. +Test: supported guest Terminal-only reset in separate v3 records; installed denied doctor and resource-free fixture refusal with same-command exit capture; hash baseline before observing OFF and applying the already approved ON click once. Visibly quit/relaunch Terminal, then one installed native/replay/report trial and automatic cleanup. Preserve interrupted UF5 and failed U3v1. +Predicts: one source/artifact and a captured uninterrupted permission transition supply all required gates, with installed origins, one durable operation, exact oracle and automatic resource absence. +Against: a transition is missing again, a required gate fails, input repeats to mask uncertainty, or another resource/permission is affected. +Refs: UFU10; U3-packet-v4-permission-journey.md; source445a348; Claude1791832201147781000 + +## UFU11 · update · 2026-10-12 15:39 +Observed: separate supported reset succeeds19:31:47Z; installed doctor1/refusal2 and resource absence0 are retained and eight baseline hashes printed before grant. Terminal is visibly OFF. Root issues one approved switch click after19:33:55Z; macOS presents an actual password-required Privacy & Security dialog. Human authenticates and reports completion; refreshed state shows ON before19:35:16Z observation. Root never enters credentials. Terminal Quit navigation initially leaves it running; its displayed Command-Q shortcut produces the owned-caffeinate confirmation, then confirmed termination visibly removes both owned Terminal windows. Relaunch is issued next. +Changes: grant actors are root click plus human authentication; the session belongs to admin group. Earlier interrupted grant remains unavailable, not substituted into this transition. GUI menu attempts are operator transport events; no fixture or native effect has started. +Next: installed strict doctor after actual Terminal relaunch, one native/replay/report trial, automatic cleanup and Claude raw-receipt review. UF6 remains open. +Refs: guest hashes-final.txt; guest grant-v3.txt; CUA OFF/password dialog/ON/Terminal absence observations; UF6 + +## UFU12 · update · 2026-10-12 15:59 +Resolves: UF4 = held +Observed: repaired installed native trial exits0 with exact oracle, same-token replay, one durable final/applied/sent row and accepted completion. One SIGINT to owned fixture1355 confirms exact document window disappearance, browser1356 and listener cleanup, fixtureexit0. Independent fixture/browser/worker2248 absence and HTTP refusal pass. All eight helper hashes match the frozen manifest. Five failing stale-read reproduction cases against21599c6 and55corrected helper cases establish the boundary distinction; live repaired cleanup supplies the missing confirmation. No operator document close or repeated native effect occurs. +Inferred: the observed stale-observation mechanism is addressed within the tested boundary; the live pass does not establish every possible AX race. +Next: retain failed UF1/UF3 and interrupted UF5; close final UF6 with independent raw review. +Refs: results/u3-packet-v4-permission-20261012-v3; output/u3-export-v3; source445a348; UFU7 + +## UFU13 · update · 2026-10-12 15:59 +Resolves: UF2 = held +Observed: frozen browser startup reports ready for owned1356 and private CDP49153 before exact fixture scope/hold. Boundary checks cover delayed readiness, startup exit/timeout, unique versus ambiguous identity and cleanup. Both original and repaired live runs reach the owned browser/native path. Claude independently approves final raw receipts; readiness never substitutes for identity. +Next: retain normal live readiness evidence separately from simulated startup failure coverage; no cold Chrome launch claim. +Refs: results/u3-packet-v4-permission-20261012-v3/startup.json; tests/test_capability_fixture.py; UFU3; Claude1791835064888832000 + +## UFU14 · update · 2026-10-12 15:59 +Resolves: UF6 = held +Observed: all required gates pass on exact205577ee/source445a348. Eight final pre-grant baseline hashes match; actual OFF/password-dialog/ON and Terminal shutdown/relaunch are observed. Claude independently checks raw94member archive and two-member supplement, read-only journal, installed origins and all eight unchanged helpers; final1791835064888832000 discharges worker absence and approves scoped qualification. Reviewer reports modelclaude-opus-5-5. Disk persistence remains not_checked and Screen Recording remains not_granted. +Next: close this package with its exact host/OS limits; recommend external agent installation qualification next. +Refs: results/u3-packet-v4-permission-20261012-v3; Claude1791834939955400000; Claude1791835064888832000; UFU10 + +## UFS1 · summary · 2026-10-12 15:59 +Established: fixes built and reviewed; repaired frozen packet exercised through supported permission reset, captured approved grant with human authentication, actual Terminal restart, installed native/replay/report and automatic owned cleanup on one SIP-enabled macOS26.6.2 guest. Raw export and independent Opus5.5 reviewer-reported review pass. UF1/UF3 remain failed, UF5 inconclusive, UF2/UF4/UF6 held; no earlier result is regraded. +Uncertain: cold OS/first Chrome launch, other responsible hosts or OS versions, MDM, pixel capture and saved-file persistence are not qualified. Actual VM stop cause and interrupted grant actor remain unavailable. +Decision: accept this scoped permission-state journey. Next recommended package is a real external-agent installation and first bounded task on the candidate; keep broader release claims behind that evidence. +Refs: results/u3-packet-v4-permission-20261012-v3/report.md; results/u3-packet-v4-permission-20261012-v1; source445a348 diff --git a/jevdual/docs/notebook/2026-10-12-reusable-body-recovery.md b/jevdual/docs/notebook/2026-10-12-reusable-body-recovery.md new file mode 100644 index 0000000..6c73368 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-12-reusable-body-recovery.md @@ -0,0 +1,87 @@ +# Reusable body contract and interrupted host recovery + +## SIM14-P1 · prediction · 2026-10-12 22:14 +Hypothesis: a validated contiguous event tail can be reconciled against the retained manifest without replaying effects or treating an unpublished event as completion proof. +Test: reproduce SIM13 event-fsync-before-manifest SIGKILL, then test the repair, malformed/gapped/conflicting tails and a browser restart followed by explicit Resume; inspect command IDs and wire sends. +Predicts: restart retains the original history, pauses, refuses inconsistent evidence and allocates the next unused journal sequence; explicit Resume obtains a fresh observation without repeating an existing command. +Against: FileExistsError persists, history is overwritten, a tail grants effect completion, a command identity is discarded or an effect is replayed. +Refs: docs/experiments/SIM14-reusable-body-recovery.md; tools/physical-workcell/test_workbench_crash_runtime.py + +## SIM14-P2 · prediction · 2026-10-12 22:14 +Hypothesis: durable planner process ownership combined with restart fencing prevents an independent child left by host SIGKILL from producing an actionable old decision. +Test: externally SIGKILL an owned host during active child planning; restart its retained root, verify ownership before cleanup, test identity mismatch and partial ownership records, then explicitly Resume with fresh image evidence in the browser. +Predicts: old planner authority is revoked, verified owned children retire before new planning, unresolved ownership fails closed, the job stays paused and the old observation cannot be dispatched. +Against: an unrelated process is signalled, a live old writer survives successful recovery, an old decision dispatches or recovery resumes automatically. +Refs: docs/experiments/SIM14-reusable-body-recovery.md; tools/physical-workcell/codex_policy.py; tools/physical-workcell/network_operator_runtime.py + +## SIM14-P3 · prediction · 2026-10-12 22:14 +Hypothesis: extracting identity, lease, command-envelope and receipt validation behind an operation-contract seam preserves strict bench behavior while allowing a non-bench operation schema without simulation imports. +Test: shared contract tests with a minimal second operation vocabulary, existing invalid body command tests and the network/client/operator recovery suites; inspect source-pinning closure. +Predicts: wire semantics and existing bounds remain unchanged, unknown bench operations stay refused, generic validation imports no bench implementation and every new authority-bearing source is pinned. +Against: unknown commands are admitted, validation weakens, old roots silently migrate across source changes, or the reusable module still needs MuJoCo/bench operations to import. +Refs: docs/experiments/SIM14-reusable-body-recovery.md; tools/physical-workcell/body_protocol.py + +## SIM14-U1 · update · 2026-10-12 22:21 +Observed: the pre-fix publication regression reproduced FileExistsError on Resume. The first guardian process regression refused cleanup proof because Darwin temporarily returned EPERM for a dead process group; this stayed blocked rather than granting success. Retrying that transient state while still requiring eventual ESRCH yielded three passing owned-process checks, including a planner and grandchild surviving host-only SIGKILL until the guardian retired them. Seven alternate-operation contract checks and 24 existing body/client checks passed in separate development runs. +Inferred: the selected journal repair and parent-liveness mechanism have initial process evidence; actual image-guided browser recovery and stronger malformed-evidence checks remain required before adoption. +Next: finish negative checks and source-pinning closure, freeze committed source, then run publication and active-planning browser recovery trials. +Refs: tools/physical-workcell/output/sim14-publication-before.log; tools/physical-workcell/output/sim14-runtime-first.log; tools/physical-workcell/output/sim13-host-crash-3b0976e974/; tools/physical-workcell/test_body_contract.py + +## SIM14-U2 · update · 2026-10-12 22:25 +Observed: extraction review caught a zone-validation mismatch: the storage helper did not include the bench's reachable-region bounds. The bench schema now retains those exact bounds with a regression case. The first legacy network-fault suite was invalidated by a concurrent whitespace edit; that attempt remains retained. A frozen rerun passed all 43 schema/body/client/network checks. The shared transport import test excludes bench, agent_bench, MuJoCo and numpy; the alternate simulated operation vocabulary exercises actual TCP reply-loss/client-restart recovery and incomplete lower-proof blocking. The extraction is committed as ee77644. +Next: complete runtime regressions and independent review, then freeze the combined implementation and live trial sources. +Refs: tools/physical-workcell/output/sim14-body-contract-conformance-v2.json; tools/physical-workcell/output/sim14-body-contract-conformance-v2.log; tools/physical-workcell/test_body_contract.py + +## SIM14-P4 · prediction · 2026-10-12 22:30 +Hypothesis: a blocking prompt write can prevent the guardian from observing host death if its planner never reads stdin; multiplexing nonblocking prompt delivery with host liveness preserves bounded retirement. +Test: use an owned non-reading planner and a prompt larger than the pipe capacity, kill the host only, then verify group retirement and closed ownership. Run before and after the bounded delivery change. +Predicts: the current guardian cannot publish cleanup proof in the timeout, while bounded nonblocking delivery observes EOF and retires the group; normal policy and crash tests remain unchanged. +Against: the current prompt write does not block under a verified full pipe, or the repaired guardian still fails to retire ownership after host death. +Refs: tools/physical-workcell/planner_guard.py; docs/experiments/SIM14-reusable-body-recovery.md + +## SIM14-U3 · update · 2026-10-12 22:28 +Observed: the frozen runtime development checks passed 18 focused and 59 existing operator/HTTP/health regressions. Independent review found a blocking stdin write before the guardian liveness loop; it is a potential orphan-retirement availability gap rather than an observed effect replay. The live harness will retain independent process absence and owner-transition evidence in addition to guardian closure. +Changes: SIM14-P4 was committed before its test; its heading time was mistyped as 22:30. The clock command immediately before that commit returned 2026-10-12 22:27; Git records the prediction order. +Next: reproduce and repair the full-pipe case, strengthen the live ownership evidence, then freeze the final source before actual model trials. +Refs: tools/physical-workcell/output/sim14-runtime-final.log; tools/physical-workcell/output/sim14-runtime-regression.log; commit 05f3c0d; commit fbdeb20 + +## SIM14-U4 · update · 2026-10-12 22:30 +Resolves: SIM14-P4 = failed +Observed: the current guardian retired a never-reading planner after host-only SIGKILL with a valid 60000-byte prompt; cleanup completed in the retained pre-change test. The expected blocked write was not reproduced on this Mac. +Inferred: pipe capacity on this host may accommodate the bounded prompt. A multiplexed write still makes the liveness/watchdog check independent of platform pipe capacity, but this run does not prove a prior full-pipe failure. +Next: measure the owned pipe capacity, preserve the passing baseline, implement bounded prompt delivery and run the policy/crash regressions before live qualification. +Refs: tools/physical-workcell/output/sim14-planner-backpressure-before.log + +## SIM14-U5 · update · 2026-10-12 22:34 +Observed: the maximum valid 65536-byte prompt also completed the original blocking write before host death; the owned pipe had all bytes queued and cleanup succeeded. The revised nonblocking path was actually backpressured with 16384 bytes queued, yet host EOF retired its planner group. Twenty final focused tests passed, including exact full-prompt delivery. An intermediate observer saw a partially published closure file; startup and closure now publish atomically while ownership remains locked. Ten trial/scorer controls pass, and independent process/group absence, changed planner incarnation and stable retired-call files are retained before Resume. +Next: freeze the combined committed sources, run both actual image-guided browser interruption cases and recheck the native receipt gap; preserve all interrupted calls and inspect exact sends after actors stop. +Refs: commit c097da8; tools/physical-workcell/output/sim14-planner-backpressure-before-maxbound.log; tools/physical-workcell/output/sim14-planner-backpressure-final.log; tools/physical-workcell/output/sim14-planner-backpressure-final-v2.log; commit 6839c23 + +## SIM14-U6 · update · 2026-10-12 22:45 +Observed: publication-v1 passed post-stop scoring on eed92cc with one completed measurement/reset cycle, 13 image-guided decisions and 40 single-send commands. Planning-v1 proved main-group retirement and paused restart, but its resumed UI/API capture raced the deliberately lost reply: the screenshot showed Running while the later recorded service state was blocked. That attempt was recovered read-only and stopped incomplete; it is unqualified. Its crash trace also captured Codex shell-snapshot children in a separate process group, which the main-group proof did not account for. Read-only process inspection now finds those recorded PIDs absent; the installed CLI lists shell_snapshot as enabled by default. +Next: retain both versions, remove the unnecessary startup snapshot, account for every observed child/group at retirement and rerun final-source browser qualification. +Refs: tools/physical-workcell/output/sim14-publication-v1/recovery-score.json; tools/physical-workcell/output/sim14-planning-v1/attempt-status.json; tools/physical-workcell/output/sim14-planning-v1/process-ledger.json + +## SIM14-P5 · prediction · 2026-10-12 22:45 +Hypothesis: disabling Codex shell snapshots removes its incidental detached startup group, while complete observed-child ownership accounting detects any surviving writer that a main-group-only proof misses. +Test: add exact CLI feature flags and bounded verified-owned child retirement where needed; run a controlled detached-child regression and final actual Codex crash gates. Bind every observed descendant identity/group to read-only absence proof before Resume, and preserve prior trials. +Predicts: normal image-guided planning remains functional, actual startup has no shell-snapshot group, and all recorded old child identities/groups are gone before fresh planning; missing cleanup evidence refuses recovery rather than guessing or signalling unrelated processes. +Against: shell-snapshot children still appear, an observed old writer survives successful retirement, an unrelated process is signalled, or the policy can no longer deliver its exact bounded image request. +Refs: tools/physical-workcell/codex_policy.py; tools/physical-workcell/planner_guard.py; tools/physical-workcell/qualify_workbench_recovery.py + +## SIM14-U7 · update · 2026-10-12 22:55 +Observed: the final guardian development suite passed 25 checks. A controlled observed detached child withheld closure and blocked recovery while an unrelated process remained untouched. Missing, inconsistent, empty and truncated leader evidence is refused. Fourteen trial controls bind every captured child identity and group; exec, reparenting and group changes cannot be counted as exit. The UI now retains host-unavailable control vetoes and offline camera provenance across cached job selection. Review found no replay path or wrong-owner dispatch; unsampled detachment remains outside the evidence claim. +Next: freeze final source, rerun publication and active-planning browser cases on fresh v2 roots, capture Running before arming a lost reply, and recheck native receipt-gap blocking. +Refs: commit 76f4c9d; commit 434aa4c; commit 4f420df; tools/physical-workcell/output/sim14-planner-leader-proof-final.log + +## SIM14-U8 · update · 2026-10-12 23:29 +Resolves: SIM14-P1 = held; SIM14-P2 = held; SIM14-P3 = held; SIM14-P5 = held +Observed: three final actual browser cases passed post-stop scoring on c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54. Publication-v2 recovered the exact original receipt and completed its cycle. Planning-v2 independently proved every captured child identity/group absent, changed owner and stable retired-call files, then completed measurement/reset after read-only recovery of a lost measurement reply. Shell-snapshot zsh startup disappeared; transient node/git groups were still observed and all were accounted for. Native-gap-v1 retained its original uncertain command through a new body boot and two receipt checks, with no successor or reset. The final cases contain 29 image-guided decisions, one interrupted reservation and 91 single-send commands. The retained test logs contain 152 unique passing identifiers. A cached job click during host outage kept action controls disabled and camera provenance offline. +Next: keep the recovered product paused and connected, preserve scored snapshots before handoff and close the package with its limits. +Refs: results/reusable-body-recovery-20261012/summary.json; results/reusable-body-recovery-20261012/publication/recovery-score.json; results/reusable-body-recovery-20261012/planning/recovery-score.json; results/reusable-body-recovery-20261012/native-gap/hard-crash-score.json + +## SIM14-S1 · summary · 2026-10-12 23:29 +Established: selected host event-publication and active-planning hard crashes recover without action replay. Valid audit tails repair their index; incomplete command and planner proof fail closed. Shared identity/lease/envelope/single-send transport is separated from bench operation schemas and verified with a second simulated TCP vocabulary. Two completed cycles and one safely blocked native receipt gap are independently scored; all trial actors were reaped. The live planning-v2 root was restarted only after scoring: paused, connected at body epoch two, one completed job, no stage or model pending, 44 commands unchanged and identical native manifest hash. A handoff-only bounded loopback forwarder preserves the retained transport origin with no faults, cache, retries, health-log budget or trial deadline. +Uncertain: arbitrary instruction-level/client publication windows, power loss, unobserved detach/reparenting between process inventories, hardware safety and authenticated cross-machine control remain unqualified. Observed detached survivors block recovery instead of being signalled from stored PIDs. Current identity admission remains simulation-only; journal/job budgets remain finite. The native receipt gap has no automatic release. The original blocked-stdin failure prediction did not reproduce on this Mac; prior attempts and the capture race remain separate from final metrics. +Decision: retain the reusable contract and guarded planner/journal recovery; hand back the completed paused simulation and its evidence. Hardware adapters must supply and qualify their own operation schemas, bounds and independent effects proof. +Refs: results/reusable-body-recovery-20261012/report.md; results/reusable-body-recovery-20261012/attempt-ledger.json; results/reusable-body-recovery-20261012/handoff.json; tools/physical-workcell/output/sim14-handoff/forwarder.py diff --git a/jevdual/docs/notebook/2026-10-12-workbench-hard-crashes.md b/jevdual/docs/notebook/2026-10-12-workbench-hard-crashes.md new file mode 100644 index 0000000..80243c9 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-12-workbench-hard-crashes.md @@ -0,0 +1,73 @@ +# Workbench hard-crash boundaries + +## SIM13-E1 · exploratory · 2026-10-12 15:22 +Question: which durable body/workbench and child-process boundaries can survive abrupt process death, and which require an unresolved outcome to stay blocked? +Test: source inspection of actual persistence, request association and process ownership; compare with qualified graceful restarts. Choose deterministic crash boundaries before implementing or running tests. +Refs: docs/experiments/SIM13-workbench-hard-crashes.md; tools/physical-workcell/body_network.py; tools/physical-workcell/network_operator_runtime.py; tools/physical-workcell/qualify_workbench_restarts.py + +## SIM13-P1 · prediction · 2026-10-12 15:22 +Hypothesis: exact durable receipts permit read-only recovery after a hard crash, while incomplete effect evidence remains blocked; neither case needs action replay. +Test: causal socket/native SIGKILL regressions followed by frozen actual-model browser trials at in-flight and uncertain body/workbench boundaries; independently inspect sends, receipts, native state, boot/control changes and owned children after cleanup. +Predicts: known receipts recover the original single-send command and leave the agent paused; Resume precedes fresh work. Missing or inconsistent proof stays visibly blocked with zero new effect admissions. No owned child writes across restarted root ownership. +Against: a command is resent, incomplete evidence grants success, old decisions gain new-boot authority, a stage loses its original identity or an owned child survives the restart boundary with write access. +Refs: docs/experiments/SIM13-workbench-hard-crashes.md; results/workbench-hard-crashes-20261012/ + +## SIM13-NATIVE · prediction · 2026-10-12 15:26 +Hypothesis: killing the body after native state persistence but before the Bench terminal receipt leaves an unresolved effect that ordinary restart cannot safely complete from pose alone; killing after an indexed Agent receipt permits exact read-only recovery. +Test: an external diagnostic launcher stops immediately before Bench._finish on the first native move, binds its marker to the accepted dispatch, persisted next state and owned PID, then the harness issues SIGKILL. Compare with existing after_dispatch SIGSTOP followed by SIGKILL. Use public TCP readback and independent stopped native evidence. +Predicts: the native-gap case retains accepted server/agent intents, a busy Bench manifest and next-state bytes but no indexed lower result. Restart retains identities, advances boot, stays unknown and blocks successors without replay. The indexed-result case recovers its exact historical receipt with unchanged state and one send. +Against: pose is promoted to completion, restart/reconciliation repeats motion, identities or state diverge, owned native writers cross restart ownership or indexed proof cannot recover. +Refs: tools/physical-workcell/crash_body.py; tools/physical-workcell/test_crash_body.py; tools/physical-workcell/body_network.py; tools/physical-workcell/bench.py + +## SIM13-HOST · prediction · 2026-10-12 15:26 +Hypothesis: once the host has durable stage and client send identity and the body has a completed receipt, SIGKILL before reply delivery or while uncertain permits recovery of that exact command without replay. +Test: hold an actual completed dispatch response externally, kill the owned host, restart its retained root/port, then read its original receipt and explicitly Resume. Pair with already-uncertain body/host kills and stale control/source checks. +Predicts: the original command ID and immutable request persist, one actual body acceptance remains, no POST or model replay occurs during restart/receipt lookup, and explicit Resume precedes fresh observation. Abrupt host death at this gate has no active planner or native child. +Against: the stage loses command association, a decision or command is replayed, automatic continuation occurs or a live owned child writes across restart. +Refs: tools/physical-workcell/test_workbench_crash_runtime.py; tools/physical-workcell/qualify_workbench_crashes.py; tools/physical-workcell/score_workbench_crashes.py + +## SIM13-U1 · update · 2026-10-12 15:26 +Observed: source inspection identifies indexed lower receipts as the body's recovery proof. Existing before/after_dispatch barriers bracket lower dispatch; an external launcher can expose the deeper native-state-before-receipt gap. Host restart preserves sent command association at acknowledged boundaries, but arbitrary event-publication and pre-client-ID instruction windows have separate limitations. Independent Codex sessions can survive a host kill during active planning; selected command gates occur after planner completion and must prove no active planner. +Next: exercise owned hard-kill regressions and freeze four actual browser cases: host in-flight with known receipt, body after native motion without receipt, and each role with an already-uncertain known command. +Refs: tools/physical-workcell/body_network.py; tools/physical-workcell/network_operator_runtime.py; docs/experiments/SIM13-workbench-hard-crashes.md + +## SIM13-U2 · update · 2026-10-12 15:32 +Resolves: SIM13-NATIVE = held +Observed: three body-launcher checks passed. SIGKILL after_native retained 867 native steps and a saved next generation, while Bench stayed busy and the Agent result remained absent. Ordinary restart advanced boot, preserved identities and returned unknown on repeated exact receipt reads; a fresh successor was refused and native/core evidence remained unchanged. SIGKILL after_dispatch recovered the exact indexed lower receipt read-only with one send and unchanged native state. All owned child service processes exited. +Next: freeze the diagnostic launcher and browser harness, then exercise the same gap with an actual AI decision in the product. +Refs: tools/physical-workcell/output/sim13-crash-body-first.log; tools/physical-workcell/output/sim13-crash-body-eb4582e2a4f8/conformance.json; tools/physical-workcell/output/sim13-crash-body-f9b6716db22e/conformance.json + +## SIM13-U3 · update · 2026-10-12 15:32 +Resolves: SIM13-HOST = held +Observed: two host process/TCP checks passed. At an externally held completed-body reply, the registered workbench exited by SIGKILL with zero descendants; restart retained the exact original command, Check receipt applied once while paused, and explicit Resume obtained a new challenge. Exactly one dispatch POST and client send intent were retained. A separate event-fsync-before-manifest-publication kill reproduced FileExistsError on Resume; no effect command was sent, but arbitrary instruction-level recovery is not supported by that path. +Next: test the selected acknowledged command gates in the actual browser; retain the event publication gap as an explicit limitation rather than silently excluding it or claiming complete crash recovery. +Refs: tools/physical-workcell/output/sim13-host-crash-runtime-v1.log; tools/physical-workcell/output/sim13-host-crash-e1229762d6/conformance.json; tools/physical-workcell/output/sim13-host-crash-41308cad5f/conformance.json + +## SIM13-U4 · update · 2026-10-12 15:50 +Observed: nine controller/recorder checks and seven scorer controls passed, in addition to five body/host process checks. Independent source review caught a link timestamp mismatch before paid testing; the scorer now uses retained relay timing and binds pinned headers. An initial scorer fixture omitted required headers and failed; its output remains retained. Stronger image/model/native bindings also validated all 27 existing SIM12 decisions read-only. Offline records use unavailable live state with explicitly cached provenance, and restart waits for the observed body boot. +Changes: all four cases use a bounded exact-byte relay; only host-inflight withholds a reply. The native-gap criterion is the first physical move rather than necessarily the first model judgment; earlier fully bound refusals remain visible and cannot advance native state. Product body/client/runtime/core sources remain unchanged. +Next: commit measured controller/scorer sources, then run three recoverable actual-model jobs and one safely blocked native-gap case through the browser. +Refs: tools/physical-workcell/output/sim13-harness-tests-v3/unittest.log; tools/physical-workcell/output/sim13-crash-scorer-negative-v1.log; tools/physical-workcell/output/sim13-crash-scorer-negative-v3.log; tools/physical-workcell/output/sim13-scorer-oldcalls-schema-v1.log + +## SIM13-U5 · update · 2026-10-12 15:54 +Observed: native-gap-v1 on frozen 38b9b146017c39e593cfedd251df223d754134c9 executed one actual image-guided move, saved next state with 1550 native steps and 3164 contact samples, and exited by SIGKILL. Ordinary restart retained the original unresolved command at boot two; two receipt reads left four commands unchanged, with continuation and New job disabled. All owned processes then exited. Post-stop scoring failed because it unconditionally opened measurements.sqlite, which correctly does not exist before the first measurement. +Inferred: the product's blocked behavior matches the prediction; the trial has not passed independent scoring because the scorer assumed a completed-job artifact in an intentionally incomplete case. +Next: correct the gap-only missing-database check without creating evidence, commit a new measured version and rerun the native-gap case. Retain v1 unchanged as a failed scoring attempt. +Refs: tools/physical-workcell/output/sim13-body-native-gap-v1/; tools/physical-workcell/output/sim13-body-native-gap-v1/score-first-failure.log; tools/physical-workcell/output/sim13-body-native-gap-v1/crash-still-blocked.png + +## SIM13-U6 · update · 2026-10-12 16:10 +Observed: the gap-only scorer correction passed eight controls without creating a measurement database. Native-gap-v2 then suffered a browser capture helper closure retaining the first output root; the recorder refused the missing v2 file. Three v1 offline capture files were overwritten by that v2 capture. Both attempts and this evidence limitation are retained. The helper now takes the target root as an explicit argument, and native-gap-v3 passed independent scoring on frozen 4e2dd74d27ec07f0b5a42039e0f30016402cf050. +Next: keep failed attempts separate from qualified metrics and preserve explicit capture paths across cases. +Refs: tools/physical-workcell/output/sim13-crash-scorer-missing-db-final.log; tools/physical-workcell/output/sim13-body-native-gap-v2/capture-error.json; tools/physical-workcell/output/sim13-body-native-gap-v3/hard-crash-score.json; results/workbench-hard-crashes-20261012/attempt-ledger.json + +## SIM13-U7 · update · 2026-10-12 16:10 +Resolves: SIM13-P1 = held +Observed: all four selected actual browser hard-crash cases passed independent post-stop scoring on frozen 4e2dd74d27ec07f0b5a42039e0f30016402cf050. Three recovered known receipts and completed measurement/contact reset; the native receipt gap stayed unknown and blocked. Qualified runs used 41 actual image-only Codex decisions and 127 commands, each with one wire POST and fresh admission proof. Each crashed process exited by SIGKILL, with no descendants at the gate; all trial processes were reaped. The three recoverable cases retained four commands through crash, restart and receipt application; explicit Resume preceded new work. Two additional unqualified native-gap attempts are reported separately. +Next: retain scored evidence before handoff, then leave a recovered product paused and connected. +Refs: results/workbench-hard-crashes-20261012/summary.json; results/workbench-hard-crashes-20261012/body-native-gap/hard-crash-score.json; results/workbench-hard-crashes-20261012/host-inflight/hard-crash-score.json; results/workbench-hard-crashes-20261012/body-uncertain/hard-crash-score.json; results/workbench-hard-crashes-20261012/host-uncertain/hard-crash-score.json + +## SIM13-S1 · summary · 2026-10-12 16:10 +Established: at selected acknowledged command boundaries, hard body/workbench death preserves single-send identity and supports exact read-only recovery where terminal proof exists. Motion saved without its lower receipt remains quarantined; repeated lookup cannot complete it or admit another action. Twenty-two distinct new checks and four actual browser cases passed. Product body/client/core/runtime files were unchanged; new code is diagnostic launch, lifecycle control and independent scoring. The live host-uncertain root was restarted only after scoring for handoff, with its completed job, no pending work and a paused runner. +Uncertain: arbitrary event-publication and pre-client-ID instruction windows remain unqualified. The event-fsync-before-manifest test exposed FileExistsError on Resume. A host killed during active planning can leave an independent planner session; selected command gates had no live children and do not qualify that boundary. Process kill is not proof of power-loss, physical stop or cross-machine security. The native receipt gap has no safe automatic release path. +Decision: retain the no-replay guards and the reproducible crash suite. Report recoverable and safely blocked cases separately; preserve unresolved roots, failed scoring/capture attempts and the original product. Saved qualification results describe stopped trials before live handoff appends administrative events. +Refs: results/workbench-hard-crashes-20261012/report.md; results/workbench-hard-crashes-20261012/publication-gap-limitation.json; results/workbench-hard-crashes-20261012/handoff.json; docs/experiments/SIM13-workbench-hard-crashes.md diff --git a/jevdual/docs/notebook/2026-10-12-workbench-health.md b/jevdual/docs/notebook/2026-10-12-workbench-health.md new file mode 100644 index 0000000..104f402 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-12-workbench-health.md @@ -0,0 +1,68 @@ +# Healthy-job health timeout investigation + +## SIM12-E1 · exploratory · 2026-10-12 14:38 +Question: which boundary caused the SIM11 healthy-job pause: expensive or queued body reads, a stale overlapping health probe, or treating a transport deadline as proof of loss? +Test: compare retained SIM11 event timing with independent source inspection, then use bounded direct-body/link probes and scoped timing instrumentation. Commit a causal prediction before a reproducing test or fix. +Refs: docs/experiments/SIM12-workbench-health.md; results/workbench-restarts-20261012/health-timeout-observation.json; tools/physical-workcell/output/sim11-browser-restarts-v2/ + +## SIM12-P1 · prediction · 2026-10-12 14:38 +Hypothesis: the host can keep a healthy job running while withholding new command admission during a delayed health read, and recover from fresh same-body/same-boot proof without weakening explicit controls or uncertain-command recovery. +Test: after causal reproduction and repair, run outage/epoch/uncertainty/pause regressions and two frozen actual image-guided browser jobs with owned health-delay injection; independently score native results and retained sends. +Predicts: delayed healthy reads cause bounded waiting rather than unrequested Pause; effects still need fresh pinned admission proof; actual loss/epoch/identity/unknown state blocks successors; two jobs complete without health-caused cancellation and with exact receipt recovery. +Against: stale health grants an effect, an unsafe body stays runnable, an unknown command is replayed, explicit Pause is overwritten, or actual-model verification fails. +Refs: docs/experiments/SIM12-workbench-health.md; results/workbench-health-20261012/ + +## SIM12-U1 · update · 2026-10-12 14:47 +Observed: thirty-two sequential pinned body/link reads all succeeded. Direct state median was 92.1 ms and linked state median 94.3 ms; no observed maximum exceeded 136.7 ms. An isolated scan of 83 copied command records performed 249 file reads and 664 canonical request digest builds, with a 20.8 ms median. The warm paused baseline did not reproduce the historical deadline. Independent code review found that one auxiliary deadline cancels the model, full state checks share the body execution lock, and the link may keep its upstream read alive after the caller times out. +Inferred: receipt scanning adds real work but is not sufficient evidence for the original slow response. The confirmed logical weakness is promoting an ambiguous probe deadline to body loss. Bounded auxiliary reads and fresh authoritative admission proof can address that weakness without inferring the historical scheduling cause. +Next: reproduce isolated healthy slow reads, then separate inconclusive telemetry from fresh command admission and bound outstanding probes. +Refs: tools/physical-workcell/output/sim12-diagnosis/summary.json; tools/physical-workcell/output/sim12-diagnosis/timing.jsonl; tools/physical-workcell/network_operator_runtime.py; tools/physical-workcell/workbench_link.py + +## SIM12-DELAY · prediction · 2026-10-12 14:47 +Hypothesis: a healthy auxiliary read delayed beyond its deadline causes a false control cancellation in the original runtime. Treating that deadline as unconfirmed, while requiring fresh pinned ready state before lease and stage admission, preserves healthy progress and still prevents stale or unsafe authority. +Test: real TCP delay of only an auxiliary identity/state reply while a model decision waits; retain the failing original pause and recheck after repair. Pair with fresh-proof failure, epoch/identity changes, unknown outcomes, explicit controls and stale-probe overlap. +Predicts: the original path pauses/cancels despite a live unchanged body; the repaired path keeps the job running, obtains independent fresh full status before any command and completes the bounded action. Hard faults and explicit Pause continue to suppress admission. +Against: the original path already tolerates the delay, repaired code dispatches from cached health, accepts an old boot, cancels the healthy decision or erases pending uncertainty. +Refs: tools/physical-workcell/network_operator_runtime.py; tools/physical-workcell/test_network_operator.py + +## SIM12-FLIGHT · prediction · 2026-10-12 14:47 +Hypothesis: tagging only auxiliary identity/state reads and limiting them to one outstanding upstream request per path bounds the two-hop backlog without affecting untagged authoritative reads or any effect command. +Test: delay an actual upstream health response past the caller deadline, issue repeated tagged probes, and assert at most two upstream health reads, bounded inconclusive replies, no late-result reuse and owned worker cleanup. Challenge invalid probe routing and unchanged command faults/reconciliation. +Predicts: delayed auxiliary results never become command proof; full status still reaches the body independently; expired probe callers cannot produce an unbounded read fanout; all workers exit before ownership release. +Against: a probe annotation changes effect semantics, a cached response authorizes admission, outstanding readers grow with retries or workers survive cleanup. +Refs: tools/physical-workcell/workbench_link.py; tools/physical-workcell/test_workbench_link.py + +## SIM12-U2 · update · 2026-10-12 15:00 +Observed: the original runtime failed a real-socket delayed healthy-state check, cancelling planning while the body remained ready. The original link also failed the repeated-probe test: six downstream probes created three concurrent upstream readers per path. Both meaningful failing logs are retained. Runtime repair uses fresh full ready status before every lease/stage and preserves planning on an inconclusive probe; link repair coalesces only annotated reads. An intermediate new-boot/delayed-state test found that unconfirmed readiness was being treated as known uncertainty; that classification was corrected and its failed output retained. A setup-only wrong interpreter path exited before the first runtime test and was not retained separately. +Inferred: false cancellation and read fanout are reproduced mechanisms; the warm baseline still does not establish the physical cause of the historical slow response. +Next: finish bounded probe cleanup and final control/admission regressions, freeze source, then drive the actual delayed-health browser trial. +Refs: tools/physical-workcell/output/sim12-health-before-fix.log; tools/physical-workcell/output/sim12-link-before-fix.log; tools/physical-workcell/output/sim12-health-targeted-v2.log; tools/physical-workcell/output/sim12-health-targeted-v3.log + +## SIM12-TIME · update · 2026-10-12 15:00 +Observed: the U1, DELAY and FLIGHT headings contain 14:47 from precomposed text; the clock output for that write was 14:49. Commit 3de70f1 holds all three entries before the reproductions and repairs. Earlier text remains unchanged. +Next: use the actual clock result when assembling later entries; Git settles prediction order. +Refs: commit 3de70f1 + +## SIM12-U3 · update · 2026-10-12 15:05 +Resolves: SIM12-DELAY = held; SIM12-FLIGHT = held +Observed: the repaired runtime passed all 26 real-socket/native checks, including fresh proof failure, changed boot, known uncertainty and explicit Pause during proof. The link passed 13 checks, bounding repeated delayed probes to one reader per path and joining them at close. Seven new harness/scorer checks passed. Before-fix failures remain retained. +Next: freeze the measured source, then test two actual image-guided jobs with diagnostic delayed health replies and browser receipt recovery. SIM12-P1 remains open until the independent product score. +Refs: tools/physical-workcell/output/sim12-runtime-final-v1.log; tools/physical-workcell/output/sim12-link-tests-final.log; tools/physical-workcell/output/sim12-harness-tests-v1/unittest.log + +## SIM12-U4 · update · 2026-10-12 15:15 +Resolves: SIM12-FLIGHT = held +Observed: U3 combined two resolutions on one line; the notebook reader retained FLIGHT as open. Thirteen link checks independently passed the reader bound and cleanup. The actual trial also closed its link with zero owned readers, and its relay joined all workers after 685 requests with peak concurrency two. +Next: retain the independent product score and close the package. +Refs: tools/physical-workcell/output/sim12-link-tests-final.log; results/workbench-health-20261012/relay-cleanup.json; results/workbench-health-20261012/health-qualification-score.json + +## SIM12-U5 · update · 2026-10-12 15:15 +Resolves: SIM12-P1 = held +Observed: frozen source 37ebfdb3054b977149d7b60b46254ddd852b749a completed two actual image-guided browser jobs with 27 model decisions, 83 commands and zero cancelled calls or unexpected halts. Independent scoring verified native placement, one SQLite measurement each, contact resets, exact image/proposal/receipt bindings, and fresh pinned admission proof for every command. The lost reply retained nineteen commands through disconnect, reconnect and exact receipt recovery; explicit Resume preceded new work. Browser reload sent nothing. The trial retained 173 healthy delayed replies and no naturally slow upstream read of at least 0.8 seconds. All four owned processes exited zero before scoring. +Next: hand off the connected paused workbench with injection disabled; retain the supplemental exposure audit and limitations separately. +Refs: results/workbench-health-20261012/health-qualification-score.json; results/workbench-health-20261012/qualification-score.json; results/workbench-health-20261012/summary.json; tools/physical-workcell/output/sim12-browser-health-v1/ + +## SIM12-S1 · summary · 2026-10-12 15:15 +Established: a delayed auxiliary read caused a reproduced false cancellation, and repeated probes caused a reproduced reader backlog. The repair distinguishes checking from confirmed unsafe state, bounds probes, discards late replies and requires independent fresh status before each lease/stage. The actual product completed successive jobs under delays with exact receipt recovery. One hundred seventeen distinct conformance checks passed. After scoring, retained roots restarted for handoff at body boot two; administrative restart and relay replacement added guarded halt events after the measured event cutoff 284 while already paused, with no new model or effect commands. +Uncertain: the precise scheduling cause of the original SIM11 delay remains unresolved. Warm baseline probes and this trial did not reproduce a naturally slow upstream reply. Simulation does not qualify hardware, sudden power loss or authenticated remote control. Saved qualification scores describe the stopped trial before handoff; the live journal subsequently contains administrative events. +Decision: adopt the repair and leave the product paused, connected, with both completed results and no pending work. Keep frozen trial evidence and handoff evidence distinct. +Refs: results/workbench-health-20261012/report.md; results/workbench-health-20261012/handoff.json; docs/experiments/SIM12-workbench-health.md; commit 37ebfdb diff --git a/jevdual/docs/notebook/2026-10-12-workbench-restarts.md b/jevdual/docs/notebook/2026-10-12-workbench-restarts.md new file mode 100644 index 0000000..726a760 --- /dev/null +++ b/jevdual/docs/notebook/2026-10-12-workbench-restarts.md @@ -0,0 +1,41 @@ +# Mid-job restart and explicit browser recovery + +## SIM11-P1 · prediction · 2026-10-12 13:56 +Hypothesis: a pending browser stage and single-send command journal survive independent workbench and body restarts without resending an uncertain action; historical body receipts can settle the same stage after its boot epoch changes. +Test: freeze a fresh owned process harness, interrupt two actual image-guided browser jobs at planning and uncertain-dispatch boundaries, recover through the UI and independently score native/SQLite/contact records after cleanup. +Predicts: all four stops/restarts preserve body/application identity and client send counts, planning interruption cancels and reaps its model process, uncertain commands remain blocked until exact receipt lookup, and explicit Resume uses a fresh TCP image before completing both jobs. +Against: a replay, lost pending command, silently resumed planning, orphan model process, body reset or independent verification failure. +Refs: docs/experiments/SIM11-workbench-restarts.md; tools/physical-workcell/qualify_workbench_restarts.py; tools/physical-workcell/score_workbench_restarts.py; results/workbench-restarts-20261012/ + +## SIM11-EPOCH · prediction · 2026-10-12 13:58 +Hypothesis: a changed body boot epoch can preserve running state when its outage is missed, because health and lease acquisition verify stable identity without invalidating the old decision. Binding epoch changes to Pause before command admission will cancel stale planning without disturbing historical receipt recovery. +Test: reproduce on real sockets by rebooting the same body while a model decision waits, avoiding an outage poll; release the old decision after the new epoch is read. Cover health-first and admission-read-first paths, then rerun with epoch invalidation. Separately retain uncertain historical dispatch recovery across reboot. +Predicts: the original fast-reboot path admits the old decision; the repaired path pauses with no successor command, requires explicit Resume and a fresh observation, and retains exact unknown command identity for read-only recovery. +Against: original code already pauses, repaired code sends the old decision, permanently blocks historical receipt lookup, or silently rotates authority while a command remains uncertain. +Refs: tools/physical-workcell/network_operator_runtime.py; tools/physical-workcell/test_network_operator.py; docs/experiments/SIM11-workbench-restarts.md + +## SIM11-U1 · update · 2026-10-12 14:03 +Observed: four new real-socket checks failed on the original runtime. Health-first reboot let the old waiting model decision advance generation to one; acquisition-first reboot also admitted that decision under boot two. Historical uncertain dispatch stayed single-send across body reboot, but its recovered job remained labelled unknown. Graceful workbench restart left an unfinished job labelled running while its host was paused. Before-fix output is retained. The original completed product roots and source snapshot were preserved before stopping its verified idle owned processes. +Inferred: boot identity alone does not invalidate a model decision, and runner recovery needs a consistent unfinished-job status. The original historical receipt mechanism remains useful. +Next: repair the runtime epoch boundaries and labels, rerun affected real-socket checks, then freeze the actual browser restart trial. +Refs: tools/physical-workcell/output/sim11-epoch-before-fix.log; tools/physical-workcell/output/sim11-preserved-baseline/preserved.json; tools/physical-workcell/test_network_operator.py + +## SIM11-U2 · update · 2026-10-12 14:09 +Resolves: SIM11-EPOCH = held +Observed: all nineteen runtime socket checks passed after epoch invalidation and job-status corrections. The old decision is withheld on health-first and lease-read-first reboot paths, exact unknown history stays recoverable once, and a later outage preserves the last verified epoch. Eighty core/adapter/link/HTTP regressions and four restart-harness control/ownership checks also passed. Ruff, JavaScript syntax and diff checks passed. The harness now records exact roots, ports, stopped processes, boot epochs and command digests; its scorer requires actual browser recovery evidence and final native/SQLite/contact proof. No actual-model restart trial has run yet. +Inferred: the epoch diagnosis is supported by failing and resolving socket evidence. The 103 mechanical checks establish readiness for the frozen browser trial, not its result. +Next: commit all measured source, then interrupt two actual image-guided jobs at the four preregistered process boundaries. +Refs: tools/physical-workcell/output/sim11-epoch-after-fix-runtime.log; tools/physical-workcell/output/sim11-core-network-regression.log; tools/physical-workcell/output/sim11-harness-tests-v1/unittest.log; tools/physical-workcell/qualify_workbench_restarts.py; tools/physical-workcell/score_workbench_restarts.py + +## SIM11-U3 · update · 2026-10-12 14:25 +Resolves: SIM11-P1 = held +Observed: all four graceful process restart boundaries and ten visible recovery states passed the independent scorer on the first actual-model trial at frozen d707210. Two jobs completed with native/SQLite/contact proof, continuous generations zero through twenty and 83 single-send command records. Workbench and body each stopped during planning and with a distinct uncertain dispatch. The five-command and 46-command recovery intervals added no sends. All seven trial processes exited before scoring. Three cancelled model calls were reaped and withheld: two planned interruptions and one additional health-timeout pause during reset. That extra pause required explicit Resume and a fresh observation; its cause remains unresolved. Reload preserved results without a command. Paused handoff retained identities, history and 83 commands at body boot four. An abbreviated-SHA launch was refused before process creation; its corrected full-SHA launch started the one actual-model attempt. An unmeasured helper fixture moved into owned output without changing measured source hashes. +Inferred: exact historical receipt recovery composes with graceful body/workbench restarts in this loopback product. Fast epoch invalidation is supported separately by socket regressions. The observed health timeout limits reliability claims even though guarded recovery worked. +Next: hand off the paused connected product and retained recovery evidence; leave sudden power loss and health-timeout diagnosis to separate work. +Refs: results/workbench-restarts-20261012/report.md; results/workbench-restarts-20261012/restart-score.json; results/workbench-restarts-20261012/qualification-score.json; results/workbench-restarts-20261012/health-timeout-observation.json; results/workbench-restarts-20261012/attempt-ledger.json; results/workbench-restarts-20261012/handoff.json + +## SIM11-S1 · summary · 2026-10-12 14:25 +Established: graceful workbench/body restart during actual planning and uncertain dispatch preserves one body, retained queue and command identity. Recovery reads exact historical receipts once, stays paused until Resume, and uses fresh TCP images. Two independently verified jobs and all four visible restart cases passed with measured source unchanged. Fast boot changes now invalidate old decisions before successor admission. +Uncertain: an additional health timeout paused reset and cancelled a model call; its cause is unresolved. Sudden power loss, forced kill during native persistence, physical hardware and authenticated remote access remain untested. +Decision: adopt the qualified restart behavior, retain the previous product and failing development evidence, and leave the current product connected and paused with both completed jobs visible. +Refs: docs/experiments/SIM11-workbench-restarts.md; results/workbench-restarts-20261012/report.md; tools/physical-workcell/NETWORK_WORKBENCH.md diff --git a/jevdual/docs/notebook/2026-10-14-consolidation-adoption.md b/jevdual/docs/notebook/2026-10-14-consolidation-adoption.md new file mode 100644 index 0000000..51f882f --- /dev/null +++ b/jevdual/docs/notebook/2026-10-14-consolidation-adoption.md @@ -0,0 +1,75 @@ +# Consolidation and unassisted local adoption + +## CA1 · prediction · 2026-10-14 12:51 +Hypothesis: latest main, capability qualification and independently developed camera/workbench branches can form one reviewed main without changing the immutable release artifact or losing private evidence. +Test: preserve exact branch refs and untracked draft context; merge origin/main into a new consolidation branch, retaining only the three documented later capability enhancements in squash-history conflicts; merge e17a49e experimental branch after user explicitly authorizes both. Review the complete netdiff, run full relevant deterministic/runtime-free suites and current hosted CI, remediate concrete findings, merge one PR. Keep VM stopped; qualify physical simulation as simulation only. Preserve ignored output and recoverable refs before linked checkout retirement; no gitclean/history deletion. +Predicts: merged tree includes both disjoint work streams, tests/review/CI pass, existing qualified8dc81c72 remains byte-identical, original experiment/capability records and drafts remain recoverable, and the sole active primary checkout ends clean onmain. +Against: useful data is lost, an unreviewed integration seam lands, preserved artifact identity changes, required checks fail, or unique work is silently dropped. +Refs: origin/main268f0fc; capability5dab496; camerae17a49e; userOctober14authorization includebothafterreview + +## CA2 · prediction · 2026-10-14 12:51 +Hypothesis: a fresh Codex session given only the exact8dc81c72 package and README can install and complete the bounded task without source coaching on this existing Mac. +Test: user changes separate-Mac requirement to thisMac, with8GiBmemoryheadroom and otherprojectactors idle. Record currentresources; stop only attestedownedidle Footwork actors ifneeded, preserve their state anddo not launchVM. Use one cleanper-run Codexcontext with opaqueexistingauth, package-only consumer instructions, freshownedinstall/job/fixture. Preserve allattempts; verify actualcalls/origins/exactoracle/durableclaims/automaticcleanup independently. Recordsetupfriction and repaironly demonstratedscopedissues in new versions withClaude review. Neverclaim anotherMac/coldOS qualification. +Predicts: actual Codex can follow README-only setup, identify permittedtargets through the publicsurface, completeoneexacteffect andvalidatedreport, and cleanup withminimalmanualsetup. +Against: it requires authorcoaching/manualPIDwindowURLdocumentinput, packageinstructionsfail, hostneedsungrantedaccess, duplicatesinput/misreportscompletion, or requiredmemoryheadroom is unavailable. +Refs: results/release-candidate-20261012; userOctober14thisMacauthorization; docs/installed-agent-host.md + +## CA3 · update · 2026-10-14 13:07 +Observed: full Python source suite passed 1582 tests; Node suite passed 97; camera synthetic suite passed 18. Initial desk/workbench tests failed because the new checkout's public CLI was not installed; after frozen uv sync, the same tests passed 30 tests and 11 subtests. The physical suite ran 304 tests with 38 failures and 15 errors: initial AgentBench setup lacked its output parent, and retained native startup logs show the owned Unix socket path exceeds the platform bound in this long managed checkout. +Inferred: the physical socket location is coupled to source checkout path length; the fail-closed bound protects the socket but prevents portable installation. The output-parent failure is a fresh-checkout test preparation defect. +Next: preregister a bounded socket-location portability repair, preserve original failed runs, review it and repeat the same suite. +Refs: output/consolidation-pytest.log; output/consolidation-node.log; output/consolidation-camera.log; output/consolidation-inbox-workbench.log; output/consolidation-inbox-workbench-rerun.log; output/consolidation-physical.log; tools/physical-workcell/output/a59ecfcf7/native-1.log + +## CA4 · prediction · 2026-10-14 13:07 +Hypothesis: deriving a short, per-user private Unix socket directory independently of checkout length, while retaining source/controller binding and owned-private socket validation, repairs fresh-checkout physical conformance without weakening refusal behavior. +Test: add meaningful long-path and adversarial ownership/symlink cases; create fresh test output parents explicitly; run the original 304-test physical suite in this long checkout and hosted Linux CI. Review changes with Claude before merge. +Predicts: long checkout paths work, hostile socket roots are refused, separate controller identities remain separate, restart/replay safety tests remain green, and all original physical tests pass. +Against: shortened paths retarget another controller, tolerate unsafe roots, weaken the existing safety bound, leave broken binding across restart, or original failures persist. +Refs: tools/physical-workcell/controller.py:socket_path; output/consolidation-physical.log + +## CA5 · prediction · 2026-10-14 13:10 +Hypothesis: frozen onboarding packet verification must derive its wheel inventory from the requested immutable commit rather than the current checkout, and the existing release verifier can preserve that constraint after camera runtime changes. +Test: reproduce reviewer inventory mismatch using the qualified wheel and source445a348; add a regression with current runtime differing from frozen source, then use committed-source wheel verification before generating packet inventory. Reverify the original205577packet and immutable8dc81c72candidate without rebuilding them. +Predicts: exact old wheel and packet verify against their frozen source; altered wheel bytes or a mismatched commit still refuse; current-source default remains correctly verified. +Against: verification accepts tampering, silently uses current source, introduces circular imports, or changes the preserved packet. +Refs: jevdual/scripts/build_onboarding_packet.py:inventory; jevdual/scripts/build_release_candidate.py:verify_committed_wheel; independent reviewer source445a348 inventory mismatch + +## CA6 · update · 2026-10-14 13:22 +Resolves: CA4 = held +Observed: the same full physical suite passes 314 tests after the private socket repair, including 10 new long-path and hostile-directory regressions. Controller restart/unknown-effect scenarios and AgentBench fresh output setup pass. Hosted Linux CI remains a separate portability check pending on PR3. The reference chrome-devtools-mcp daemon also uses a short per-user temporary runtime directory because of POSIX socket path limits. +Inferred: coupling the socket location to checkout length caused the startup failures; shortening only its private namespace repairs that constraint while retaining source/controller identity and safety refusals. +Next: complete current CI and independent review, remediate actionable findings, then merge PR3. +Refs: bce67ed; output/consolidation-physical.log; output/consolidation-physical-rerun.log; tools/physical-workcell/test_controller.py:SocketPathTests; chrome-devtools-mcp/src/daemon/utils.ts:getRuntimeHome + +## CA7 · update · 2026-10-14 13:22 +Resolves: CA5 = held +Observed: frozen old packet verification failed before the change and passes afterwards; 34 packet/release tests pass, including frozen-source divergence and tampering. Approved packet205577 and candidate8dc81c72 retain their original digests. A first new-test assertion mismatched only the existing error wording and is retained separately. +Inferred: immutable source reconstruction resolves the verification regression without changing either qualified artifact. +Next: merge after review/CI; preserve exact original artifacts for the adoption handoff. +Refs: 57ed816; output/consolidation-20261014/packet-before.txt; output/consolidation-20261014/packet-after.json; output/consolidation-20261014/release-after.json; output/consolidation-20261014/packet-fix-tests.txt; output/consolidation-20261014/packet-fix-tests-v2.txt + +## CA8 · prediction · 2026-10-14 13:25 +Hypothesis: current Linux CI errors arise from a macOS-only import in a synthetic test fixture, and the actionable static-review gaps can be repaired without changing the immutable qualified artifact or weakening fail-closed simulation behavior. +Test: replace only unavailable native test dependencies with explicit fakes while keeping Mac coverage real; correct order-handoff cleanup to the bound socket and freeze source bytes; score adoption by exact ZIP/wheel and manifest commit; snapshot archive bytes once, reject unsafe ZIP metadata and noncommit source objects; privatize measurement database creation and reject nonobject proposals; evaluate planner cleanup findings against declared retirement scope and add bounded regressions where supported. Run original affected suites, full Linux CI, notebook/lint and independent remediation review. Preserve first CI failure and the exact Opus review. +Predicts: all 54 unavailable-module errors disappear without dropping synthetic safety tests; cleanup observes the actual bound socket; dirty qualification is refused; verifier tampering, symlink archive and malformed source/proposal cases refuse; initial artifacts stay byte-identical. +Against: tests are skipped to hide a behavior failure, safety assertions are weakened, archive identity changes, cleanup still targets a stale path, or new regressions appear. +Refs: PR3; output/consolidation-ci-failure.log; output/consolidation-opus-review/response.json; review target57ed816 + +## CA9 · update · 2026-10-14 13:44 +Observed: full source Python rerun passes1594 with27skips/12browserdeselections; frozen0e5a59a physical rerun passes330. Archive hardening passes44 and immutable205577/8dc artifacts verify unchanged. Native fixture56 and controlledLinux-import55 tests pass; actual hostedLinux rerun is pending. Independent remediation scrutiny found qualifier location checks lacked produceridentity; two regressions failed before and pass after integer version/sourcekind/currentcontrollerhash checks. Final affected simulation conformance passes42. Original runs, test-preparation errors and development failures remain retained. +Inferred: fixes close the observed setup, provenance and bounded-input gaps while preserving existing fail-closed semantics. Observed detached planner survivors intentionally withhold closure; signalling retained identities would broaden the tested authority. The15secondhostgrace repairs supported delayed guardian publication without promising universal cleanup. +Next: push one consolidated remediation update, inspect current hostedCI and all review threads, retain qualification commits through a true merge, then consolidate primary checkout. A3 remains gated on user8GiBavailableRAM; no VM or adoption model task has started. +Refs: f34ce57; 0e97555; 9cf0c97; 0e5a59a; output/consolidation-pytest-final.log; output/consolidation-physical-final.log; output/consolidation-20261014/archive-hardening-tests-final-v2.txt; output/consolidation-20261014/qualifier-source-identity; output/release-publication-scan-reviewed.json + +## CA10 · prediction · 2026-10-14 13:50 +Hypothesis: Linux's remaining browser-readiness test failure comes from fake_clock replacing the shared process-wide time.monotonic/time.sleep, allowing unrelated background code to consume the finite tick iterator, rather than from product browser startup behavior. +Test: preserve hostedrun38609371553, add a regression that the fixture clock leaves process-wide time functions unchanged, then replace only the loaded fixture module's time binding with an explicit local clock. Run all fixture tests and current hostedCI without changing runtime/helpers/qualifiedZIP. +Predicts: originalclock-isolation regression fails thenpasses; readiness stillwaits40syntheticpolls andstrictdeadline/endpointchecks remainasserted; LinuxCIStopIteration disappears. +Against: global clockswerealreadyisolated, localclockstillfails, startupassertions mustbeweakened, orCIexposesproductbehaviorfailure. +Refs: output/consolidation-ci-failure-v2.log; tests/test_capability_fixture.py:fake_clock; hostedrun38609371553 + +## CA11 · update · 2026-10-14 13:54 +Observed: the new clock-isolation regression fails before andpassesafter replacing only the loadedfixturemodule's time binding. All existing timingpatches now affect thatlocal namespace. Native fixture57 and controlledLinux-import56 tests pass, withoneexistingmac-gate skip. Readiness stillasserts40polls andexactendpoint; deadline/refusal assertions areunchanged. Actual hostedLinux confirmation remains pending. +Inferred: sharedstdlib-clock mutation was an isolation defect and isremoved without changing product/helper/qualifiedartifact bytes; the nextCIrun must establish whether it caused the hostedStopIteration. +Next: push the boundedtest-only correction andinspect currentCI beforemerge. +Refs: output/consolidation-ci-failure-v2.log; tests/test_capability_fixture.py:test_fake_clock_preserves_process_wide_time_functions; worker clockbefore/after evidence inoutput/consolidation-20261014 diff --git a/jevdual/docs/onboarding-vm-plan.md b/jevdual/docs/onboarding-vm-plan.md new file mode 100644 index 0000000..a072ea6 --- /dev/null +++ b/jevdual/docs/onboarding-vm-plan.md @@ -0,0 +1,21 @@ +# Disposable macOS environment plan + +The current Mac is arm64, macOS 26.5.1/build 25F80, with 24 GiB installed memory. On 2026-10-10, its project volume had only 7.9 GiB available. Neither lume nor tart was on PATH. No hypervisor, restore image or guest was installed by this preparation. + +A spare independently provisioned Mac is the shortest qualification route. For a local guest, select an approved storage directory with adequate physical capacity before installation or image download. Lume's [quickstart](https://cua.ai/docs/lume/quickstart) calls for about 50 GB free disk and 8 GB free memory; installed memory is not evidence of free memory. Our initial proposed guest allocation is 4 CPUs, 8 GB RAM and 64 GB virtual disk, subject to current host availability. Do not place it in the reference repository corpus. + +Use a fresh Apple restore image with its selected version, URL and SHA256 recorded. Create an ungranted SIP-enabled guest; verify SIP inside it. The local Cua reference separates granted golden-image regressions from its SIP-on permission-flow lane. Its pregranted seed, permission seeding helpers and SIP-off base do not establish our first-use baseline. + +After approval of the exact installer, image and storage path, install a pinned Lume release through its documented installer. Inspect the installer and version before executing; its installation/configuration paths must be included in that approval. The CLI [reference](https://cua.ai/docs/lume/reference/cli/vms) supports a local IPSW and explicit storage: + +~~~sh +lume create footwork-u2-20261010 --ipsw /absolute/approved/restore.ipsw \ + --cpu 4 --memory 8GB --disk-size 64GB --storage /absolute/approved/vm-storage +lume run footwork-u2-20261010 --storage /absolute/approved/vm-storage +~~~ + +These are proposed commands, not executed or validated locally. Recheck the installed CLI's help before running. Complete Setup Assistant in the visible guest, then transfer only this qualification packet. Do not share the host home, repository, account credentials or signed-in browser profile. Keep normal SIP settings and observe initial permission status before granting anything. + +Use the guest's interactive Terminal for U2. SSH-only execution does not establish the same responsible GUI host. Guest user/permission setup and the exact storage choice remain pending. Export the qualification records before stopping or retiring the guest; deletion requires scoped approval and a fresh target check. + +References: local Cua libs/cua-driver/tests/runners/macos-lume/README.md, SIP-on permission-flow lane; local Arc README Accessibility-only setup; local Cua responsibility-chain guidance. Those reference versions supply mechanisms, not upgrades to Footwork's pinned driver. diff --git a/jevdual/docs/plans/workbench-notes-directions.md b/jevdual/docs/plans/workbench-notes-directions.md new file mode 100644 index 0000000..8ecc124 --- /dev/null +++ b/jevdual/docs/plans/workbench-notes-directions.md @@ -0,0 +1,50 @@ +# Project-linked workbench notes: useful directions and language boundaries + +The near-term goal is a physical observation that resolves a project question, changes a bounded work proposal, or supplies an acceptance check. Saving another transcript is insufficient evidence of utility. The first implementation binds a reviewed note to the user's selected Footwork project, its checkout revision and code references; it names a proposed next check without pretending that check ran. + +## The local record contract + +Keep separate fields for retained camera/approved-text evidence, caller interpretation, project identity/revision, proposed action/check, expected observation and actual outcome. A photo does not authorize an action; a saved packet does not establish its proposed physical effect. Keep local workbench artifacts private and checked through Footwork's exact-artifact command. An external task or project write needs an explicit resource binding and a real readback. + +The first record uses the previously verified live paper note. It asks whether project-bound workbench evidence can produce an actionable next experiment, proposes a separately preregistered paper-region/word-review comparison and records that the comparison has not run. This exercises association and handoff, not automated experimentation. + +## Grounded use cases + +| Direction | Existing project asset | Useful outcome beyond capture | First bounded experiment | +| --- | --- | --- | --- | +| Footwork physical experiment notebook | Camera receipt, OCR provenance, exact file readback, engineering predictions | A physical setup/observation changes one explicit hypothesis or next test; source evidence survives agent handoff | Bind one reviewed frame to project/code revision, hypothesis and acceptance check, then separately run that check | +| ExecuFunction/Siftable work intake | Project/task graph and Taster's evidence/coverage contracts | A sketch/blocker becomes a reviewed work proposal with owner, dependency and observable acceptance; avoid duplicate or misrouted work | Draft one project-bound proposal locally, review identity/acceptance, then exercise a future task adapter with real task readback | +| Family-history evidence intake | Missionaries in China / Kuliang Main research; current task Upload and classify family-side materials | A photographed original supports or contradicts a particular timeline/identity claim, with location/source/privacy labels and unresolved readings | One family letter/photo -> source record + cited claim + corroboration question; Chinese OCR/translation is a separate route from the current en-US helper | +| Best-Edit field evidence | Newsroom/Civic reporting, document verification and PDF parser provenance | A public notice or field note is linked to the article claim it supports and a verification checklist | Photograph one non-sensitive notice; retain original, reviewed quote and source/date; propose a claim-verification check before editorial use | +| OBS MCP bench/demonstration record | Watched takes, chapter markers, capture preflight and media readback | Align observed actions and test results to recording phases so a failed experiment can be reproduced and inspected | One disposable setup with deliberate before/after capture and an explicit expected result; recordings remain opt-in and are checked against actual file metadata | +| Instrument/assembly acceptance | Local OpenHTF measurements/phases/attachments and AprilTag/OpenPnP reference patterns | Compare actual readings/setup steps to a written test specification and mark missing or unresolved evidence | Manually bind an instrument/object ID, unit, operator/calibration context and reviewed reading to one criterion; add direct sensor data before relying on it for actuation | + +Live grounding on 2026-10-10: Codex app local project identities; Siftable project list and specific contexts. The family-history project currently has an open Upload and classify family-side materials task. Meta Wearables Integration is also an existing planning project describing hands-free/photo-to-note work and mobile-release prerequisites; it is a future capture surface, not proof of a deployed camera workflow. Duplicate ExecuFunction project names exist, so identifiers/namespaces must be explicit. Do not automatically map Siftable IDs from name similarity or Codex local project IDs. + +Source code ground: ExecuFunction packages/taster/src/engine/coverageReads.ts records which changed ranges were read at the reviewed revision; packages/taster/src/contract/verdict.ts withholds complete when stages/evidence/reconciliation are incomplete. Best-Edit README defines Newsroom/Civic and document-verification workflows. pdf-parser README defines native extraction, OCR routing and typed provenance. OBS_MCP README defines watched takes, recording preflight and media verification. These are source/context observations, not new live qualification of those products. + +[OpenHTF](https://github.com/google/openhtf) supplies a useful physical-test model: measurements with passing specifications plus free-form attachments. A workbench photo belongs beside a measurement and its criterion. [OpenCV marker pose estimation](https://docs.opencv.org/5.0/tutorials/objdetect/aruco_detection/aruco_detection.html) requires known marker size and camera calibration for metric pose. Detecting an object or tag in a picture alone does not establish voltage, force, depth, calibration or completed assembly. + +## What to prioritize + +1. Complete the Footwork record/handoff and readback route using one real reviewed source. +2. Improve paper-region/word review and measure correction effort against ordinary note entry; raw handwriting currently requires corrections. +3. Try an evidence-to-claim workflow with the family-history materials the user can actually access, or an OBS-linked before/after bench experiment. Select one question and one expected observation. +4. Add direct measurements and deterministic criteria before actuation. Keep model judgments advisory until their role is independently qualified. + +## Languages and measured hot routes + +Python standard library owns current workflow orchestration, schema validation, explicit project binding, local filesystem journals and CLI handoff. Swift calls native Apple Vision for OCR and camera-permission inspection; FFmpeg/ffprobe perform native media acquisition/decoding. Static review uses HTML/CSS. The new workbench package stays inside Footwork's tools directory. + +Footwork already has a Rust/PyO3 boundary in jevdual/crates/jevdual-core. The observed local backend uses native paint_order, element_hashes and evidence_match; snapshot_lookup currently falls back to Python. This is a read-only runtime observation, not evidence that a port would help the camera route. New Rust ports require a Python twin, fixture equality and before/after measurements under jevdual/docs/rust-boundary.md. + +Recorded route samples in jevdual/results/workbench-20261010/route-profile.json show approximately 1.3 ms to read/hash the 2.46 MB retained PNG, 0.18 ms for the direct exact-note check, 201 ms for a fresh public Footwork check process, 286 ms for public desk status and 452 ms for a Swift process plus real OCR on a three-line printed fixture with a warm module cache. Ranges and sample counts are retained. No sustained-video workload or Rust implementation was compared; human review was not timed. + +Current optimization candidates are process startup and reuse/compilation of native helpers, where warranted. A Rust rewrite of small-note JSON/hash plumbing is unsupported by these measurements; hashlib SHA256 already runs in the native _hashlib extension. [Apple's accurate OCR mode](https://developer.apple.com/documentation/vision/vnrecognizetextrequest/recognitionlevel) prioritizes recognition accuracy and does more computation than fast mode; language choice alone does not accelerate that native engine. + +Rust becomes a plausible boundary for sustained frame ingest, bounded latest-frame queues, high-rate image transforms/diffs, tracking, and sensor-stream fusion after those costs are measured. Prefer existing native implementations such as OpenCV/AprilTag for their algorithms rather than reimplementing them in Rust for naming consistency. On macOS, Swift is a direct route to AVFoundation/Vision. A future robot's low-level control loop needs its own hardware/runtime qualification; the conversational/project layer should supply bounded objectives rather than pretend to be a real-time controller. + +## User steering · 2026-10-10 15:26 +The user prioritizes physical applications mixed with computer actions and agents with bodies that can traverse environments. They report no robot hardware and chose simulation. Notes are supporting evidence, rather than the main product. Start a bounded MuJoCo workcell: read an explicit software work order, move a simulated part into an inspection zone, verify observed simulator state, then update the software order only when that predicate holds. Give an agent observe/act/status interfaces and generation-bound actions; do not claim simulated motion qualifies a real robot. + +Toward general bodies, use body-specific calibrated controllers behind an explicit action/observation contract. [LeRobot Robot](https://huggingface.co/docs/lerobot/main/api/robots) exposes connect/get_observation/send_action/disconnect; [MuJoCo](https://mujoco.readthedocs.io/en/3.15.0/programming/simulation.html) supplies native simulated dynamics; [ROS2 actions](https://github.com/ros2/ros2_documentation/blob/rolling/source/ROS-Framework/interfaces/actions/Working-with-actions/Understanding-ROS2-Actions/Understanding-ROS2-Actions.rst) separate goals, feedback, results and cancellation. These provide seams for a future adapter, not an already universal Footwork body. Physical movement needs its own coordinate frame, limits, controller, state readback and cancellation qualification. diff --git a/jevdual/docs/release-candidate.md b/jevdual/docs/release-candidate.md new file mode 100644 index 0000000..497de02 --- /dev/null +++ b/jevdual/docs/release-candidate.md @@ -0,0 +1,13 @@ +# Footwork capability candidate + +This MIT-licensed local candidate gives an agent five MCP tools for one scoped browser-to-TextEdit job: bind, observe, act, status and report. The Python wheel runs without Rust or a model provider account. A real agent host still needs its own working account. + +The current slice reads one owned loopback browser page and appends to one exact TextEdit document on macOS. It checks resource identity and preserves durable operation results across server restarts. Configure a new job with the actual browser/document identifiers; reuse its journal only to reconnect that same job. This candidate does not discover arbitrary apps or authorize a broader task. + +Start with [installation](capability-packaging.md), then [the agent-host recipe](installed-agent-host.md). The jevdual/scripts directory contains the qualified fixture and installation helpers. Use an existing compatible Python, uv and Chrome. Installation fetches binary dependencies from PyPI; the dependencies are not bundled. Run strict doctor from the responsible host before acting. Automated hosts should use the generated isolated Python commands. + +After the agent finishes, validate its structured claims with the emitted report adapter and publish only the returned trusted summary. Native completion is historical evidence; saved-file persistence requires a separate check. If execution is uncertain, collect the durable result instead of repeating input with a new token. + +`MANIFEST.json` binds every payload member to its SHA256. `qualification.json` identifies the exact wheel, helper source, measured host scope and supporting evidence. The evidence summaries distinguish the guest permission journey from the real agent task. The artifact is a local release candidate; it has not been published to a registry. + +Qualification covers only the exact hosts and environments recorded in those summaries. A previously provisioned macOS guest permission reset is not a never-granted cold OS. Other agent hosts, macOS versions, MDM, pixel capture, arbitrary apps and disk persistence require their own evidence. Dependencies retain their own licenses. diff --git a/jevdual/python/jevdual/artifacts.py b/jevdual/python/jevdual/artifacts.py new file mode 100644 index 0000000..3be7d17 --- /dev/null +++ b/jevdual/python/jevdual/artifacts.py @@ -0,0 +1,67 @@ +"""Read-only, digest-bound equality of external artifact bytes; no session provenance.""" + +from __future__ import annotations + +import hashlib +import os +import re +import stat +from pathlib import Path + +MAX_ARTIFACT_BYTES = 1024 * 1024 +_DIGEST = re.compile(r"[0-9a-f]{64}\Z") + + +class ArtifactError(Exception): + """A file cannot supply stable, bounded evidence.""" + + +def read_regular(path: Path, *, limit: int = MAX_ARTIFACT_BYTES) -> bytes: + """Read a regular file without following a final symlink; detect concurrent mutation.""" + try: + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK) + with os.fdopen(fd, "rb") as source: + before = os.fstat(source.fileno()) + if not stat.S_ISREG(before.st_mode): + raise ArtifactError("not a regular file") + if before.st_size > limit: + raise ArtifactError(f"file exceeds {limit} byte limit") + data = source.read(limit + 1) + after = os.fstat(source.fileno()) + if len(data) > limit: + raise ArtifactError(f"file exceeds {limit} byte limit") + if (before.st_size, before.st_mtime_ns, before.st_ctime_ns) != ( + after.st_size, after.st_mtime_ns, after.st_ctime_ns + ): + raise ArtifactError("file changed during read") + return data + except OSError as exc: + raise ArtifactError("file is missing, symlinked or unreadable") from exc + + +def check_artifact(path: Path, expected_file: Path, expected_sha256: str) -> dict: + """Compare exact current bytes to a digest-bound source; never print artifact text.""" + result = {"ok": False, "path": str(path), "expected_file": str(expected_file), + "expected_sha256": expected_sha256, + "claims": [], "limit_bytes": MAX_ARTIFACT_BYTES} + if not isinstance(expected_sha256, str) or not _DIGEST.fullmatch(expected_sha256): + return {**result, "detail": "expected SHA256 must contain 64 lowercase hexadecimal characters"} + try: + expected = read_regular(expected_file) + source_sha = hashlib.sha256(expected).hexdigest() + result["source_sha256"] = source_sha + result["expected_bytes"] = len(expected) + if source_sha != expected_sha256: + return {**result, "detail": "expected source no longer matches its bound digest"} + observed = read_regular(path) + result["observed_sha256"] = hashlib.sha256(observed).hexdigest() + result["observed_bytes"] = len(observed) + if observed != expected: + return {**result, "detail": "destination bytes differ from the bound expected source"} + # Recheck the source after destination read; mutable source cannot silently change approval. + if hashlib.sha256(read_regular(expected_file)).hexdigest() != expected_sha256: + return {**result, "detail": "expected source changed during verification"} + return {**result, "ok": True, "detail": "current bytes equal the digest-bound expected source", + "claims": ["current_artifact_byte_equality"]} + except ArtifactError as exc: + return {**result, "detail": str(exc)} diff --git a/jevdual/python/jevdual/cli.py b/jevdual/python/jevdual/cli.py index a509b4b..02837e5 100644 --- a/jevdual/python/jevdual/cli.py +++ b/jevdual/python/jevdual/cli.py @@ -57,6 +57,15 @@ async def cmd_apps(_: argparse.Namespace) -> int: return 0 +async def cmd_check_artifact(args: argparse.Namespace) -> int: + """External-artifact equality needs no Driver, model, session, or carried value.""" + from jevdual.artifacts import check_artifact + + result = check_artifact(Path(args.path), Path(args.expected_file), args.expected_sha256) + print(json.dumps(result, sort_keys=True)) + return 0 if result["ok"] else 6 + + async def cmd_menu(args: argparse.Namespace) -> int: from cua_driver import CuaDriver @@ -413,6 +422,11 @@ def common(sp: argparse.ArgumentParser) -> None: ) common(cf) cf.set_defaults(fn=S.cmd_check_file) + artifact = sub.add_parser("check-artifact", help="read-only exact bytes against a digest-bound expected file") + artifact.add_argument("path") + artifact.add_argument("--expected-file", required=True) + artifact.add_argument("--expected-sha256", required=True) + artifact.set_defaults(fn=cmd_check_artifact) gr = sub.add_parser("grant", help="allow one exact effect on one resource (the words `do` takes)") gr.add_argument("words", nargs="+") gr.add_argument("--uses", type=int, default=1) diff --git a/jevdual/results/agent-visual-bench-20261011/AI-action-audit.json b/jevdual/results/agent-visual-bench-20261011/AI-action-audit.json new file mode 100644 index 0000000..f4fd28d --- /dev/null +++ b/jevdual/results/agent-visual-bench-20261011/AI-action-audit.json @@ -0,0 +1,870 @@ +{ + "passed": true, + "source_commit": "9a5d2f8bfac8088b13c63052887e0c0c2e9d5c7e", + "guard_sources_unchanged": true, + "attempt": 1, + "successful_cycles": 2, + "AI_host": "Codex multimodal assistant in this conversation", + "AI_proposals": 29, + "image_presentations": 31, + "all_proposals_bind_presented_images": true, + "refused_stale_measurements": 1, + "unknown_outcomes": 0, + "disturbance": { + "before": { + "role": "independent_native_state_scorer", + "generation": 12, + "state_sha256": "c5eb98e5389520f67d0a5696115aa8d7d7a2ae63ded866d4f74b1716a02128fc", + "native_time_s": 31.415999999993467, + "block_xyz": [ + 0.07578820807466202, + -2.3639510932334726e-09, + 0.03499573045536971 + ], + "pusher_xyz": [ + -0.299999969238987, + 3.5114932976316857e-36, + 0.06 + ], + "block_center_in_zone": true, + "pusher_home": true, + "external_force_cleared": true + }, + "after": { + "role": "independent_native_state_scorer", + "generation": 13, + "state_sha256": "1816ee306f82952178fe88ac2857976ccd1594d917628fad36a438f79183280b", + "native_time_s": 32.57599999999385, + "block_xyz": [ + -0.0572428260016602, + -2.363914289828072e-09, + 0.034995730455369856 + ], + "pusher_xyz": [ + -0.2999999999999935, + 7.426712930285049e-43, + 0.06 + ], + "block_center_in_zone": false, + "pusher_home": true, + "external_force_cleared": true + }, + "displacement_x_m": -0.13303103407632222 + }, + "actions": [ + { + "sequence": 5, + "job_id": "ai-ordinary", + "generation": 0, + "proposal": { + "action": "move", + "challenge_id": "challenge-e4339df93bb44cf9827aaa89ca5e2d0c", + "intent": "Push the visible orange block right toward the center of the green measurement region.", + "observation_sha256": "16db335110b8ec1dcb8abbd79eff6e896a9e2b202021915e1d08db2f199011f5", + "operation_id": "agent-aeb2273d6d6a4f539555071890b542f7", + "proposal_id": "proposal-5c78049d2f0644859cfc4f961d0e408e", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": 0.015, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-aeb2273d6d6a4f539555071890b542f7", + "kind": "move", + "generation": 1, + "native_time_s": 4.0999999999997705, + "native_steps": 1550, + "pusher_block_contact_samples": 3164, + "max_axis_actuator_force_n": 1.2780714513550784, + "state_sha256": "3dd31d583a8b7ec3f4a7fa51f403baa50fda8f9839a705a1787505c87464e425" + }, + "challenge_image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + { + "sequence": 9, + "job_id": "ai-ordinary", + "generation": 1, + "proposal": { + "action": "move", + "challenge_id": "challenge-0a7f171f17d0440aba49e0b394ca6981", + "intent": "The block is centered in the measurement region; retract the pusher left, away from the block, to clear the image.", + "observation_sha256": "440ac82d3e2e12b6ee77d2bb487c4b6b714c248917bdd8b336f6ce38c1bf30e4", + "operation_id": "agent-201690e6fe504ef39f50055e592eed1f", + "proposal_id": "proposal-6e348696878b4aef835229d6577cb5d5", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.3, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-201690e6fe504ef39f50055e592eed1f", + "kind": "move", + "generation": 2, + "native_time_s": 7.199999999999429, + "native_steps": 1550, + "pusher_block_contact_samples": 64, + "max_axis_actuator_force_n": 0.5633864611553108, + "state_sha256": "17c6620f9eeb3b3c3a6e787215e843c7a8bfa66eb3c56f8693c3477b2eb5d9c9" + }, + "challenge_image_sha256": "cb4221e600bbb24aec549f49924fc9224876a1ad8d04159fe0cc3798658d8357" + }, + { + "sequence": 13, + "job_id": "ai-ordinary", + "generation": 2, + "proposal": { + "action": "measure", + "challenge_id": "challenge-b946ac2ff9d74070b5f3efbed673b58d", + "intent": "The orange block center remains in the green region and the pusher is clear; run the computer measurement.", + "observation_sha256": "b302e99abe05b60bed706ecf0d96f6bd29e514a9f410145162091369562d8743", + "operation_id": "agent-62ebb86d89f941daab3a059aa53fd695", + "proposal_id": "proposal-632b712fdcd646679f954f6f4abb6e37", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": null, + "target_y": null + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-62ebb86d89f941daab3a059aa53fd695", + "kind": "measure", + "generation": 2, + "state_sha256": "17c6620f9eeb3b3c3a6e787215e843c7a8bfa66eb3c56f8693c3477b2eb5d9c9" + }, + "challenge_image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159" + }, + { + "sequence": 17, + "job_id": "ai-ordinary", + "generation": 2, + "proposal": { + "action": "begin_reset", + "challenge_id": "challenge-37730e6485b945b4a30e5ae544e5a91d", + "intent": "The verified measurement is saved; begin returning the block and pusher to their home positions.", + "observation_sha256": "90e4f830c218c32a5a548d2afacebb237f03e76a55d9303e71611a662299eb33", + "operation_id": "agent-ae2a5100d3fc4fbcb76023e5ced0f9a0", + "proposal_id": "proposal-08212ee8faa04bbdaecadb14da534852", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": null, + "target_y": null + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "generation": 2, + "native_time_s": 7.199999999999429 + }, + "challenge_image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159" + }, + { + "sequence": 21, + "job_id": "ai-ordinary", + "generation": 2, + "proposal": { + "action": "move", + "challenge_id": "challenge-fe85036078c24be39d7e21b9ad462286", + "intent": "Move the pusher into the upper bypass lane so it can reach the right side of the block without crossing it.", + "observation_sha256": "fe39e48543c6acc01d0c6a1517a460da78e39efff54d923177790c352ee6c2a5", + "operation_id": "agent-da74795104e8433d84ca03c55a5a9f3e", + "proposal_id": "proposal-0b967a31699f4f67aa758ee98001a948", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.3, + "target_y": 0.135 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-da74795104e8433d84ca03c55a5a9f3e", + "kind": "move", + "generation": 3, + "native_time_s": 9.101999999999709, + "native_steps": 951, + "pusher_block_contact_samples": 0, + "max_axis_actuator_force_n": 0.38448407128570206, + "state_sha256": "213816189f73ad2af9c338b8c204e0b238e4e11d6893995b1fead34a8a63d491" + }, + "challenge_image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159" + }, + { + "sequence": 25, + "job_id": "ai-ordinary", + "generation": 3, + "proposal": { + "action": "move", + "challenge_id": "challenge-859f365ee5294dac81a68cf58fb8d5be", + "intent": "The pusher is visibly above the block; cross the clear lane to a position just beyond its right face.", + "observation_sha256": "4e35f1762c23b98fce7181f23c806c6c8de3d4738a23170c60d70174797abb9a", + "operation_id": "agent-375e8bb74d4f4dc7b1b3dfabad8d83b1", + "proposal_id": "proposal-e1daead845b74cd68a6e2ebd57f81e17", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": 0.14, + "target_y": 0.135 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-375e8bb74d4f4dc7b1b3dfabad8d83b1", + "kind": "move", + "generation": 4, + "native_time_s": 13.036000000001023, + "native_steps": 1967, + "pusher_block_contact_samples": 0, + "max_axis_actuator_force_n": 0.38525092330498, + "state_sha256": "b36c073315154f2c4412cf7923dec065ec428566b7e7dccda023c35c90b3f1a8" + }, + "challenge_image_sha256": "09026909d3489122b3f84704a9072495ec2288b7ccd0e2d1f6029521293335ea" + }, + { + "sequence": 29, + "job_id": "ai-ordinary", + "generation": 4, + "proposal": { + "action": "move", + "challenge_id": "challenge-0e224d3ea5de42699bc1abc4e84d87b5", + "intent": "The pusher is to the right of the orange block with clearance; lower it into the block's horizontal pushing lane.", + "observation_sha256": "fd5522f5296936afe5adfed7d4ae8f736194be2af57d0f5e8fcd5cc7d5d77034", + "operation_id": "agent-e891c9fd8986410a806f80ba59238880", + "proposal_id": "proposal-e8a48a05e9624798a66b82795c401f84", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": 0.14, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-e891c9fd8986410a806f80ba59238880", + "kind": "move", + "generation": 5, + "native_time_s": 14.938000000001658, + "native_steps": 951, + "pusher_block_contact_samples": 0, + "max_axis_actuator_force_n": 0.38448407128571205, + "state_sha256": "451ed25d727f1acf2e8d38507093aa9de9aa8c055c51ffe0832c2fa72f225858" + }, + "challenge_image_sha256": "c62187cd908d71592bdd7193b83ec80666e6f350e90968204d2d87a7169d12dc" + }, + { + "sequence": 33, + "job_id": "ai-ordinary", + "generation": 5, + "proposal": { + "action": "move", + "challenge_id": "challenge-98c4443f685c43bca0555096bbe64d57", + "intent": "Push left from the block's right side, bringing its center back into the home region near x=-0.12.", + "observation_sha256": "a09ead8b0c746828364df389adba823a8de3851c166deebff772d7f06562003a", + "operation_id": "agent-eecc4e58e7ae47df8088761be7f5ea25", + "proposal_id": "proposal-92ef08cad3d549d4a63649d345b5daa1", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.065, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-eecc4e58e7ae47df8088761be7f5ea25", + "kind": "move", + "generation": 6, + "native_time_s": 17.306000000001287, + "native_steps": 1184, + "pusher_block_contact_samples": 3108, + "max_axis_actuator_force_n": 1.346867858248153, + "state_sha256": "a1b40541b3203c3066b0020b486ff538bdf4fee5604232eb0a2ab425ce7383d7" + }, + "challenge_image_sha256": "e6f35d68a39eccb69ef08ffb81de2aed231bbe097a530bc31bb48ffda662a3f1" + }, + { + "sequence": 37, + "job_id": "ai-ordinary", + "generation": 6, + "proposal": { + "action": "move", + "challenge_id": "challenge-3abc7266237042b68e06877018787ccd", + "intent": "The block is back at home; back the pusher slightly right to release contact before going around it.", + "observation_sha256": "27add33dac2fb680e42be390ee8c161959090d554e00e341b075af6b7dc828ed", + "operation_id": "agent-3c86afdd8a704441a66a717fabb9ad30", + "proposal_id": "proposal-b85d03dc400444c082fdecbab78e9a37", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.035, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-3c86afdd8a704441a66a717fabb9ad30", + "kind": "move", + "generation": 7, + "native_time_s": 18.50800000000062, + "native_steps": 601, + "pusher_block_contact_samples": 66, + "max_axis_actuator_force_n": 0.5646407287582988, + "state_sha256": "1302ea1009ef1d9d21b30ced5086ad9dc29b48de393d967df67e3ee5b7ac1a81" + }, + "challenge_image_sha256": "6c4785ad11fbf959867ca24381e9caf3a051c777b86c8e5357697aa2cdea5f67" + }, + { + "sequence": 41, + "job_id": "ai-ordinary", + "generation": 7, + "proposal": { + "action": "move", + "challenge_id": "challenge-d5633b65146f4065bb710dda3ee037dd", + "intent": "There is now a visible gap beside the home block; lift the pusher into the bypass lane without dragging it.", + "observation_sha256": "e21f0e55f8037de4d27502219301f2b0083a00a91c692b99ba9141591d2e1eda", + "operation_id": "agent-1f592c90c3b74e08bc1620fe7067c84c", + "proposal_id": "proposal-f07bff4deb78444898958ab4dc5734dc", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.035, + "target_y": 0.135 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-1f592c90c3b74e08bc1620fe7067c84c", + "kind": "move", + "generation": 8, + "native_time_s": 20.409999999999567, + "native_steps": 951, + "pusher_block_contact_samples": 0, + "max_axis_actuator_force_n": 0.38448407128570206, + "state_sha256": "3624577cb56ac84d63ed54a8a7d8d3dbfbb3cec0060bc2605f9af177790e155a" + }, + "challenge_image_sha256": "6a03bda6c21d45a6c0de319fa273262335ebcf36de34e1f4a41bee1604479d49" + }, + { + "sequence": 45, + "job_id": "ai-ordinary", + "generation": 8, + "proposal": { + "action": "move", + "challenge_id": "challenge-ccab1d2dc0e345ee9071c7fbdcb72f95", + "intent": "The pusher is above the block; traverse left through the clear bypass lane toward its home column.", + "observation_sha256": "432fd403dc293c58ebba6f62f8b83375a6912f1ebca74e9f1255e146123a203c", + "operation_id": "agent-94497e62b78c470588dde1d8788ed008", + "proposal_id": "proposal-7ac22a5b098b44aa83322a3b25eeff64", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.3, + "target_y": 0.135 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-94497e62b78c470588dde1d8788ed008", + "kind": "move", + "generation": 9, + "native_time_s": 23.177999999998033, + "native_steps": 1384, + "pusher_block_contact_samples": 0, + "max_axis_actuator_force_n": 0.38504787816081887, + "state_sha256": "0c6cbcb3b882d727eff2c704d68a051975a1e937c3679af12f3191993d40a051" + }, + "challenge_image_sha256": "fa76012ace8a01cde6bb10e4742798a428ccbd9a868bf7bc97a9447e0cfc866e" + }, + { + "sequence": 49, + "job_id": "ai-ordinary", + "generation": 9, + "proposal": { + "action": "move", + "challenge_id": "challenge-7851634f3566401f954ebae1f6e29e88", + "intent": "The pusher has cleared the block on the left; descend to its home position.", + "observation_sha256": "18828d79e20f454ceb7e1c0692b20736f6e8ce090292c6eaa4b838e9fbe957d6", + "operation_id": "agent-e088c59467614556aaea8da9b9c8d32d", + "proposal_id": "proposal-026bb7d6f7f340ed960566c81bac830c", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.3, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-e088c59467614556aaea8da9b9c8d32d", + "kind": "move", + "generation": 10, + "native_time_s": 25.07999999999698, + "native_steps": 951, + "pusher_block_contact_samples": 0, + "max_axis_actuator_force_n": 0.38448407128571205, + "state_sha256": "aafe7bd44fe197a436c786478e803f5600e9356a85b524d8f8ba32237c0f8efc" + }, + "challenge_image_sha256": "cda2378ead3e165186825aa560540c8495d01f07acff067406513a16ee92db65" + }, + { + "sequence": 53, + "job_id": "ai-ordinary", + "generation": 10, + "proposal": { + "action": "finish_reset", + "challenge_id": "challenge-93a8a4f678e947648e4fa8062ce4203b", + "intent": "The image shows the orange block in its home region and the blue pusher at home; request independent reset verification.", + "observation_sha256": "56b763e47095f854b68d2788490b3bb1aabd55b94628db7d191ba264e9125797", + "operation_id": "agent-c0a81c03a9114aa2b68b127755699f4c", + "proposal_id": "proposal-1d2f0b6f0dde4fd4af7b38314b3ee4ff", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": null, + "target_y": null + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-c0a81c03a9114aa2b68b127755699f4c", + "kind": "finish_reset", + "generation": 10, + "native_time_s": 25.07999999999698, + "state_sha256": "aafe7bd44fe197a436c786478e803f5600e9356a85b524d8f8ba32237c0f8efc", + "block_home_verified": true, + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8940 + }, + "challenge_image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87" + }, + { + "sequence": 58, + "job_id": "ai-disturbed", + "generation": 10, + "proposal": { + "action": "move", + "challenge_id": "challenge-4af4aec17ab440c48a0852b7041b60d0", + "intent": "Push the home block into the second job's green region, which is slightly farther right than the first target.", + "observation_sha256": "52fd733ecb953954823b1aed211fdab04665dd55423b11e3cb525d4b57f97264", + "operation_id": "agent-60980668f692471f8a01870d0547380a", + "proposal_id": "proposal-ddd3e71640aa402bbb40883d692c4f4f", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": 0.025, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-60980668f692471f8a01870d0547380a", + "kind": "move", + "generation": 11, + "native_time_s": 28.247999999995223, + "native_steps": 1584, + "pusher_block_contact_samples": 3178, + "max_axis_actuator_force_n": 1.3153962165348574, + "state_sha256": "3e55b7e0bf2ef8517aaf3066b75ee6a6433a92aa15dbb4f8f5d788beab29f821" + }, + "challenge_image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + { + "sequence": 62, + "job_id": "ai-disturbed", + "generation": 11, + "proposal": { + "action": "move", + "challenge_id": "challenge-1ce07c2c0e8b4af0897468a2d7aabfb0", + "intent": "The block appears centered in the new green region; pull the pusher left away from contact before measuring.", + "observation_sha256": "c385b080a7448784f2e6bbb13ab9a21a5ddbf8ac0c7bc2ff7d54a6fd361efd3b", + "operation_id": "agent-184b6f968ae04876917143e5a717fb7a", + "proposal_id": "proposal-4756082b5edc4f24b076334707a98d64", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.3, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-184b6f968ae04876917143e5a717fb7a", + "kind": "move", + "generation": 12, + "native_time_s": 31.415999999993467, + "native_steps": 1584, + "pusher_block_contact_samples": 66, + "max_axis_actuator_force_n": 0.569265513047462, + "state_sha256": "c5eb98e5389520f67d0a5696115aa8d7d7a2ae63ded866d4f74b1716a02128fc" + }, + "challenge_image_sha256": "0d42c93fef97b6bf1ded19965b65518e43b873916c9e8bc1d8300debf130f9fa" + }, + { + "sequence": 66, + "job_id": "ai-disturbed", + "generation": 12, + "proposal": { + "action": "measure", + "challenge_id": "challenge-c2d34501db714daa970aa0cfa132270a", + "intent": "The latest image shows the block prepared in the second measurement region with the pusher clear; measure it.", + "observation_sha256": "2a65106d4fae92eaf4041ed249ef41fb2c5c35004002fe3189977d8511f53d91", + "operation_id": "agent-1651b0bc149b40c5bf581c666d4f99e1", + "proposal_id": "proposal-28b240a0c5e944c8b2ab0e793835d374", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": null, + "target_y": null + }, + "outcome": "refused", + "reason": "observation is stale or foreign; no requested effect dispatched", + "receipt_summary": {}, + "challenge_image_sha256": "126a049946a82dda474c3ec72f2da13b50e2a5e5d1686cc4c408251f75e06d9d" + }, + { + "sequence": 70, + "job_id": "ai-disturbed", + "generation": 13, + "proposal": { + "action": "move", + "challenge_id": "challenge-0a4a355152664f8690b4e6cc5c4d03c4", + "intent": "The fresh image shows the block displaced left of the green region while the pusher remains left; push it right back toward the target center.", + "observation_sha256": "ad507cb4b1ecd5fac93a34b3680eb581d0eafbefea96e16d18ee59c4e916afdc", + "operation_id": "agent-069bca35f676423a99b8ce98a5fc1061", + "proposal_id": "proposal-b1d32f971dcc4472a9382e1c4303c4a2", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": 0.028, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-069bca35f676423a99b8ce98a5fc1061", + "kind": "move", + "generation": 14, + "native_time_s": 35.76399999999774, + "native_steps": 1594, + "pusher_block_contact_samples": 2794, + "max_axis_actuator_force_n": 1.371068465268717, + "state_sha256": "fec6ff3846f7112235a07ec03a974ba3468ba9852bc9e7ebf63f572dda064cf4" + }, + "challenge_image_sha256": "ba1d19598b817acd57a746150d30d4755b48a80287fd362d3a24ce6a0e244d45" + }, + { + "sequence": 74, + "job_id": "ai-disturbed", + "generation": 14, + "proposal": { + "action": "move", + "challenge_id": "challenge-48c16468357b44d4a3565bf9e8511e1e", + "intent": "The corrective push has brought the block back to the green region; clear the pusher left before a fresh measurement.", + "observation_sha256": "59569198225d2fcadd937aebee144237c640e0be9f328b072e7bea1fa7aee7ce", + "operation_id": "agent-47235fb2ba1e483db4fd3985073c7f12", + "proposal_id": "proposal-d31079ad8f9b41df9d9693fed7b1d10c", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.3, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-47235fb2ba1e483db4fd3985073c7f12", + "kind": "move", + "generation": 15, + "native_time_s": 38.95200000000164, + "native_steps": 1594, + "pusher_block_contact_samples": 66, + "max_axis_actuator_force_n": 0.5679920156839486, + "state_sha256": "4dd44356d2ac0116aed5dbff1f314ddf300da024850c26ad61191a1672b895c9" + }, + "challenge_image_sha256": "6fc50ac16b71d7a5f11e88c85c3fa2ef13df8374291e764e5e3c7213a38f8c25" + }, + { + "sequence": 78, + "job_id": "ai-disturbed", + "generation": 15, + "proposal": { + "action": "measure", + "challenge_id": "challenge-5a89495500094ccda58a991a9a27f494", + "intent": "The fresh image verifies recovered placement with the pusher clear; measure this current state rather than retrying the stale proposal.", + "observation_sha256": "d5e448fbe007cc42f9bf85384f5b77046b5b366b38f9016c64375dc832b5d10e", + "operation_id": "agent-5dd59ada282f46c6aa679674b91c2983", + "proposal_id": "proposal-653340c1067b4593b341d0a5c17f5fe3", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": null, + "target_y": null + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-5dd59ada282f46c6aa679674b91c2983", + "kind": "measure", + "generation": 15, + "state_sha256": "4dd44356d2ac0116aed5dbff1f314ddf300da024850c26ad61191a1672b895c9" + }, + "challenge_image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f" + }, + { + "sequence": 82, + "job_id": "ai-disturbed", + "generation": 15, + "proposal": { + "action": "begin_reset", + "challenge_id": "challenge-e8114cfa482c4499ada30283ad6d51aa", + "intent": "The recovered measurement has been saved and verified; start the second job's contact reset.", + "observation_sha256": "96291545eb9577454635f8003039c389aa429bc02d088dd420ee27f0da2f9139", + "operation_id": "agent-cd76880c390b4870804c1752de7787ee", + "proposal_id": "proposal-f730ddc0014d4c8cbc2ab377fc58d592", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": null, + "target_y": null + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "generation": 15, + "native_time_s": 38.95200000000164 + }, + "challenge_image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f" + }, + { + "sequence": 86, + "job_id": "ai-disturbed", + "generation": 15, + "proposal": { + "action": "move", + "challenge_id": "challenge-a5477a6495ee49c991b30693bdd152ab", + "intent": "Lift the home pusher into the clear upper lane to approach the recovered block from the right.", + "observation_sha256": "dffc9cb0085484f265fd45c35b1d5641912e2f85ee453cd5294d9e52dd017b44", + "operation_id": "agent-b2b139c73743438a88f7d8fdd6b69ae6", + "proposal_id": "proposal-bbfb051bac274a30a9883640b7163e88", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.3, + "target_y": 0.135 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-b2b139c73743438a88f7d8fdd6b69ae6", + "kind": "move", + "generation": 16, + "native_time_s": 40.854000000003964, + "native_steps": 951, + "pusher_block_contact_samples": 0, + "max_axis_actuator_force_n": 0.38448407128570206, + "state_sha256": "96ea71954529f457c436bd3374a38c98f1c8354db6ced335c39ae71a6d815a9a" + }, + "challenge_image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f" + }, + { + "sequence": 90, + "job_id": "ai-disturbed", + "generation": 16, + "proposal": { + "action": "move", + "challenge_id": "challenge-8132b582443e4392b135133800f6b3c9", + "intent": "The pusher is clear above the block; cross right far enough to leave a gap beside the second target's block.", + "observation_sha256": "78923d47db657e6c3f2d4793eddd26247e254bb64c3f71414cabeee413f6cc49", + "operation_id": "agent-31f5d3447bdc4c959f8fcebcda292837", + "proposal_id": "proposal-26c0c918565744a78dd5e62adca22618", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": 0.155, + "target_y": 0.135 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-31f5d3447bdc4c959f8fcebcda292837", + "kind": "move", + "generation": 17, + "native_time_s": 44.888000000008894, + "native_steps": 2017, + "pusher_block_contact_samples": 0, + "max_axis_actuator_force_n": 0.38525380916615404, + "state_sha256": "73f71645ea2ac92f6ee314c4208739ee09cde971373bf3afc8c0d2ca5456179a" + }, + "challenge_image_sha256": "9c6a5951995c9196fd2cb636f7cf6d44888a91202dc61bcf74b3097a0923830c" + }, + { + "sequence": 94, + "job_id": "ai-disturbed", + "generation": 17, + "proposal": { + "action": "move", + "challenge_id": "challenge-98023517d0234bca96066852a972469d", + "intent": "The blue pusher is beyond the orange block's right face; descend into the pushing lane while keeping that clearance.", + "observation_sha256": "19b732785404802032a73291f1b61efeb7b05e112feb65dc04ae2e71be48de0d", + "operation_id": "agent-ade0665e21b74c3a888bc087b70df0e7", + "proposal_id": "proposal-599e463aa13545c9961af251ce562581", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": 0.155, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-ade0665e21b74c3a888bc087b70df0e7", + "kind": "move", + "generation": 18, + "native_time_s": 46.79000000001122, + "native_steps": 951, + "pusher_block_contact_samples": 0, + "max_axis_actuator_force_n": 0.38448407128571205, + "state_sha256": "66f43add681d9afd93b778fee054d903b0be84d874a7c944b296f6a8fb14d418" + }, + "challenge_image_sha256": "184c403f4a7eeccbd82ba7247128604e475e92229f2db1d6c96595d7fbdd0941" + }, + { + "sequence": 98, + "job_id": "ai-disturbed", + "generation": 18, + "proposal": { + "action": "move", + "challenge_id": "challenge-4d9640297ce040beac29488d78a43013", + "intent": "Push the recovered block left from its right side until its center returns to the home region.", + "observation_sha256": "2edf60f18ae1b4be9e3536086c02d267ec09c2a972c3dd8da23fd679d05b1f08", + "operation_id": "agent-e75a593f49e44507bb78388cf36a687e", + "proposal_id": "proposal-b955e6b92c10430eb6bc1c05d15feeaf", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.065, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-e75a593f49e44507bb78388cf36a687e", + "kind": "move", + "generation": 19, + "native_time_s": 49.258000000014235, + "native_steps": 1234, + "pusher_block_contact_samples": 3140, + "max_axis_actuator_force_n": 1.2698356367073558, + "state_sha256": "a0f0ec383589971e1a03613b651bb9b030fb16d15642875f5554b664be229fb4" + }, + "challenge_image_sha256": "3ef275d26144f92492c90735f8bebdd5cfdcc989da6423ae83715a010873d5f8" + }, + { + "sequence": 102, + "job_id": "ai-disturbed", + "generation": 19, + "proposal": { + "action": "move", + "challenge_id": "challenge-20c6400e862f450684f04802b6e8cb11", + "intent": "The block is at home again; release the pusher's right-side contact before moving it around the block.", + "observation_sha256": "1ba35b89bcd2551e61231066feb5d688806a5be3916fe599ff08dd847518e5a5", + "operation_id": "agent-d2ea0866d79243d998331c5f7ba4b0e5", + "proposal_id": "proposal-8e37df41a1bf47c4bbbbbcf29266c18b", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.035, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-d2ea0866d79243d998331c5f7ba4b0e5", + "kind": "move", + "generation": 20, + "native_time_s": 50.460000000015704, + "native_steps": 601, + "pusher_block_contact_samples": 66, + "max_axis_actuator_force_n": 0.5646353795216896, + "state_sha256": "44f23a1582a444feccb9c870e249463f06b097700bac4b5a471964a8fdefc14f" + }, + "challenge_image_sha256": "bf70dba8e16f7cc3324ef2e5e39d2d5408b83df9f0c03f2f446a57659cb8255a" + }, + { + "sequence": 106, + "job_id": "ai-disturbed", + "generation": 20, + "proposal": { + "action": "move", + "challenge_id": "challenge-6ecc3179fac541d48f6c295b638a8a16", + "intent": "The gap beside the home block is clear; lift the pusher into the bypass lane for its return.", + "observation_sha256": "11c5e1c85f31671ec78c8f20be556a3940fad0a838ca924c53b9ffd8ed8cf8b4", + "operation_id": "agent-445f007c1d7e4533b5ca3e26c25b3f4f", + "proposal_id": "proposal-58c8f40bc83b47eb975aeeda1762123e", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.035, + "target_y": 0.135 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-445f007c1d7e4533b5ca3e26c25b3f4f", + "kind": "move", + "generation": 21, + "native_time_s": 52.36200000001803, + "native_steps": 951, + "pusher_block_contact_samples": 0, + "max_axis_actuator_force_n": 0.38448407128570206, + "state_sha256": "fa265d62672cca23c53812efa31387d66592aa20bef95347a97743dbfd86db2e" + }, + "challenge_image_sha256": "6f0d26dda31720cc658216254a63289a4e7804cb2339d2a7489e0365be6f3114" + }, + { + "sequence": 110, + "job_id": "ai-disturbed", + "generation": 21, + "proposal": { + "action": "move", + "challenge_id": "challenge-e7b2a397392a416c9bedd0dd02496dd1", + "intent": "Cross left above the block, keeping vertical clearance until the pusher reaches its home column.", + "observation_sha256": "ead667059eb30587f4e4644db3b4395669edd7ddb304978bac7eaae418eda53e", + "operation_id": "agent-4fa1490cdd1048ecb09a04719ecbb068", + "proposal_id": "proposal-c786d2479b654bb39beefb874fbd61be", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.3, + "target_y": 0.135 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-4fa1490cdd1048ecb09a04719ecbb068", + "kind": "move", + "generation": 22, + "native_time_s": 55.13000000002141, + "native_steps": 1384, + "pusher_block_contact_samples": 0, + "max_axis_actuator_force_n": 0.38504787816081887, + "state_sha256": "793337af114683113d0dbbb19022f7b01d00569444efb89c7f11f1b091b69c92" + }, + "challenge_image_sha256": "1505aeadca7409c872c4e3edf9a58508ad3c89e3f5911644449d5f542117d248" + }, + { + "sequence": 114, + "job_id": "ai-disturbed", + "generation": 22, + "proposal": { + "action": "move", + "challenge_id": "challenge-b0dca65c9e7a434a8dad5ea5fd803784", + "intent": "The pusher is safely left of the block; return it downward to its home position.", + "observation_sha256": "6190a3ace769eb3de80e4fdc76f524591d9072ea8284c233454631c21efcdf53", + "operation_id": "agent-6e0ac39e73c046f3b6270133fbff055c", + "proposal_id": "proposal-d555c91721ed4ed2992bb92aaf808a53", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": -0.3, + "target_y": 0.0 + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-6e0ac39e73c046f3b6270133fbff055c", + "kind": "move", + "generation": 23, + "native_time_s": 57.032000000023736, + "native_steps": 951, + "pusher_block_contact_samples": 0, + "max_axis_actuator_force_n": 0.38448407128571205, + "state_sha256": "6eecc4d33c156d2bc623608fecc63b5c4282a95fd37c531bf87ec5571d073f30" + }, + "challenge_image_sha256": "94f2de9164d14475774f68d3f5860562d35d3bd09ff9f26349f1d6a4008db583" + }, + { + "sequence": 118, + "job_id": "ai-disturbed", + "generation": 23, + "proposal": { + "action": "finish_reset", + "challenge_id": "challenge-9292c66d94af4c4b93b74f186a0262c3", + "intent": "Both visible bodies have returned home after recovery and measurement; verify and complete the second reset.", + "observation_sha256": "877139c7001ac604742bfdbc012509a0d1489d468fdebad0d2a39a8ad04dd793", + "operation_id": "agent-1fc92aa65ba3418eb400ecf69497e103", + "proposal_id": "proposal-dbeab904c5274b8ab4d351a2800ca15f", + "session_id": "e663f891cbb84bb5ba355213708165af", + "target_x": null, + "target_y": null + }, + "outcome": "executed", + "reason": null, + "receipt_summary": { + "operation_id": "agent-1fc92aa65ba3418eb400ecf69497e103", + "kind": "finish_reset", + "generation": 23, + "native_time_s": 57.032000000023736, + "state_sha256": "6eecc4d33c156d2bc623608fecc63b5c4282a95fd37c531bf87ec5571d073f30", + "block_home_verified": true, + "pusher_home_verified": true, + "reset_contact_samples": 3206, + "reset_native_steps": 9040 + }, + "challenge_image_sha256": "a834aeb17495159a215c0d6a51becde2e50afd38f47316aaa1a8c414d4af0bd4" + } + ] +} diff --git a/jevdual/results/agent-visual-bench-20261011/AI-observations.gif b/jevdual/results/agent-visual-bench-20261011/AI-observations.gif new file mode 100644 index 0000000..c40c70f Binary files /dev/null and b/jevdual/results/agent-visual-bench-20261011/AI-observations.gif differ diff --git a/jevdual/results/agent-visual-bench-20261011/after-disturbance.png b/jevdual/results/agent-visual-bench-20261011/after-disturbance.png new file mode 100644 index 0000000..eab19df Binary files /dev/null and b/jevdual/results/agent-visual-bench-20261011/after-disturbance.png differ diff --git a/jevdual/results/agent-visual-bench-20261011/before-disturbance.png b/jevdual/results/agent-visual-bench-20261011/before-disturbance.png new file mode 100644 index 0000000..a911ebc Binary files /dev/null and b/jevdual/results/agent-visual-bench-20261011/before-disturbance.png differ diff --git a/jevdual/results/agent-visual-bench-20261011/checks.json b/jevdual/results/agent-visual-bench-20261011/checks.json new file mode 100644 index 0000000..eeeee0c --- /dev/null +++ b/jevdual/results/agent-visual-bench-20261011/checks.json @@ -0,0 +1,58 @@ +{ + "source_commit": "9a5d2f8bfac8088b13c63052887e0c0c2e9d5c7e", + "suites": [ + { + "path": "tools/physical-workcell/output/a7-suite-b8f88806f5/suite.log", + "sha256": "3741e670a91f357e7b81797caa1edba642e5a7b70578158de73cb429e148b7cb", + "count": 15, + "elapsed_s": 2.749, + "passed": true, + "tests": [ + "test_challenge_has_only_images_task_and_public_observation", + "test_changed_challenge_image_refuses", + "test_changed_proposal_refuses_before_dispatch", + "test_error_after_real_move_stays_unknown_even_if_bench_ready", + "test_foreign_challenge_and_unsafe_identity_refuse", + "test_measurement_placement_refusal_does_not_create_sqlite", + "test_measurement_worker_failure_retains_unknown_and_blocks_replay", + "test_move_executes_once_duplicate_returns_exact_result", + "test_nonfinite_and_conflicting_arguments_never_create_proposal", + "test_proposal_persists_intent_without_effect", + "test_public_cli_measurement_reset_and_status_strip_detector_locations", + "test_source_change_and_foreign_bench_identity_block_dispatch", + "test_stale_move_refuses_and_returns_fresh_image", + "test_unresolved_intent_blocks_same_and_new_proposal", + "test_wrong_reset_phase_preserves_bench" + ] + }, + { + "path": "tools/physical-workcell/output/sim7-final-checks/bench.log", + "sha256": "55d4c5578d868e07b47b1274c41d54a180c4fa3a8cddc6bde5af8fd29b81a8a3", + "count": 14, + "elapsed_s": 3.888, + "passed": true, + "tests": [ + "test_corrupted_image_refuses_measurement_and_does_not_create_a_database", + "test_corrupted_native_state_never_causes_an_implicit_reset", + "test_corrupted_retained_model_refuses_without_recreating_the_body", + "test_corrupted_source_binding_refuses_without_a_new_generation", + "test_diagnostic_force_needs_explicit_enablement_and_finite_bounds", + "test_exact_motion_replay_retains_one_native_effect_and_conflicts_refuse", + "test_external_force_invalidates_stale_motion_and_measurement_before_dispatch", + "test_foreign_or_modified_observation_cannot_authorize_a_motion", + "test_measurement_column_corruption_is_not_hidden_by_an_unchanged_receipt", + "test_measurement_is_a_separate_process_with_exact_persisted_image_binding", + "test_measurement_receipt_corruption_refuses_historical_replay", + "test_measurement_replay_is_historical_and_does_not_run_another_worker", + "test_retained_interrupted_intent_blocks_all_new_dispatch", + "test_successive_job_gate_requires_measurement_and_verified_reset" + ] + } + ], + "total": 29, + "ruff": "passed", + "native_guards_unchanged": true, + "notebook": "passed", + "diff_check": "passed", + "redaction_files_changed": 0 +} diff --git a/jevdual/results/agent-visual-bench-20261011/cleanup.json b/jevdual/results/agent-visual-bench-20261011/cleanup.json new file mode 100644 index 0000000..cdfbb76 --- /dev/null +++ b/jevdual/results/agent-visual-bench-20261011/cleanup.json @@ -0,0 +1,19 @@ +{ + "background_controller_started": false, + "all_cli_and_measurement_processes_synchronous_and_waited": true, + "measurement_workers": [ + { + "job_id": "ai-ordinary", + "worker_pid": 84953, + "exit_code": 0 + }, + { + "job_id": "ai-disturbed", + "worker_pid": 87908, + "exit_code": 0 + } + ], + "final_bench_idle": true, + "unknown_outcomes": 0, + "evidence_retained": true +} diff --git a/jevdual/results/agent-visual-bench-20261011/frames.json b/jevdual/results/agent-visual-bench-20261011/frames.json new file mode 100644 index 0000000..4be59c7 --- /dev/null +++ b/jevdual/results/agent-visual-bench-20261011/frames.json @@ -0,0 +1,281 @@ +[ + { + "challenge_id": "challenge-e4339df93bb44cf9827aaa89ca5e2d0c", + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "generation": 0, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000000-0fcba1683fc2ca06.png" + }, + { + "challenge_id": "challenge-0a7f171f17d0440aba49e0b394ca6981", + "image_sha256": "cb4221e600bbb24aec549f49924fc9224876a1ad8d04159fe0cc3798658d8357", + "generation": 1, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000001-bad2078e152acd7a.png" + }, + { + "challenge_id": "challenge-b946ac2ff9d74070b5f3efbed673b58d", + "image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159", + "generation": 2, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000002-332dd2be629a31e3.png" + }, + { + "challenge_id": "challenge-37730e6485b945b4a30e5ae544e5a91d", + "image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159", + "generation": 2, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "measured", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000002-d77f175856925cf4.png" + }, + { + "challenge_id": "challenge-fe85036078c24be39d7e21b9ad462286", + "image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159", + "generation": 2, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000002-0d491dd460366578.png" + }, + { + "challenge_id": "challenge-859f365ee5294dac81a68cf58fb8d5be", + "image_sha256": "09026909d3489122b3f84704a9072495ec2288b7ccd0e2d1f6029521293335ea", + "generation": 3, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000003-d2dcace5d1bb6a76.png" + }, + { + "challenge_id": "challenge-0e224d3ea5de42699bc1abc4e84d87b5", + "image_sha256": "c62187cd908d71592bdd7193b83ec80666e6f350e90968204d2d87a7169d12dc", + "generation": 4, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000004-5ed6501f87fe26b3.png" + }, + { + "challenge_id": "challenge-98c4443f685c43bca0555096bbe64d57", + "image_sha256": "e6f35d68a39eccb69ef08ffb81de2aed231bbe097a530bc31bb48ffda662a3f1", + "generation": 5, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000005-b554100267ea777c.png" + }, + { + "challenge_id": "challenge-3abc7266237042b68e06877018787ccd", + "image_sha256": "6c4785ad11fbf959867ca24381e9caf3a051c777b86c8e5357697aa2cdea5f67", + "generation": 6, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000006-3a09bfeb46845bbd.png" + }, + { + "challenge_id": "challenge-d5633b65146f4065bb710dda3ee037dd", + "image_sha256": "6a03bda6c21d45a6c0de319fa273262335ebcf36de34e1f4a41bee1604479d49", + "generation": 7, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000007-2709c62d9ef39660.png" + }, + { + "challenge_id": "challenge-ccab1d2dc0e345ee9071c7fbdcb72f95", + "image_sha256": "fa76012ace8a01cde6bb10e4742798a428ccbd9a868bf7bc97a9447e0cfc866e", + "generation": 8, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000008-61b275312f5615cc.png" + }, + { + "challenge_id": "challenge-7851634f3566401f954ebae1f6e29e88", + "image_sha256": "cda2378ead3e165186825aa560540c8495d01f07acff067406513a16ee92db65", + "generation": 9, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000009-9b921e5067762a87.png" + }, + { + "challenge_id": "challenge-93a8a4f678e947648e4fa8062ce4203b", + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "generation": 10, + "job_id": "ai-ordinary", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000010-ea9e6cc3a19e8a18.png" + }, + { + "challenge_id": "challenge-34408876d0a34de0a98301cb7f2ef02a", + "image_sha256": "c71216bf1c74a28b5e7475bf9af5afe4a22d62ee1aeb31c3ecf8dc62dbb591a8", + "generation": 10, + "job_id": null, + "tool": "view_image", + "phase": "idle", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000010-38e0b9de817f645c.png" + }, + { + "challenge_id": "challenge-4af4aec17ab440c48a0852b7041b60d0", + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "generation": 10, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000010-5295830fbdea7b61.png" + }, + { + "challenge_id": "challenge-1ce07c2c0e8b4af0897468a2d7aabfb0", + "image_sha256": "0d42c93fef97b6bf1ded19965b65518e43b873916c9e8bc1d8300debf130f9fa", + "generation": 11, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000011-48260e325fc6e5c5.png" + }, + { + "challenge_id": "challenge-c2d34501db714daa970aa0cfa132270a", + "image_sha256": "126a049946a82dda474c3ec72f2da13b50e2a5e5d1686cc4c408251f75e06d9d", + "generation": 12, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000012-a53794cdf5f55ea3.png" + }, + { + "challenge_id": "challenge-0a4a355152664f8690b4e6cc5c4d03c4", + "image_sha256": "ba1d19598b817acd57a746150d30d4755b48a80287fd362d3a24ce6a0e244d45", + "generation": 13, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000013-a53794cdf5f55ea3.png" + }, + { + "challenge_id": "challenge-48c16468357b44d4a3565bf9e8511e1e", + "image_sha256": "6fc50ac16b71d7a5f11e88c85c3fa2ef13df8374291e764e5e3c7213a38f8c25", + "generation": 14, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000014-c9d65316327fa7eb.png" + }, + { + "challenge_id": "challenge-5a89495500094ccda58a991a9a27f494", + "image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f", + "generation": 15, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000015-eb4e1af60ee1ae89.png" + }, + { + "challenge_id": "challenge-e8114cfa482c4499ada30283ad6d51aa", + "image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f", + "generation": 15, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "measured", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000015-bb47387aad541841.png" + }, + { + "challenge_id": "challenge-a5477a6495ee49c991b30693bdd152ab", + "image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f", + "generation": 15, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000015-56e663affd1e3522.png" + }, + { + "challenge_id": "challenge-8132b582443e4392b135133800f6b3c9", + "image_sha256": "9c6a5951995c9196fd2cb636f7cf6d44888a91202dc61bcf74b3097a0923830c", + "generation": 16, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000016-34800b2a5f2c8244.png" + }, + { + "challenge_id": "challenge-98023517d0234bca96066852a972469d", + "image_sha256": "184c403f4a7eeccbd82ba7247128604e475e92229f2db1d6c96595d7fbdd0941", + "generation": 17, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000017-83ca05ae26421883.png" + }, + { + "challenge_id": "challenge-4d9640297ce040beac29488d78a43013", + "image_sha256": "3ef275d26144f92492c90735f8bebdd5cfdcc989da6423ae83715a010873d5f8", + "generation": 18, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000018-4d3178b6b72ca439.png" + }, + { + "challenge_id": "challenge-20c6400e862f450684f04802b6e8cb11", + "image_sha256": "bf70dba8e16f7cc3324ef2e5e39d2d5408b83df9f0c03f2f446a57659cb8255a", + "generation": 19, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000019-bd8d7b56f8897648.png" + }, + { + "challenge_id": "challenge-6ecc3179fac541d48f6c295b638a8a16", + "image_sha256": "6f0d26dda31720cc658216254a63289a4e7804cb2339d2a7489e0365be6f3114", + "generation": 20, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000020-125013457cb8308a.png" + }, + { + "challenge_id": "challenge-e7b2a397392a416c9bedd0dd02496dd1", + "image_sha256": "1505aeadca7409c872c4e3edf9a58508ad3c89e3f5911644449d5f542117d248", + "generation": 21, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000021-899159eed0d2cf4a.png" + }, + { + "challenge_id": "challenge-b0dca65c9e7a434a8dad5ea5fd803784", + "image_sha256": "94f2de9164d14475774f68d3f5860562d35d3bd09ff9f26349f1d6a4008db583", + "generation": 22, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000022-a1d8394031a01bc5.png" + }, + { + "challenge_id": "challenge-9292c66d94af4c4b93b74f186a0262c3", + "image_sha256": "a834aeb17495159a215c0d6a51becde2e50afd38f47316aaa1a8c414d4af0bd4", + "generation": 23, + "job_id": "ai-disturbed", + "tool": "view_image", + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000023-5b88ee5f2a264bb0.png" + }, + { + "challenge_id": "challenge-f8ee1d62647e4bb3aa8b348d9b7f68ec", + "image_sha256": "9f67472a3681ba01ab6c8ee8ce7ea2a34bd816ba9c67c1ab0f36a01cc12ce597", + "generation": 23, + "job_id": null, + "tool": "view_image", + "phase": "idle", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim7-ai-20261011-v1/bench/images/000023-38e0b9de817f645c.png" + } +] diff --git a/jevdual/results/agent-visual-bench-20261011/fresh-agent-status.json b/jevdual/results/agent-visual-bench-20261011/fresh-agent-status.json new file mode 100644 index 0000000..982b607 --- /dev/null +++ b/jevdual/results/agent-visual-bench-20261011/fresh-agent-status.json @@ -0,0 +1 @@ +{"bench": {"active_job": null, "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", "diagnostic_enabled": true, "generation": 23, "jobs": {"ai-disturbed": {"completed_generation": 23, "completed_native_time_s": 57.032000000023736, "job_id": "ai-disturbed", "measurement_operation_id": "agent-5dd59ada282f46c6aa679674b91c2983", "measurement_receipt": {"bench_id": "3e025aca6ea240f29003b7c6fdc91a39", "generation": 15, "image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f", "job_id": "ai-disturbed", "measurement": {"footprint_area_m2": 0.0049, "method": "simulated_orange_RGB_footprint", "orange_pixel_count": 1764}, "native_time_s": 38.95200000000164, "operation_id": "agent-5dd59ada282f46c6aa679674b91c2983", "source_kind": "mujoco_simulation", "state_sha256": "4dd44356d2ac0116aed5dbff1f314ddf300da024850c26ad61191a1672b895c9", "version": 1, "worker_pid": 87908}, "moves": 12, "phase": "completed", "reset_receipt": {"bench_id": "3e025aca6ea240f29003b7c6fdc91a39", "block_home_verified": true, "generation": 23, "image_sha256": "a834aeb17495159a215c0d6a51becde2e50afd38f47316aaa1a8c414d4af0bd4", "job_id": "ai-disturbed", "kind": "finish_reset", "native_time_s": 57.032000000023736, "operation_id": "agent-1fc92aa65ba3418eb400ecf69497e103", "pusher_home_verified": true, "reset_contact_samples": 3206, "reset_native_steps": 9040, "state_sha256": "6eecc4d33c156d2bc623608fecc63b5c4282a95fd37c531bf87ec5571d073f30"}, "reset_started_generation": 15, "started_generation": 10, "started_native_time_s": 25.07999999999698, "target_zone": {"x_max": 0.115, "x_min": 0.045, "y_max": 0.025, "y_min": -0.025}}, "ai-ordinary": {"completed_generation": 10, "completed_native_time_s": 25.07999999999698, "job_id": "ai-ordinary", "measurement_operation_id": "agent-62ebb86d89f941daab3a059aa53fd695", "measurement_receipt": {"bench_id": "3e025aca6ea240f29003b7c6fdc91a39", "generation": 2, "image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159", "job_id": "ai-ordinary", "measurement": {"footprint_area_m2": 0.004794444444444444, "method": "simulated_orange_RGB_footprint", "orange_pixel_count": 1726}, "native_time_s": 7.199999999999429, "operation_id": "agent-62ebb86d89f941daab3a059aa53fd695", "source_kind": "mujoco_simulation", "state_sha256": "17c6620f9eeb3b3c3a6e787215e843c7a8bfa66eb3c56f8693c3477b2eb5d9c9", "version": 1, "worker_pid": 84953}, "moves": 10, "phase": "completed", "reset_receipt": {"bench_id": "3e025aca6ea240f29003b7c6fdc91a39", "block_home_verified": true, "generation": 10, "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", "job_id": "ai-ordinary", "kind": "finish_reset", "native_time_s": 25.07999999999698, "operation_id": "agent-c0a81c03a9114aa2b68b127755699f4c", "pusher_home_verified": true, "reset_contact_samples": 3174, "reset_native_steps": 8940, "state_sha256": "aafe7bd44fe197a436c786478e803f5600e9356a85b524d8f8ba32237c0f8efc"}, "reset_started_generation": 2, "started_generation": 0, "started_native_time_s": 1.0000000000000007, "target_zone": {"x_max": 0.105, "x_min": 0.035, "y_max": 0.025, "y_min": -0.025}}}, "native_time_s": 57.032000000023736, "source_fingerprints": {"bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", "mujoco_version": "3.15.0", "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", "state_spec": 16383, "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961"}, "source_kind": "mujoco_simulation", "state": "ready", "unresolved_effect": null, "version": 1}, "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", "host_role": "external_multimodal_AI_host", "sequence": 118, "session_id": "e663f891cbb84bb5ba355213708165af", "source_fingerprints": {"agent_bench_sha256": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", "mujoco_version": "3.15.0", "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", "state_spec": 16383, "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961"}, "unresolved_proposals": []} diff --git a/jevdual/results/agent-visual-bench-20261011/harness-disturbance.json b/jevdual/results/agent-visual-bench-20261011/harness-disturbance.json new file mode 100644 index 0000000..734f62f --- /dev/null +++ b/jevdual/results/agent-visual-bench-20261011/harness-disturbance.json @@ -0,0 +1,16 @@ +{ + "prepared_proposal_id": "proposal-28b240a0c5e944c8b2ab0e793835d374", + "prior_challenge_id": "challenge-c2d34501db714daa970aa0cfa132270a", + "intervention": { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "force_x_n": -1.5, + "forced_steps": 80, + "generation": 13, + "job_id": "ai-disturbed", + "kind": "diagnostic_external_force", + "native_steps": 580, + "native_time_s": 32.57599999999385, + "operation_id": "sim7-harness-force", + "state_sha256": "1816ee306f82952178fe88ac2857976ccd1594d917628fad36a438f79183280b" + } +} diff --git a/jevdual/results/agent-visual-bench-20261011/image-presentations.json b/jevdual/results/agent-visual-bench-20261011/image-presentations.json new file mode 100644 index 0000000..4419661 --- /dev/null +++ b/jevdual/results/agent-visual-bench-20261011/image-presentations.json @@ -0,0 +1,223 @@ +{ + "host": "Codex multimodal assistant in this conversation", + "scope": "images emitted via view_image to the active AI host; not cryptographic model attestation", + "views": [ + { + "challenge_id": "challenge-e4339df93bb44cf9827aaa89ca5e2d0c", + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "generation": 0, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-0a7f171f17d0440aba49e0b394ca6981", + "image_sha256": "cb4221e600bbb24aec549f49924fc9224876a1ad8d04159fe0cc3798658d8357", + "generation": 1, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-b946ac2ff9d74070b5f3efbed673b58d", + "image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159", + "generation": 2, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-37730e6485b945b4a30e5ae544e5a91d", + "image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159", + "generation": 2, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-fe85036078c24be39d7e21b9ad462286", + "image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159", + "generation": 2, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-859f365ee5294dac81a68cf58fb8d5be", + "image_sha256": "09026909d3489122b3f84704a9072495ec2288b7ccd0e2d1f6029521293335ea", + "generation": 3, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-0e224d3ea5de42699bc1abc4e84d87b5", + "image_sha256": "c62187cd908d71592bdd7193b83ec80666e6f350e90968204d2d87a7169d12dc", + "generation": 4, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-98c4443f685c43bca0555096bbe64d57", + "image_sha256": "e6f35d68a39eccb69ef08ffb81de2aed231bbe097a530bc31bb48ffda662a3f1", + "generation": 5, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-3abc7266237042b68e06877018787ccd", + "image_sha256": "6c4785ad11fbf959867ca24381e9caf3a051c777b86c8e5357697aa2cdea5f67", + "generation": 6, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-d5633b65146f4065bb710dda3ee037dd", + "image_sha256": "6a03bda6c21d45a6c0de319fa273262335ebcf36de34e1f4a41bee1604479d49", + "generation": 7, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-ccab1d2dc0e345ee9071c7fbdcb72f95", + "image_sha256": "fa76012ace8a01cde6bb10e4742798a428ccbd9a868bf7bc97a9447e0cfc866e", + "generation": 8, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-7851634f3566401f954ebae1f6e29e88", + "image_sha256": "cda2378ead3e165186825aa560540c8495d01f07acff067406513a16ee92db65", + "generation": 9, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-93a8a4f678e947648e4fa8062ce4203b", + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "generation": 10, + "job_id": "ai-ordinary", + "tool": "view_image" + }, + { + "challenge_id": "challenge-34408876d0a34de0a98301cb7f2ef02a", + "image_sha256": "c71216bf1c74a28b5e7475bf9af5afe4a22d62ee1aeb31c3ecf8dc62dbb591a8", + "generation": 10, + "job_id": null, + "tool": "view_image" + }, + { + "challenge_id": "challenge-4af4aec17ab440c48a0852b7041b60d0", + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "generation": 10, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-1ce07c2c0e8b4af0897468a2d7aabfb0", + "image_sha256": "0d42c93fef97b6bf1ded19965b65518e43b873916c9e8bc1d8300debf130f9fa", + "generation": 11, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-c2d34501db714daa970aa0cfa132270a", + "image_sha256": "126a049946a82dda474c3ec72f2da13b50e2a5e5d1686cc4c408251f75e06d9d", + "generation": 12, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-0a4a355152664f8690b4e6cc5c4d03c4", + "image_sha256": "ba1d19598b817acd57a746150d30d4755b48a80287fd362d3a24ce6a0e244d45", + "generation": 13, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-48c16468357b44d4a3565bf9e8511e1e", + "image_sha256": "6fc50ac16b71d7a5f11e88c85c3fa2ef13df8374291e764e5e3c7213a38f8c25", + "generation": 14, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-5a89495500094ccda58a991a9a27f494", + "image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f", + "generation": 15, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-e8114cfa482c4499ada30283ad6d51aa", + "image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f", + "generation": 15, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-a5477a6495ee49c991b30693bdd152ab", + "image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f", + "generation": 15, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-8132b582443e4392b135133800f6b3c9", + "image_sha256": "9c6a5951995c9196fd2cb636f7cf6d44888a91202dc61bcf74b3097a0923830c", + "generation": 16, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-98023517d0234bca96066852a972469d", + "image_sha256": "184c403f4a7eeccbd82ba7247128604e475e92229f2db1d6c96595d7fbdd0941", + "generation": 17, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-4d9640297ce040beac29488d78a43013", + "image_sha256": "3ef275d26144f92492c90735f8bebdd5cfdcc989da6423ae83715a010873d5f8", + "generation": 18, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-20c6400e862f450684f04802b6e8cb11", + "image_sha256": "bf70dba8e16f7cc3324ef2e5e39d2d5408b83df9f0c03f2f446a57659cb8255a", + "generation": 19, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-6ecc3179fac541d48f6c295b638a8a16", + "image_sha256": "6f0d26dda31720cc658216254a63289a4e7804cb2339d2a7489e0365be6f3114", + "generation": 20, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-e7b2a397392a416c9bedd0dd02496dd1", + "image_sha256": "1505aeadca7409c872c4e3edf9a58508ad3c89e3f5911644449d5f542117d248", + "generation": 21, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-b0dca65c9e7a434a8dad5ea5fd803784", + "image_sha256": "94f2de9164d14475774f68d3f5860562d35d3bd09ff9f26349f1d6a4008db583", + "generation": 22, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-9292c66d94af4c4b93b74f186a0262c3", + "image_sha256": "a834aeb17495159a215c0d6a51becde2e50afd38f47316aaa1a8c414d4af0bd4", + "generation": 23, + "job_id": "ai-disturbed", + "tool": "view_image" + }, + { + "challenge_id": "challenge-f8ee1d62647e4bb3aa8b348d9b7f68ec", + "image_sha256": "9f67472a3681ba01ab6c8ee8ce7ea2a34bd816ba9c67c1ab0f36a01cc12ce597", + "generation": 23, + "job_id": null, + "tool": "view_image" + } + ] +} diff --git a/jevdual/results/agent-visual-bench-20261011/native-sqlite-score.json b/jevdual/results/agent-visual-bench-20261011/native-sqlite-score.json new file mode 100644 index 0000000..5a8f18f --- /dev/null +++ b/jevdual/results/agent-visual-bench-20261011/native-sqlite-score.json @@ -0,0 +1,422 @@ +{ + "attempt": 1, + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "completed_cycles": 2, + "current_status": { + "active_job": null, + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "diagnostic_enabled": true, + "generation": 23, + "jobs": { + "ai-disturbed": { + "completed_generation": 23, + "completed_native_time_s": 57.032000000023736, + "job_id": "ai-disturbed", + "measurement_operation_id": "agent-5dd59ada282f46c6aa679674b91c2983", + "measurement_receipt": { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "generation": 15, + "image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f", + "job_id": "ai-disturbed", + "measurement": { + "block_box": [ + 346, + 159, + 388, + 201 + ], + "block_center_xy": [ + 0.07833333333333332, + 0.0 + ], + "centroid_pixel_xy": [ + 366.5, + 179.5 + ], + "footprint_area_m2": 0.0049, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1764 + }, + "native_time_s": 38.95200000000164, + "operation_id": "agent-5dd59ada282f46c6aa679674b91c2983", + "source_kind": "mujoco_simulation", + "state_sha256": "4dd44356d2ac0116aed5dbff1f314ddf300da024850c26ad61191a1672b895c9", + "version": 1, + "worker_pid": 87908 + }, + "moves": 12, + "phase": "completed", + "reset_receipt": { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "block_home_verified": true, + "generation": 23, + "image_sha256": "a834aeb17495159a215c0d6a51becde2e50afd38f47316aaa1a8c414d4af0bd4", + "job_id": "ai-disturbed", + "kind": "finish_reset", + "native_time_s": 57.032000000023736, + "operation_id": "agent-1fc92aa65ba3418eb400ecf69497e103", + "pusher_home_verified": true, + "reset_contact_samples": 3206, + "reset_native_steps": 9040, + "state_sha256": "6eecc4d33c156d2bc623608fecc63b5c4282a95fd37c531bf87ec5571d073f30" + }, + "reset_started_generation": 15, + "started_generation": 10, + "started_native_time_s": 25.07999999999698, + "target_zone": { + "x_max": 0.115, + "x_min": 0.045, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "ai-ordinary": { + "completed_generation": 10, + "completed_native_time_s": 25.07999999999698, + "job_id": "ai-ordinary", + "measurement_operation_id": "agent-62ebb86d89f941daab3a059aa53fd695", + "measurement_receipt": { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "generation": 2, + "image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159", + "job_id": "ai-ordinary", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 7.199999999999429, + "operation_id": "agent-62ebb86d89f941daab3a059aa53fd695", + "source_kind": "mujoco_simulation", + "state_sha256": "17c6620f9eeb3b3c3a6e787215e843c7a8bfa66eb3c56f8693c3477b2eb5d9c9", + "version": 1, + "worker_pid": 84953 + }, + "moves": 10, + "phase": "completed", + "reset_receipt": { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "ai-ordinary", + "kind": "finish_reset", + "native_time_s": 25.07999999999698, + "operation_id": "agent-c0a81c03a9114aa2b68b127755699f4c", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8940, + "state_sha256": "aafe7bd44fe197a436c786478e803f5600e9356a85b524d8f8ba32237c0f8efc" + }, + "reset_started_generation": 2, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 57.032000000023736, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "passed": true, + "scope": "native/SQLite post-trial scoring; AI image consumption/proposals are separately recorded in the session", + "scores": [ + { + "job_id": "ai-ordinary", + "measurement": { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "generation": 2, + "image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159", + "job_id": "ai-ordinary", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 7.199999999999429, + "operation_id": "agent-62ebb86d89f941daab3a059aa53fd695", + "source_kind": "mujoco_simulation", + "state_sha256": "17c6620f9eeb3b3c3a6e787215e843c7a8bfa66eb3c56f8693c3477b2eb5d9c9", + "version": 1, + "worker_pid": 84953 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06582866319694268, + -8.41323830075274e-11, + 0.034995730455369696 + ], + "external_force_cleared": true, + "generation": 2, + "native_time_s": 7.199999999999429, + "pusher_home": true, + "pusher_xyz": [ + -0.2999999692200571, + 2.026020754412724e-35, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "17c6620f9eeb3b3c3a6e787215e843c7a8bfa66eb3c56f8693c3477b2eb5d9c9" + }, + "motion_count": 10, + "passed": true, + "reset": { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "ai-ordinary", + "kind": "finish_reset", + "native_time_s": 25.07999999999698, + "operation_id": "agent-c0a81c03a9114aa2b68b127755699f4c", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8940, + "state_sha256": "aafe7bd44fe197a436c786478e803f5600e9356a85b524d8f8ba32237c0f8efc" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.1158277283874742, + -2.3639748691543634e-09, + 0.034995730455369786 + ], + "external_force_cleared": true, + "generation": 10, + "native_time_s": 25.07999999999698, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "aafe7bd44fe197a436c786478e803f5600e9356a85b524d8f8ba32237c0f8efc" + } + }, + { + "job_id": "ai-disturbed", + "measurement": { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "generation": 15, + "image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f", + "job_id": "ai-disturbed", + "measurement": { + "block_box": [ + 346, + 159, + 388, + 201 + ], + "block_center_xy": [ + 0.07833333333333332, + 0.0 + ], + "centroid_pixel_xy": [ + 366.5, + 179.5 + ], + "footprint_area_m2": 0.0049, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1764 + }, + "native_time_s": 38.95200000000164, + "operation_id": "agent-5dd59ada282f46c6aa679674b91c2983", + "source_kind": "mujoco_simulation", + "state_sha256": "4dd44356d2ac0116aed5dbff1f314ddf300da024850c26ad61191a1672b895c9", + "version": 1, + "worker_pid": 87908 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.07879828608478157, + -2.364032708257486e-09, + 0.03499573045536981 + ], + "external_force_cleared": true, + "generation": 15, + "native_time_s": 38.95200000000164, + "pusher_home": true, + "pusher_xyz": [ + -0.29999996923881395, + -2.4295371105308154e-36, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "4dd44356d2ac0116aed5dbff1f314ddf300da024850c26ad61191a1672b895c9" + }, + "motion_count": 12, + "passed": true, + "reset": { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "block_home_verified": true, + "generation": 23, + "image_sha256": "a834aeb17495159a215c0d6a51becde2e50afd38f47316aaa1a8c414d4af0bd4", + "job_id": "ai-disturbed", + "kind": "finish_reset", + "native_time_s": 57.032000000023736, + "operation_id": "agent-1fc92aa65ba3418eb400ecf69497e103", + "pusher_home_verified": true, + "reset_contact_samples": 3206, + "reset_native_steps": 9040, + "state_sha256": "6eecc4d33c156d2bc623608fecc63b5c4282a95fd37c531bf87ec5571d073f30" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11582602325339661, + -4.449925211827781e-09, + 0.03499573045536985 + ], + "external_force_cleared": true, + "generation": 23, + "native_time_s": 57.032000000023736, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "6eecc4d33c156d2bc623608fecc63b5c4282a95fd37c531bf87ec5571d073f30" + } + } + ], + "source_commit": "9a5d2f8bfac8088b13c63052887e0c0c2e9d5c7e", + "source_unchanged": true, + "sqlite": { + "count": 2, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "generation": 15, + "image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f", + "job_id": "ai-disturbed", + "operation_id": "agent-5dd59ada282f46c6aa679674b91c2983", + "receipt": { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "generation": 15, + "image_sha256": "b9a5cd587ca0afbcf85dbb7708030f9914fc7be82d9f29317563c4a7cd204e2f", + "job_id": "ai-disturbed", + "measurement": { + "block_box": [ + 346, + 159, + 388, + 201 + ], + "block_center_xy": [ + 0.07833333333333332, + 0.0 + ], + "centroid_pixel_xy": [ + 366.5, + 179.5 + ], + "footprint_area_m2": 0.0049, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1764 + }, + "native_time_s": 38.95200000000164, + "operation_id": "agent-5dd59ada282f46c6aa679674b91c2983", + "source_kind": "mujoco_simulation", + "state_sha256": "4dd44356d2ac0116aed5dbff1f314ddf300da024850c26ad61191a1672b895c9", + "version": 1, + "worker_pid": 87908 + }, + "state_sha256": "4dd44356d2ac0116aed5dbff1f314ddf300da024850c26ad61191a1672b895c9" + }, + { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "generation": 2, + "image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159", + "job_id": "ai-ordinary", + "operation_id": "agent-62ebb86d89f941daab3a059aa53fd695", + "receipt": { + "bench_id": "3e025aca6ea240f29003b7c6fdc91a39", + "generation": 2, + "image_sha256": "8d5e38c9bbef1250112550bdea0dee41bd573dd7cb2d42278be9eeda5590b159", + "job_id": "ai-ordinary", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 7.199999999999429, + "operation_id": "agent-62ebb86d89f941daab3a059aa53fd695", + "source_kind": "mujoco_simulation", + "state_sha256": "17c6620f9eeb3b3c3a6e787215e843c7a8bfa66eb3c56f8693c3477b2eb5d9c9", + "version": 1, + "worker_pid": 84953 + }, + "state_sha256": "17c6620f9eeb3b3c3a6e787215e843c7a8bfa66eb3c56f8693c3477b2eb5d9c9" + } + ] + }, + "version": 1 +} diff --git a/jevdual/results/agent-visual-bench-20261011/recovered.png b/jevdual/results/agent-visual-bench-20261011/recovered.png new file mode 100644 index 0000000..ff6006f Binary files /dev/null and b/jevdual/results/agent-visual-bench-20261011/recovered.png differ diff --git a/jevdual/results/agent-visual-bench-20261011/report.md b/jevdual/results/agent-visual-bench-20261011/report.md new file mode 100644 index 0000000..d376a59 --- /dev/null +++ b/jevdual/results/agent-visual-bench-20261011/report.md @@ -0,0 +1,50 @@ +# An AI controls successive simulated bench jobs + +The multimodal AI in this Codex conversation completed both preregistered jobs on one bench. It inspected rendered images and authored individual movement, measurement and reset proposals through the new handoff. A saved measurement proposal refused after the diagnostic harness moved the block; the AI inspected the changed image, chose corrective motion, and completed measurement and reset. + +Frozen source: `9a5d2f8bfac8088b13c63052887e0c0c2e9d5c7e`. This was one frozen trial attempt. No deterministic coordinate planner or native-pose input guided the AI. Bench, measurement-worker, native-motion and image-guard source stayed unchanged from SIM6. + +## What was tested + +The [visual AI handoff](../../../tools/physical-workcell/AGENT_BENCH.md) binds immutable image challenges to typed proposals, retains dispatch intent, routes actions through the existing bench, and returns results and observation snapshots. CLI responses filter detector coordinates from measurement/status receipts. Full private records remain available for post-trial scoring. + +The AI received task regions, pusher dimensions and calibrated images. It selected every target in this conversation and inspected a new image after each dispatch. Tool orchestration saved and dispatched those choices; it contained no action-selection loop or automatic fallback policy. The actual host was this Codex assistant, not an unattended provider-backed process. No API key or new provider was used. + +The model supplied 29 proposals. The host presented 31 images through `view_image`, including initial observations for both jobs. Every proposal binds a challenge whose image digest appears in the presentation record. There were 28 executed proposals, one expected stale refusal, and no unknown outcomes. + +## Results + +| Job | AI motion actions | Measurement generation | Reset generation | Reset contact samples | Result | +|---|---:|---:|---:|---:|---| +| Ordinary | 10 | 2 | 10 | 3,174 | Completed | +| Disturbed | 12 | 15 | 23 | 3,206 | Recovered and completed | + +One bench ID, `3e025aca6ea240f29003b7c6fdc91a39`, spanned both jobs. Native time advanced from 1.000 s to 25.080 s to 57.032 s. Job two started from the exact generation/time retained by job one's verified reset. Both resets used native contacts and returned the block and pusher home. + +Separate native scoring found block x=0.06583 m at the first measurement and x=0.07880 m at the recovered measurement, inside their respective target regions. Read-only SQLite scoring confirmed exactly one measurement per job with matching image/state identities. Both measurement subprocesses exited with code 0. The final bench was idle with no unresolved effects. + +Evidence: [native/SQLite score](native-sqlite-score.json), [AI action audit](AI-action-audit.json), [image presentations](image-presentations.json), [source hashes](source-freeze.json), [cleanup](cleanup.json). The complete private session and integration-state history remain in `tools/physical-workcell/output/sim7-ai-20261011-v1/`. + +## Disturbance recovery + +The AI saved its second-job measurement proposal at generation 12. Before dispatch, the harness applied a -1.5 N external force for 80 native steps, followed by settling. Generation 13 retained the changed state with the force cleared. Post-trial scoring measured a -0.13303 m displacement along x and found the block outside the measurement region. + +The original proposal was refused as stale. Its returned observation showed the displaced block. The AI proposed a corrective push from that new challenge, inspected the result, retracted the pusher and issued a new measurement at generation 15. It then chose the reset route and requested independent completion verification at generation 23. + +The guard refused the stale measurement before computer dispatch. This was a deliberately injected, preregistered disturbance; the AI knew a disturbance trial was planned. It is not a blind benchmark or a general recovery-rate estimate. + +## Verification and limits + +Fifteen handoff tests and fourteen unchanged-bench tests passed. They cover exact replay, foreign/stale evidence, source and receipt tampering, phase rules, filtered model inputs, failed measurement workers and a lost host reply after actual native motion. The latter retains an unknown result and blocks future dispatch even when the underlying bench is ready. See [checks.json](checks.json). Ruff, notebook validation, diff checks and result redaction also passed. + +The separate scorer ran only after both AI-controlled jobs finished. It checked historical measurement placement, current reset positions, contact evidence, native time/generation continuity, SQLite persistence and unchanged source. Presentation records document images emitted to the AI host; the host-role label alone is not model authentication or cryptographic proof of model origin. + +This qualifies the interface and this AI session's two-job behavior in the narrow simulator. It does not qualify an unattended model service, other model policies, arbitrary objects or disturbances, continuous wall-clock physics, real cameras, instruments, actuators or production Footwork capability governance. Unknown interrupted effects stop new dispatch; they are not automatically repaired. + +## Replay + +Orange is the block, blue is the pusher, and green is the task region. The replay contains recorded observations rather than interpolated motion or continuous video. It pauses at the displaced frame during the second job. [Frame identities](frames.json) preserve the original image digests. + +![AI-controlled successive jobs and disturbance recovery](AI-observations.gif) + +To reproduce, use the handoff guide in a checkout with the frozen source and a fresh private bench/session. Supply the task and images to a multimodal host, retain every proposal and outcome, then invoke `score_agent_bench.py score --run-dir RUN_DIRECTORY` after control ends. The scoring command checks physics and persistence; it does not manufacture or run an AI policy. diff --git a/jevdual/results/agent-visual-bench-20261011/reset.png b/jevdual/results/agent-visual-bench-20261011/reset.png new file mode 100644 index 0000000..02754ec Binary files /dev/null and b/jevdual/results/agent-visual-bench-20261011/reset.png differ diff --git a/jevdual/results/agent-visual-bench-20261011/source-freeze.json b/jevdual/results/agent-visual-bench-20261011/source-freeze.json new file mode 100644 index 0000000..d2ca778 --- /dev/null +++ b/jevdual/results/agent-visual-bench-20261011/source-freeze.json @@ -0,0 +1,16 @@ +{ + "commit": "9a5d2f8bfac8088b13c63052887e0c0c2e9d5c7e", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_agent_bench.py": "8a0eeb0885efaa5360a94cd3e187b460c34610c06a973f37dcd3b7d287fac521", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/bench-operator-20261011/attempt-ledger.json b/jevdual/results/bench-operator-20261011/attempt-ledger.json new file mode 100644 index 0000000..7a58f4d --- /dev/null +++ b/jevdual/results/bench-operator-20261011/attempt-ledger.json @@ -0,0 +1,12 @@ +{ + "transport_probes": 2, + "trial_v1_reserved_calls": 30, + "trial_v2_reserved_calls": 31, + "total_reserved_cli_requests": 63, + "usage_scope": "reported token totals only; no monetary price or complete billing evidence", + "v1_qualification": "inconclusive, retained; retrospective repaired score has changed-source flag", + "v2_qualification": "passed first version-2 attempt", + "cli_version": "codex-cli 0.147.0", + "model": "CLI default; exact model name not reported", + "reasoning_effort": "low" +} \ No newline at end of file diff --git a/jevdual/results/bench-operator-20261011/completed-reload.png b/jevdual/results/bench-operator-20261011/completed-reload.png new file mode 100644 index 0000000..b78b461 Binary files /dev/null and b/jevdual/results/bench-operator-20261011/completed-reload.png differ diff --git a/jevdual/results/bench-operator-20261011/conformance-summary.json b/jevdual/results/bench-operator-20261011/conformance-summary.json new file mode 100644 index 0000000..7fb5e74 --- /dev/null +++ b/jevdual/results/bench-operator-20261011/conformance-summary.json @@ -0,0 +1,105 @@ +{ + "suites": [ + { + "suite": "runtime", + "passed": true, + "tests": 20, + "elapsed_s": 14.404, + "cases": [ + "test_cancelled_reply_stays_withheld_after_event_clears (test_operator_runtime.OperatorTests.test_cancelled_reply_stays_withheld_after_event_clears)", + "test_frame_identity_changes_when_zone_repaint_keeps_generation (test_operator_runtime.OperatorTests.test_frame_identity_changes_when_zone_repaint_keeps_generation)", + "test_get_state_and_frame_do_not_capture_or_call_model (test_operator_runtime.OperatorTests.test_get_state_and_frame_do_not_capture_or_call_model)", + "test_global_reserved_call_budget_blocks_starting_another_job (test_operator_runtime.OperatorTests.test_global_reserved_call_budget_blocks_starting_another_job)", + "test_http_origin_identity_duplicate_keys_and_body_schema (test_operator_runtime.OperatorTests.test_http_origin_identity_duplicate_keys_and_body_schema)", + "test_interrupted_start_intent_blocks_restart_without_replay (test_operator_runtime.OperatorTests.test_interrupted_start_intent_blocks_restart_without_replay)", + "test_invalid_finish_reset_refuses_native_guard (test_operator_runtime.OperatorTests.test_invalid_finish_reset_refuses_native_guard)", + "test_model_cannot_claim_completion_or_bypass_guard (test_operator_runtime.OperatorTests.test_model_cannot_claim_completion_or_bypass_guard)", + "test_model_unavailable_pauses_counts_attempt_and_never_completes (test_operator_runtime.OperatorTests.test_model_unavailable_pauses_counts_attempt_and_never_completes)", + "test_mutated_operator_manifest_and_fabricated_completion_refuse (test_operator_runtime.OperatorTests.test_mutated_operator_manifest_and_fabricated_completion_refuse)", + "test_new_identity_bound_to_separate_persistent_bench (test_operator_runtime.OperatorTests.test_new_identity_bound_to_separate_persistent_bench)", + "test_pause_between_outer_loop_check_and_step_admission_starts_nothing (test_operator_runtime.OperatorTests.test_pause_between_outer_loop_check_and_step_admission_starts_nothing)", + "test_pause_during_model_prevents_late_reply_dispatch (test_operator_runtime.OperatorTests.test_pause_during_model_prevents_late_reply_dispatch)", + "test_queue_validation_duplicate_and_path_schema (test_operator_runtime.OperatorTests.test_queue_validation_duplicate_and_path_schema)", + "test_restart_retains_body_and_queue_without_replay (test_operator_runtime.OperatorTests.test_restart_retains_body_and_queue_without_replay)", + "test_shutdown_cancelled_model_exit_persists_stopped_after_worker_exits (test_operator_runtime.OperatorTests.test_shutdown_cancelled_model_exit_persists_stopped_after_worker_exits)", + "test_source_change_refuses_mutation (test_operator_runtime.OperatorTests.test_source_change_refuses_mutation)", + "test_stop_decision_pauses_without_completion (test_operator_runtime.OperatorTests.test_stop_decision_pauses_without_completion)", + "test_two_jobs_replay_public_action_fixture_and_disturbance_guard (test_operator_runtime.OperatorTests.test_two_jobs_replay_public_action_fixture_and_disturbance_guard)", + "test_unknown_native_blocks_resume_and_no_reset (test_operator_runtime.OperatorTests.test_unknown_native_blocks_resume_and_no_reset)" + ], + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-pause-admission-after-fix-runtime.log" + }, + { + "suite": "policy", + "passed": true, + "tests": 3, + "elapsed_s": 0.559, + "cases": [ + "test_closed_action_schema_finite_targets_and_no_success_flag (test_codex_policy.PolicyTests.test_closed_action_schema_finite_targets_and_no_success_flag)", + "test_fake_cli_bound_image_schema_tool_isolation_and_reservation (test_codex_policy.PolicyTests.test_fake_cli_bound_image_schema_tool_isolation_and_reservation)", + "test_usage_optional_and_any_tool_event_refuses (test_codex_policy.PolicyTests.test_usage_optional_and_any_tool_event_refuses)" + ], + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-final-policy-tests.log" + }, + { + "suite": "agent", + "passed": true, + "tests": 15, + "elapsed_s": 3.689, + "cases": [ + "test_challenge_has_only_images_task_and_public_observation (test_agent_bench.AgentBenchTests.test_challenge_has_only_images_task_and_public_observation)", + "test_changed_challenge_image_refuses (test_agent_bench.AgentBenchTests.test_changed_challenge_image_refuses)", + "test_changed_proposal_refuses_before_dispatch (test_agent_bench.AgentBenchTests.test_changed_proposal_refuses_before_dispatch)", + "test_error_after_real_move_stays_unknown_even_if_bench_ready (test_agent_bench.AgentBenchTests.test_error_after_real_move_stays_unknown_even_if_bench_ready)", + "test_foreign_challenge_and_unsafe_identity_refuse (test_agent_bench.AgentBenchTests.test_foreign_challenge_and_unsafe_identity_refuse)", + "test_measurement_placement_refusal_does_not_create_sqlite (test_agent_bench.AgentBenchTests.test_measurement_placement_refusal_does_not_create_sqlite)", + "test_measurement_worker_failure_retains_unknown_and_blocks_replay (test_agent_bench.AgentBenchTests.test_measurement_worker_failure_retains_unknown_and_blocks_replay)", + "test_move_executes_once_duplicate_returns_exact_result (test_agent_bench.AgentBenchTests.test_move_executes_once_duplicate_returns_exact_result)", + "test_nonfinite_and_conflicting_arguments_never_create_proposal (test_agent_bench.AgentBenchTests.test_nonfinite_and_conflicting_arguments_never_create_proposal)", + "test_proposal_persists_intent_without_effect (test_agent_bench.AgentBenchTests.test_proposal_persists_intent_without_effect)", + "test_public_cli_measurement_reset_and_status_strip_detector_locations (test_agent_bench.AgentBenchTests.test_public_cli_measurement_reset_and_status_strip_detector_locations)", + "test_source_change_and_foreign_bench_identity_block_dispatch (test_agent_bench.AgentBenchTests.test_source_change_and_foreign_bench_identity_block_dispatch)", + "test_stale_move_refuses_and_returns_fresh_image (test_agent_bench.AgentBenchTests.test_stale_move_refuses_and_returns_fresh_image)", + "test_unresolved_intent_blocks_same_and_new_proposal (test_agent_bench.AgentBenchTests.test_unresolved_intent_blocks_same_and_new_proposal)", + "test_wrong_reset_phase_preserves_bench (test_agent_bench.AgentBenchTests.test_wrong_reset_phase_preserves_bench)" + ], + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-final-agent-tests.log" + }, + { + "suite": "bench", + "passed": true, + "tests": 14, + "elapsed_s": 5.049, + "cases": [ + "test_corrupted_image_refuses_measurement_and_does_not_create_a_database (test_bench.BenchBehaviorTests.test_corrupted_image_refuses_measurement_and_does_not_create_a_database)", + "test_corrupted_native_state_never_causes_an_implicit_reset (test_bench.BenchBehaviorTests.test_corrupted_native_state_never_causes_an_implicit_reset)", + "test_corrupted_retained_model_refuses_without_recreating_the_body (test_bench.BenchBehaviorTests.test_corrupted_retained_model_refuses_without_recreating_the_body)", + "test_corrupted_source_binding_refuses_without_a_new_generation (test_bench.BenchBehaviorTests.test_corrupted_source_binding_refuses_without_a_new_generation)", + "test_diagnostic_force_needs_explicit_enablement_and_finite_bounds (test_bench.BenchBehaviorTests.test_diagnostic_force_needs_explicit_enablement_and_finite_bounds)", + "test_exact_motion_replay_retains_one_native_effect_and_conflicts_refuse (test_bench.BenchBehaviorTests.test_exact_motion_replay_retains_one_native_effect_and_conflicts_refuse)", + "test_external_force_invalidates_stale_motion_and_measurement_before_dispatch (test_bench.BenchBehaviorTests.test_external_force_invalidates_stale_motion_and_measurement_before_dispatch)", + "test_foreign_or_modified_observation_cannot_authorize_a_motion (test_bench.BenchBehaviorTests.test_foreign_or_modified_observation_cannot_authorize_a_motion)", + "test_measurement_column_corruption_is_not_hidden_by_an_unchanged_receipt (test_bench.BenchBehaviorTests.test_measurement_column_corruption_is_not_hidden_by_an_unchanged_receipt)", + "test_measurement_is_a_separate_process_with_exact_persisted_image_binding (test_bench.BenchBehaviorTests.test_measurement_is_a_separate_process_with_exact_persisted_image_binding)", + "test_measurement_receipt_corruption_refuses_historical_replay (test_bench.BenchBehaviorTests.test_measurement_receipt_corruption_refuses_historical_replay)", + "test_measurement_replay_is_historical_and_does_not_run_another_worker (test_bench.BenchBehaviorTests.test_measurement_replay_is_historical_and_does_not_run_another_worker)", + "test_retained_interrupted_intent_blocks_all_new_dispatch (test_bench.BenchBehaviorTests.test_retained_interrupted_intent_blocks_all_new_dispatch)", + "test_successive_job_gate_requires_measurement_and_verified_reset (test_bench.BenchBehaviorTests.test_successive_job_gate_requires_measurement_and_verified_reset)" + ], + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-final-bench-tests.log" + }, + { + "suite": "scorer", + "passed": true, + "tests": 2, + "elapsed_s": 0.004, + "cases": [ + "test_out_of_zone_refusal_does_not_count_as_stale_even_with_force (test_score_operator.ScorerTests.test_out_of_zone_refusal_does_not_count_as_stale_even_with_force)", + "test_refused_measure_and_later_move_do_not_prove_any_force (test_score_operator.ScorerTests.test_refused_measure_and_later_move_do_not_prove_any_force)" + ], + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-score-repair-tests.log" + } + ], + "total_tests": 54, + "scope": "labelled conformance; fake policies are not actual AI completion evidence" +} \ No newline at end of file diff --git a/jevdual/results/bench-operator-20261011/final-checks.json b/jevdual/results/bench-operator-20261011/final-checks.json new file mode 100644 index 0000000..151c5e0 --- /dev/null +++ b/jevdual/results/bench-operator-20261011/final-checks.json @@ -0,0 +1,249 @@ +{ + "passed": true, + "qualification_version": 2, + "attempt": 1, + "native_sqlite": true, + "browser_reload_completed_jobs": 2, + "browser_results_match_measurements": true, + "live_pause_no_effect": true, + "restart_no_replay": true, + "unchanged_core_guards": true, + "model_processes_reaped": 31, + "owned_model_pids_still_alive": [], + "product_server_retained_intentionally": { + "pid": 79998, + "origin": "http://127.0.0.1:64370", + "purpose": "user-facing product left running idle after verified trial", + "started_at": 1791748917.8438258 + }, + "final_state": { + "app_id": "dfc891dc85774b2497cd8b306844a96e", + "simulation": true, + "runner_status": "running", + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "label": "Baseline inspection", + "last_intent": "Finish reset now that the block center and pusher are both in their home positions.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "status": "completed", + "target": "A" + }, + { + "call_count": 17, + "disturbance_test": true, + "error": null, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "label": "Recovery inspection", + "last_intent": "Finish reset now that the block is centered in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "active_job_id": null, + "activity": [ + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 7 reserved", + "seq": 49, + "time": 1791748757.249968 + }, + { + "intent": "Measure with the block centered in the target region and the pusher clear of contact.", + "kind": "dispatch", + "outcome": "executed: measure", + "seq": 50, + "time": 1791748763.792654 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 8 reserved", + "seq": 51, + "time": 1791748764.077049 + }, + { + "intent": "Begin the reset sequence after the verified measurement.", + "kind": "dispatch", + "outcome": "executed: begin_reset", + "seq": 52, + "time": 1791748768.943161 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 9 reserved", + "seq": 53, + "time": 1791748769.218656 + }, + { + "intent": "Enter the upper bypass lane before crossing to the block's right side.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 54, + "time": 1791748776.686862 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 10 reserved", + "seq": 55, + "time": 1791748776.915343 + }, + { + "intent": "Cross the upper bypass lane to position the pusher safely right of the block.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 56, + "time": 1791748784.087341 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 11 reserved", + "seq": 57, + "time": 1791748784.352294 + }, + { + "intent": "Return from the bypass lane to align on the block's right side for a leftward push.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 58, + "time": 1791748791.344933 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 12 reserved", + "seq": 59, + "time": 1791748791.558492 + }, + { + "intent": "Push the block left from its right side until its center reaches the home zone.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 60, + "time": 1791748799.1835608 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 13 reserved", + "seq": 61, + "time": 1791748799.412058 + }, + { + "intent": "Retreat right to clear contact with the home-positioned block before entering the bypass lane.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 62, + "time": 1791748809.6114829 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 14 reserved", + "seq": 63, + "time": 1791748809.81729 + }, + { + "intent": "Enter the upper bypass lane before crossing left toward the pusher home position.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 64, + "time": 1791748817.115353 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 15 reserved", + "seq": 65, + "time": 1791748817.392006 + }, + { + "intent": "Cross the upper bypass lane to align above the pusher home position.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 66, + "time": 1791748824.084478 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 16 reserved", + "seq": 67, + "time": 1791748824.3450701 + }, + { + "intent": "Lower the safely bypassed pusher to its home position while leaving the block centered in the home zone.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 68, + "time": 1791748831.661006 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 17 reserved", + "seq": 69, + "time": 1791748831.861367 + }, + { + "intent": "Finish reset now that the block is centered in the home zone and the pusher is at its home position.", + "kind": "dispatch", + "outcome": "executed: finish_reset", + "seq": 70, + "time": 1791748838.118604 + }, + { + "intent": "Recovery inspection", + "kind": "completed", + "outcome": "Saved measurement and contact reset verified", + "seq": 71, + "time": 1791748838.1658392 + }, + { + "seq": 72, + "time": 1791748926.575486, + "kind": "resume", + "intent": "", + "outcome": "" + } + ], + "usage": { + "calls": 31, + "input_tokens": 388482, + "output_tokens": 4804, + "reported_calls": 30, + "token_usage_complete": false + }, + "readiness": { + "ready": true, + "reason": "Ready for the next action boundary" + }, + "current_frame": { + "generation": 24, + "image_url": "/api/frame?generation=24&sha256=010748ac551827d5579da25ccf5ef0a1618f26a60826a7c5d5c452904d1eb68b", + "available": true + } + }, + "remaining_queue_slots": 4, + "native_hardware_qualified": false, + "scope": "local simulated prototype; no hardware or demand qualification" +} \ No newline at end of file diff --git a/jevdual/results/bench-operator-20261011/initial.json b/jevdual/results/bench-operator-20261011/initial.json new file mode 100644 index 0000000..e0ce58f --- /dev/null +++ b/jevdual/results/bench-operator-20261011/initial.json @@ -0,0 +1,25 @@ +{ + "active_job": null, + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 +} diff --git a/jevdual/results/bench-operator-20261011/pause-check.json b/jevdual/results/bench-operator-20261011/pause-check.json new file mode 100644 index 0000000..b8113fc --- /dev/null +++ b/jevdual/results/bench-operator-20261011/pause-check.json @@ -0,0 +1,93 @@ +{ + "state": { + "app_id": "dfc891dc85774b2497cd8b306844a96e", + "simulation": true, + "runner_status": "paused", + "jobs": [ + { + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "label": "Baseline inspection", + "target": "A", + "disturbance_test": false, + "status": "paused", + "phase": "prepare", + "call_count": 1, + "last_intent": "", + "measurement": null, + "error": "operator_pause_requested" + } + ], + "model_request_pending": false, + "active_job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "activity": [ + { + "seq": 1, + "time": 1791748595.4540858, + "kind": "queued", + "intent": "Baseline inspection", + "outcome": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b" + }, + { + "seq": 2, + "time": 1791748595.4656131, + "kind": "start_intent", + "intent": "Baseline inspection", + "outcome": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b" + }, + { + "seq": 3, + "time": 1791748595.552342, + "kind": "inference", + "intent": "Baseline inspection", + "outcome": "Model call 1 reserved" + }, + { + "seq": 4, + "time": 1791748598.720981, + "kind": "pause", + "intent": "", + "outcome": "Paused; an in-flight native action may finish before the next boundary" + }, + { + "seq": 5, + "time": 1791748598.737185, + "kind": "halt", + "intent": "", + "outcome": "operator_pause_requested" + } + ], + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0, + "token_usage_complete": false + }, + "readiness": { + "ready": false, + "reason": "operator_pause_requested" + }, + "current_frame": { + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "available": true + } + }, + "outcome": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 2.8941972500178963, + "exit_code": 0, + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "outcome": "cancelled", + "pid": 60738, + "process_exit_observed": true, + "provider": "codex_cli", + "reason": "operator_pause_requested", + "request_sha256": "a0f2b3e1bf07e9e33875d1eb4709ef4377bee4cb2c0863310c95f6d4e405c053", + "started_at": 1791748595.837315, + "usage": null + }, + "proposal_exists": false, + "dispatch_exists": false, + "native_generation": 0 +} \ No newline at end of file diff --git a/jevdual/results/bench-operator-20261011/paused.png b/jevdual/results/bench-operator-20261011/paused.png new file mode 100644 index 0000000..7543d0a Binary files /dev/null and b/jevdual/results/bench-operator-20261011/paused.png differ diff --git a/jevdual/results/bench-operator-20261011/product-ready.png b/jevdual/results/bench-operator-20261011/product-ready.png new file mode 100644 index 0000000..7a09200 Binary files /dev/null and b/jevdual/results/bench-operator-20261011/product-ready.png differ diff --git a/jevdual/results/bench-operator-20261011/qualification-score.json b/jevdual/results/bench-operator-20261011/qualification-score.json new file mode 100644 index 0000000..af54904 --- /dev/null +++ b/jevdual/results/bench-operator-20261011/qualification-score.json @@ -0,0 +1,1516 @@ +{ + "attempt": 1, + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "calls": [ + { + "call": 1, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-01", + "decision": { + "action": "move", + "intent": "Approach the block from the left and establish contact for a rightward push.", + "target_x": -0.17, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.680992666981183, + "exit_code": 0, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "outcome": "decision", + "pid": 67916, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "6987827b1f131e8315ea3c6c27c863506da88a052803315ee9a3220267b1923b", + "started_at": 1791748708.814469, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12604, + "output_tokens": 211 + } + }, + "reason": "" + }, + { + "call": 2, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-02", + "decision": { + "action": "move", + "intent": "Push the block right until its center reaches the target region.", + "target_x": 0.027, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "46b6852eacb1c63790a9fd63c5090faf7ff2bf63f9df0f9bb505d04f1e403117", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.139760125079192, + "exit_code": 0, + "image_sha256": "46b6852eacb1c63790a9fd63c5090faf7ff2bf63f9df0f9bb505d04f1e403117", + "outcome": "decision", + "pid": 68423, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "779336792e5e7df7a6e85d0aad02167ce457c6a5347b0409dec223c6f30c880b", + "started_at": 1791748716.941611, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12679, + "output_tokens": 138 + } + }, + "reason": "" + }, + { + "call": 3, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-03", + "decision": { + "action": "move", + "intent": "Retreat the pusher left to clear contact while leaving the block centered in the target region.", + "target_x": -0.03, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "309b7ab0690ea94e8cc138f40b3eaef61106dc280ef356bd9fbbbfc855d4e903", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.309757583076134, + "exit_code": 0, + "image_sha256": "309b7ab0690ea94e8cc138f40b3eaef61106dc280ef356bd9fbbbfc855d4e903", + "outcome": "decision", + "pid": 68945, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "40bbc2022ae2b1249f409ffe7e36b12db82a9b2f7fc36f0db149ed9ca59e52ff", + "started_at": 1791748723.544991, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12748, + "output_tokens": 175 + } + }, + "reason": "" + }, + { + "call": 4, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-04", + "decision": { + "action": "measure", + "intent": "Measure with the block centered in the target region and the pusher clear of contact.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "9631b7e3430d2dc46771fe34515c8790d382496a5f4039d17cd1f380069f3615", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "refused", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.3183612920111045, + "exit_code": 0, + "image_sha256": "9631b7e3430d2dc46771fe34515c8790d382496a5f4039d17cd1f380069f3615", + "outcome": "decision", + "pid": 69447, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "71b5ad9b18752ec057bf8f47869a1a91d00f4ee2c8edfc7c5c74426311190838", + "started_at": 1791748730.30097, + "usage": { + "cached_input_tokens": 10752, + "input_tokens": 13240, + "output_tokens": 137 + } + }, + "reason": "observation is stale or foreign; no requested effect dispatched" + }, + { + "call": 5, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-05", + "decision": { + "action": "move", + "intent": "Push the block right so its center enters the target region.", + "target_x": 0.025, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "8ecd05bf7bad1a62371c00eb6b072300f2d2d2f268611cc07c308fa4a4054337", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 10.437788041075692, + "exit_code": 0, + "image_sha256": "8ecd05bf7bad1a62371c00eb6b072300f2d2d2f268611cc07c308fa4a4054337", + "outcome": "decision", + "pid": 69955, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "f34b89178ca4acd5da4793301b366444fe00b7554041589c64316d09b8aaede8", + "started_at": 1791748737.113226, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12904, + "output_tokens": 316 + } + }, + "reason": "" + }, + { + "call": 6, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-06", + "decision": { + "action": "move", + "intent": "Retreat the pusher left to clear contact before measuring the centered block.", + "target_x": -0.03, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "25238865c3ca77c3e54455b58a89ed7a62cb99510340bc20a4d025e9495eeb02", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 8.909630124922842, + "exit_code": 0, + "image_sha256": "25238865c3ca77c3e54455b58a89ed7a62cb99510340bc20a4d025e9495eeb02", + "outcome": "decision", + "pid": 70474, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "f4ef85e35473f7d6c0bf81aa06174b38e156da8b6f9384933fe9f42c8e73fb11", + "started_at": 1791748747.9909508, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12973, + "output_tokens": 212 + } + }, + "reason": "" + }, + { + "call": 7, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-07", + "decision": { + "action": "measure", + "intent": "Measure with the block centered in the target region and the pusher clear of contact.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 5.998212875099853, + "exit_code": 0, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "outcome": "decision", + "pid": 70989, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "7ab1935fefdd412052d4ed533d652644c48aa08134bba0a59bb5aa4aaaa8e652", + "started_at": 1791748757.342904, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13045, + "output_tokens": 115 + } + }, + "reason": "" + }, + { + "call": 8, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-08", + "decision": { + "action": "begin_reset", + "intent": "Begin the reset sequence after the verified measurement.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 4.467566958977841, + "exit_code": 0, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "outcome": "decision", + "pid": 71496, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "a1ef66dba0fd98d066d01027b977749800cea4d55ad581df87de809a7b29808e", + "started_at": 1791748764.1715, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13041, + "output_tokens": 38 + } + }, + "reason": "" + }, + { + "call": 9, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-09", + "decision": { + "action": "move", + "intent": "Enter the upper bypass lane before crossing to the block's right side.", + "target_x": -0.03, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.072396583040245, + "exit_code": 0, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "outcome": "decision", + "pid": 71998, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "6fe2ac1cf0f497e0586c18d73d4f861b6c4112bc66483004f8818dbe94359abe", + "started_at": 1791748769.3148868, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13033, + "output_tokens": 210 + } + }, + "reason": "" + }, + { + "call": 10, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-10", + "decision": { + "action": "move", + "intent": "Cross the upper bypass lane to position the pusher safely right of the block.", + "target_x": 0.145, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "80c5e2a9c1566aafee4e3cc9a933fa44aeefe74ff79b17937d645ccec6b4aabe", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.764236333081499, + "exit_code": 0, + "image_sha256": "80c5e2a9c1566aafee4e3cc9a933fa44aeefe74ff79b17937d645ccec6b4aabe", + "outcome": "decision", + "pid": 72506, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "c2dad9a9858be45b82498b31f3f7cd81b6cc120c8658f7d8e1a65fc98a2dc4fe", + "started_at": 1791748777.013569, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13028, + "output_tokens": 186 + } + }, + "reason": "" + }, + { + "call": 11, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-11", + "decision": { + "action": "move", + "intent": "Return from the bypass lane to align on the block's right side for a leftward push.", + "target_x": 0.145, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "30f8b66ec786bd64bc42c067b79821315c26e2476a4981ddbc6e225295c9eecf", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.608077459037304, + "exit_code": 0, + "image_sha256": "30f8b66ec786bd64bc42c067b79821315c26e2476a4981ddbc6e225295c9eecf", + "outcome": "decision", + "pid": 73017, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "b7a896f78882eedf81ca2555c77af8e2aae24eb147817e936cf0f1589de5c5eb", + "started_at": 1791748784.451158, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13021, + "output_tokens": 186 + } + }, + "reason": "" + }, + { + "call": 12, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-12", + "decision": { + "action": "move", + "intent": "Push the block left from its right side until its center reaches the home zone.", + "target_x": -0.065, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "58232c9bfdc530c98e9eb2b17ff816201091549fb45eb19be05700a6df326b7d", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.216281041968614, + "exit_code": 0, + "image_sha256": "58232c9bfdc530c98e9eb2b17ff816201091549fb45eb19be05700a6df326b7d", + "outcome": "decision", + "pid": 73524, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "0f6d5fc16d3c143b3765cca1a090634dcc228e2c822cc4d1ce183b9e057ff4da", + "started_at": 1791748791.65263, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13028, + "output_tokens": 176 + } + }, + "reason": "" + }, + { + "call": 13, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-13", + "decision": { + "action": "move", + "intent": "Retreat right to clear contact with the home-positioned block before entering the bypass lane.", + "target_x": -0.03, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "3fbc15f387e7d6bdfcc7acc65497d1904707776869995ca60f37dde177934c2d", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 9.817345042014495, + "exit_code": 0, + "image_sha256": "3fbc15f387e7d6bdfcc7acc65497d1904707776869995ca60f37dde177934c2d", + "outcome": "decision", + "pid": 74038, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "d8b17a72ccd504e15e29fb32a8f4af5778c1a86310ddb2e3c3fc63d4ea0a618a", + "started_at": 1791748799.511749, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13029, + "output_tokens": 174 + } + }, + "reason": "" + }, + { + "call": 14, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-14", + "decision": { + "action": "move", + "intent": "Enter the upper bypass lane before crossing left toward the pusher home position.", + "target_x": -0.03, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "2c1e749091601504668cd2a8b356a1dc1d16741c33df8cf633c2c08fffef224c", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.902980957995169, + "exit_code": 0, + "image_sha256": "2c1e749091601504668cd2a8b356a1dc1d16741c33df8cf633c2c08fffef224c", + "outcome": "decision", + "pid": 74551, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "2f2a8d85cd5c03e44cf961145a0c2150ab35bc70c160328c33ce8e4d5aab6d42", + "started_at": 1791748809.915574, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13036, + "output_tokens": 153 + } + }, + "reason": "" + }, + { + "call": 15, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-15", + "decision": { + "action": "move", + "intent": "Cross the upper bypass lane to align above the pusher home position.", + "target_x": -0.3, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "bb5bfbfd914c629da0738d197c05b19249ff1da05d3d4e392819d7c93fa1681b", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.304673292092048, + "exit_code": 0, + "image_sha256": "bb5bfbfd914c629da0738d197c05b19249ff1da05d3d4e392819d7c93fa1681b", + "outcome": "decision", + "pid": 75060, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "c3948a3dffedb99c7c66d34ef527b0ae997363183b43be1ca55f5a7ae7acd4c4", + "started_at": 1791748817.490079, + "usage": { + "cached_input_tokens": 10752, + "input_tokens": 13463, + "output_tokens": 120 + } + }, + "reason": "" + }, + { + "call": 16, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-16", + "decision": { + "action": "move", + "intent": "Lower the safely bypassed pusher to its home position while leaving the block centered in the home zone.", + "target_x": -0.3, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "8c8c81e8fa6699a6ff9dde4b0115531ed48bc55aaa30aa8c6f3a5da4178d33b2", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.912628417019732, + "exit_code": 0, + "image_sha256": "8c8c81e8fa6699a6ff9dde4b0115531ed48bc55aaa30aa8c6f3a5da4178d33b2", + "outcome": "decision", + "pid": 75563, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "9d2bff37e3a2f77b2084202a43edeb8b0eef41400b4e09822b1c2e2e1a714ada", + "started_at": 1791748824.4423308, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13047, + "output_tokens": 141 + } + }, + "reason": "" + }, + { + "call": 17, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9a905ec1-093b-47b6-ba1f-558b736da542-17", + "decision": { + "action": "finish_reset", + "intent": "Finish reset now that the block is centered in the home zone and the pusher is at its home position.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "2d2c4fcec313d713e1865aba74ba4fb11a792380d7ce2b6b694b4ff968aac95b", + "input_has_no_private_pose": true, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 5.831225499976426, + "exit_code": 0, + "image_sha256": "2d2c4fcec313d713e1865aba74ba4fb11a792380d7ce2b6b694b4ff968aac95b", + "outcome": "decision", + "pid": 76067, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "38b69530938359112a9e35bffd1a6ece8b9eb91dc38faeef58a79bb65758896b", + "started_at": 1791748831.9587572, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 12064, + "output_tokens": 99 + } + }, + "reason": "" + }, + { + "call": 1, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-01", + "exact_image": true, + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 2.8941972500178963, + "exit_code": 0, + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "outcome": "cancelled", + "pid": 60738, + "process_exit_observed": true, + "provider": "codex_cli", + "reason": "operator_pause_requested", + "request_sha256": "a0f2b3e1bf07e9e33875d1eb4709ef4377bee4cb2c0863310c95f6d4e405c053", + "started_at": 1791748595.837315, + "usage": null + } + }, + { + "call": 2, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-02", + "decision": { + "action": "move", + "intent": "Push the block rightward from home until its center reaches the measurement region.", + "target_x": 0.015, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.771410749992356, + "exit_code": 0, + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "outcome": "decision", + "pid": 61291, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "a0f2b3e1bf07e9e33875d1eb4709ef4377bee4cb2c0863310c95f6d4e405c053", + "started_at": 1791748621.132839, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13032, + "output_tokens": 212 + } + }, + "reason": "" + }, + { + "call": 3, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-03", + "decision": { + "action": "move", + "intent": "Retreat the pusher left to clear contact before measuring the centered block.", + "target_x": -0.04, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "cb4221e600bbb24aec549f49924fc9224876a1ad8d04159fe0cc3798658d8357", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.536180708091706, + "exit_code": 0, + "image_sha256": "cb4221e600bbb24aec549f49924fc9224876a1ad8d04159fe0cc3798658d8357", + "outcome": "decision", + "pid": 61800, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "974954ac74bc5746d8154c23be8b0fc193f8cd458471c20563676f056ea68211", + "started_at": 1791748629.212647, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12690, + "output_tokens": 149 + } + }, + "reason": "" + }, + { + "call": 4, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-04", + "decision": { + "action": "measure", + "intent": "Measure the block now that its center is inside the region and the pusher is clear.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 5.477408333099447, + "exit_code": 0, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "outcome": "decision", + "pid": 62304, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "94802673bc8a7479b4086ad6733d6c55575cf6fe38405e56a67b07fb43d3eb46", + "started_at": 1791748636.0486348, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12762, + "output_tokens": 148 + } + }, + "reason": "" + }, + { + "call": 5, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-05", + "decision": { + "action": "begin_reset", + "intent": "Begin resetting the measured block to its home zone.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 4.967806125059724, + "exit_code": 0, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "outcome": "decision", + "pid": 62817, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "2c907e165a47f2897a17c5e81e264193a3e4b7557446f34510b5791b47b1b29d", + "started_at": 1791748642.164298, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12832, + "output_tokens": 39 + } + }, + "reason": "" + }, + { + "call": 6, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-06", + "decision": { + "action": "move", + "intent": "Enter the upper bypass lane before crossing to the block's right side.", + "target_x": -0.04, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.360148167004809, + "exit_code": 0, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "outcome": "decision", + "pid": 63316, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "59ff995d793e076ff5eedcb5ecc3002d33d96e6671970855d7454a35a70aec71", + "started_at": 1791748647.5468538, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12894, + "output_tokens": 172 + } + }, + "reason": "" + }, + { + "call": 7, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-07", + "decision": { + "action": "move", + "intent": "Cross the upper bypass lane to position the pusher safely right of the block.", + "target_x": 0.13, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "0b9ecb877096f2d1d09b1046fc58d78ca05baab62e332d78ede2994ce16b51a5", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.585064874961972, + "exit_code": 0, + "image_sha256": "0b9ecb877096f2d1d09b1046fc58d78ca05baab62e332d78ede2994ce16b51a5", + "outcome": "decision", + "pid": 63849, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "7d8dffa8cabfc343b348abb9308517cb8bdc03d776fd424762e6d19407406dc7", + "started_at": 1791748655.3502579, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12965, + "output_tokens": 153 + } + }, + "reason": "" + }, + { + "call": 8, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-08", + "decision": { + "action": "move", + "intent": "Descend from the bypass lane to align just right of the block for a leftward push.", + "target_x": 0.13, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "41df83de033593526658fc0ac492c8506adf5413fb2c922e36fe303838e5f747", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.3576757500413805, + "exit_code": 0, + "image_sha256": "41df83de033593526658fc0ac492c8506adf5413fb2c922e36fe303838e5f747", + "outcome": "decision", + "pid": 64372, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "d1990163416e9631728d269425177622121a20bf36547972b2524692e24a8168", + "started_at": 1791748662.4149141, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13038, + "output_tokens": 208 + } + }, + "reason": "" + }, + { + "call": 9, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-09", + "decision": { + "action": "move", + "intent": "Push from the block's right side until its center reaches the home zone.", + "target_x": -0.065, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "06475d5ab8e24a45b006d5956b5631ca93cc9381a357528fb821c9c6c8f9772f", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.805414041969925, + "exit_code": 0, + "image_sha256": "06475d5ab8e24a45b006d5956b5631ca93cc9381a357528fb821c9c6c8f9772f", + "outcome": "decision", + "pid": 64878, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "dd2b6bc8005c646db50051236477855273894763d7b297e52747a3f3cc33cb0d", + "started_at": 1791748670.254776, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13042, + "output_tokens": 226 + } + }, + "reason": "" + }, + { + "call": 10, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-10", + "decision": { + "action": "move", + "intent": "Retreat slightly right to release contact before entering the bypass lane.", + "target_x": -0.04, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "2c9ebfdb6099f62c21d31821dc81a1797c61abfddf09c9defe304da5788a9962", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.244875542004593, + "exit_code": 0, + "image_sha256": "2c9ebfdb6099f62c21d31821dc81a1797c61abfddf09c9defe304da5788a9962", + "outcome": "decision", + "pid": 65390, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "fc2af26e6b22128f2ae288a8bc97fc8ed4719312cc60ca75f5483daa53324e90", + "started_at": 1791748678.526176, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13042, + "output_tokens": 161 + } + }, + "reason": "" + }, + { + "call": 11, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-11", + "decision": { + "action": "move", + "intent": "Enter the upper bypass lane to cross safely toward the pusher home position.", + "target_x": -0.04, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "5e2fc7b4662d203274cbb2a36d0e914ef60fd89c4d1ead1dd22ed0b702f354f9", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 5.9611949160462245, + "exit_code": 0, + "image_sha256": "5e2fc7b4662d203274cbb2a36d0e914ef60fd89c4d1ead1dd22ed0b702f354f9", + "outcome": "decision", + "pid": 65902, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "22faf95784fb405c88fcfb4f0ed883e1f3965b2af6f5139c4fde5b0d2dc9a695", + "started_at": 1791748685.2536378, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13043, + "output_tokens": 159 + } + }, + "reason": "" + }, + { + "call": 12, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-12", + "decision": { + "action": "move", + "intent": "Cross the upper bypass lane to align above the pusher home position.", + "target_x": -0.3, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "4e5d0e92f04cec9106091fcc3ada6db457946d90e2752f5c9999647d8a36bf4d", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.720921500003897, + "exit_code": 0, + "image_sha256": "4e5d0e92f04cec9106091fcc3ada6db457946d90e2752f5c9999647d8a36bf4d", + "outcome": "decision", + "pid": 66407, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "91ae494fcba5b553580bf852d861cab0c3c2416b4c58295c59c2f2493ec717c3", + "started_at": 1791748691.673928, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13053, + "output_tokens": 169 + } + }, + "reason": "" + }, + { + "call": 13, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-13", + "decision": { + "action": "move", + "intent": "Descend from the bypass lane to place the pusher at its home position.", + "target_x": -0.3, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "cda2378ead3e165186825aa560540c8495d01f07acff067406513a16ee92db65", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 4.30064162495546, + "exit_code": 0, + "image_sha256": "cda2378ead3e165186825aa560540c8495d01f07acff067406513a16ee92db65", + "outcome": "decision", + "pid": 66916, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "e6220401a263c4a7069d6d49ed9d6b48fb9a8b4bb9145b17b068d8ff6f71f7da", + "started_at": 1791748698.837965, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13053, + "output_tokens": 112 + } + }, + "reason": "" + }, + { + "call": 14, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v2/calls/9bd00615-a89f-4c60-99ff-4b3dd5110b4b-14", + "decision": { + "action": "finish_reset", + "intent": "Finish reset now that the block center and pusher are both in their home positions.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "input_has_no_private_pose": true, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 4.761849500006065, + "exit_code": 0, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "outcome": "decision", + "pid": 67417, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "53208d3e1e2e144586822b3d87627eaa08c5795458f827ff009a62f53026353a", + "started_at": 1791748703.5867379, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13053, + "output_tokens": 109 + } + }, + "reason": "" + } + ], + "completed_cycles": 2, + "current_status": { + "active_job": null, + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "diagnostic_enabled": true, + "generation": 24, + "jobs": { + "9a905ec1-093b-47b6-ba1f-558b736da542": { + "completed_generation": 24, + "completed_native_time_s": 47.08400000001158, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "measurement_operation_id": "agent-b519582eb3ab41de958c8eadcb92f5ed", + "measurement_receipt": { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "generation": 16, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 30.87199999999377, + "operation_id": "agent-b519582eb3ab41de958c8eadcb92f5ed", + "source_kind": "mujoco_simulation", + "state_sha256": "9b206c16fbb8c063c96435a193d4f6c0a3e6f1f8b9ee9248db7f39db00c89c8d", + "version": 1, + "worker_pid": 71488 + }, + "moves": 13, + "phase": "completed", + "reset_receipt": { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "block_home_verified": true, + "generation": 24, + "image_sha256": "2d2c4fcec313d713e1865aba74ba4fb11a792380d7ce2b6b694b4ff968aac95b", + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "kind": "finish_reset", + "native_time_s": 47.08400000001158, + "operation_id": "agent-aa948ca623a44006ac18558c969e48d5", + "pusher_home_verified": true, + "reset_contact_samples": 3254, + "reset_native_steps": 8106, + "state_sha256": "132d2bea7a67e78141657149106ccc90df895bca904e6af7492317b5a60d179c" + }, + "reset_started_generation": 16, + "started_generation": 10, + "started_native_time_s": 21.41399999999901, + "target_zone": { + "x_max": 0.115, + "x_min": 0.045, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "9bd00615-a89f-4c60-99ff-4b3dd5110b4b": { + "completed_generation": 10, + "completed_native_time_s": 21.41399999999901, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "measurement_operation_id": "agent-2407da0b686446928abaf297263b5a5e", + "measurement_receipt": { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.46799999999962, + "operation_id": "agent-2407da0b686446928abaf297263b5a5e", + "source_kind": "mujoco_simulation", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9", + "version": 1, + "worker_pid": 62810 + }, + "moves": 10, + "phase": "completed", + "reset_receipt": { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "kind": "finish_reset", + "native_time_s": 21.41399999999901, + "operation_id": "agent-2f7c12fce7ff42fa99c63b87a023d57f", + "pusher_home_verified": true, + "reset_contact_samples": 3176, + "reset_native_steps": 7973, + "state_sha256": "d923fb3c194e7404636029c8d2b468edf4b568f6a200a808f32edd587a9bd531" + }, + "reset_started_generation": 2, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 47.08400000001158, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "fault_chain": { + "after": { + "block_center_in_zone": false, + "block_xyz": [ + 0.02165326919336783, + -3.655373498173245e-09, + 0.03499717436613952 + ], + "external_force_cleared": true, + "generation": 14, + "native_time_s": 28.135999999995285, + "pusher_home": false, + "pusher_xyz": [ + -0.0333443983479918, + -1.9429622913956716e-16, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "f3a7e43aa0a79f79670c21dfff70841121a674347e63bd6f56a487f83d52c040" + }, + "before": { + "block_center_in_zone": true, + "block_xyz": [ + 0.07778295988512215, + -3.6553672755072192e-09, + 0.03499573045536984 + ], + "external_force_cleared": true, + "generation": 13, + "native_time_s": 26.975999999995928, + "pusher_home": false, + "pusher_xyz": [ + -0.029999969510766278, + -2.717046987497888e-26, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "2fd97239450e5a0a8f66d61c8303f81a953afd8028c880de0540b8c6bf254e01" + }, + "displacement_m": 0.0561296907103263, + "force": { + "receipt": { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "force_x_n": -1.5, + "forced_steps": 80, + "generation": 14, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "kind": "diagnostic_external_force", + "native_steps": 580, + "native_time_s": 28.135999999995285, + "operation_id": "disturb-9f0942e06dda4629a523a0dbbb440dcb", + "state_sha256": "f3a7e43aa0a79f79670c21dfff70841121a674347e63bd6f56a487f83d52c040" + }, + "request": { + "force_x_n": -1.5, + "kind": "disturb", + "steps": 80 + } + }, + "fresh_model_move_bound_to_force_image": true, + "immutable_chain_bound": true, + "no_measurement_effect": true, + "passed": true, + "saved_proposal_id": "proposal-43634e68ce9f4c2b8d860c8c25bd69e4", + "stale_call": 4 + }, + "fresh_model_move_after_refusal": true, + "model_boundaries_passed": true, + "passed": true, + "qualification_version": 2, + "scope": "separate post-trial native/SQLite and retained model-boundary scoring; browser evidence recorded separately", + "scores": [ + { + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "label": "Baseline inspection", + "measurement": { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.46799999999962, + "operation_id": "agent-2407da0b686446928abaf297263b5a5e", + "source_kind": "mujoco_simulation", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9", + "version": 1, + "worker_pid": 62810 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06582870900153087, + -8.38971234022521e-11, + 0.03499573045536974 + ], + "external_force_cleared": true, + "generation": 2, + "native_time_s": 5.46799999999962, + "pusher_home": false, + "pusher_xyz": [ + -0.03999996967021979, + 1.8297702229966647e-25, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9" + }, + "model_calls": 14, + "motion_count": 10, + "passed": true, + "reset": { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "kind": "finish_reset", + "native_time_s": 21.41399999999901, + "operation_id": "agent-2f7c12fce7ff42fa99c63b87a023d57f", + "pusher_home_verified": true, + "reset_contact_samples": 3176, + "reset_native_steps": 7973, + "state_sha256": "d923fb3c194e7404636029c8d2b468edf4b568f6a200a808f32edd587a9bd531" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11579047256178736, + -3.655089424085224e-09, + 0.03499573045536985 + ], + "external_force_cleared": true, + "generation": 10, + "native_time_s": 21.41399999999901, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "d923fb3c194e7404636029c8d2b468edf4b568f6a200a808f32edd587a9bd531" + } + }, + { + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "label": "Recovery inspection", + "measurement": { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "generation": 16, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 30.87199999999377, + "operation_id": "agent-b519582eb3ab41de958c8eadcb92f5ed", + "source_kind": "mujoco_simulation", + "state_sha256": "9b206c16fbb8c063c96435a193d4f6c0a3e6f1f8b9ee9248db7f39db00c89c8d", + "version": 1, + "worker_pid": 71488 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.07578649625730521, + -3.6554321865059693e-09, + 0.034995730455369856 + ], + "external_force_cleared": true, + "generation": 16, + "native_time_s": 30.87199999999377, + "pusher_home": false, + "pusher_xyz": [ + -0.029999969671500593, + 1.5748173236245696e-25, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "9b206c16fbb8c063c96435a193d4f6c0a3e6f1f8b9ee9248db7f39db00c89c8d" + }, + "model_calls": 17, + "motion_count": 13, + "passed": true, + "reset": { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "block_home_verified": true, + "generation": 24, + "image_sha256": "2d2c4fcec313d713e1865aba74ba4fb11a792380d7ce2b6b694b4ff968aac95b", + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "kind": "finish_reset", + "native_time_s": 47.08400000001158, + "operation_id": "agent-aa948ca623a44006ac18558c969e48d5", + "pusher_home_verified": true, + "reset_contact_samples": 3254, + "reset_native_steps": 8106, + "state_sha256": "132d2bea7a67e78141657149106ccc90df895bca904e6af7492317b5a60d179c" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11584066223772632, + -8.066777403780345e-09, + 0.03499573045536981 + ], + "external_force_cleared": true, + "generation": 24, + "native_time_s": 47.08400000001158, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "132d2bea7a67e78141657149106ccc90df895bca904e6af7492317b5a60d179c" + } + } + ], + "source_commit": "1415f594ccc91dbc6f7856ba9b2ab0252d45cfdb", + "source_unchanged": true, + "sqlite": { + "count": 2, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "generation": 16, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "operation_id": "agent-b519582eb3ab41de958c8eadcb92f5ed", + "receipt": { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "generation": 16, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 30.87199999999377, + "operation_id": "agent-b519582eb3ab41de958c8eadcb92f5ed", + "source_kind": "mujoco_simulation", + "state_sha256": "9b206c16fbb8c063c96435a193d4f6c0a3e6f1f8b9ee9248db7f39db00c89c8d", + "version": 1, + "worker_pid": 71488 + }, + "state_sha256": "9b206c16fbb8c063c96435a193d4f6c0a3e6f1f8b9ee9248db7f39db00c89c8d" + }, + { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "operation_id": "agent-2407da0b686446928abaf297263b5a5e", + "receipt": { + "bench_id": "3e46ab9264d148db8f026373a79f0078", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.46799999999962, + "operation_id": "agent-2407da0b686446928abaf297263b5a5e", + "source_kind": "mujoco_simulation", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9", + "version": 1, + "worker_pid": 62810 + }, + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9" + } + ] + }, + "stale_measure_refused": true, + "usage": { + "calls": 31, + "input_tokens": 388482, + "output_tokens": 4804, + "reported_calls": 30 + }, + "version": 1 +} diff --git a/jevdual/results/bench-operator-20261011/recovery.png b/jevdual/results/bench-operator-20261011/recovery.png new file mode 100644 index 0000000..848aeb7 Binary files /dev/null and b/jevdual/results/bench-operator-20261011/recovery.png differ diff --git a/jevdual/results/bench-operator-20261011/report.md b/jevdual/results/bench-operator-20261011/report.md new file mode 100644 index 0000000..38df955 --- /dev/null +++ b/jevdual/results/bench-operator-20261011/report.md @@ -0,0 +1,48 @@ +# Footwork Bench: unattended visual workcell and local product + +The final source-frozen qualification passed two queued image-guided inspection cycles on one persistent simulated bench. A real independently running Codex policy chose each move, measurement and reset action. Native state and read-only SQLite verified both cycles separately; the browser retained their results after reload and a server restart. + +## What the product does + +Open the local bench, add a named inspection and select a region. The AI observes the camera image, moves the pusher, corrects placement, triggers a separately executed optical measurement and returns both bodies home by contact. The browser exposes the queue, most recent camera capture, model planning, intent, measurement, activity and pause/resume. Repeat creates a new job. Simulation remains explicit throughout. + +Runbook: [OPERATOR.md](../../../tools/physical-workcell/OPERATOR.md). Implementation stays in Footwork's existing physical-workcell package: stdlib Python, plain browser HTML/CSS/JavaScript and pinned MuJoCo 3.15.0. Existing native guard files are unchanged. + +## Final qualification + +Source freeze: `1415f594ccc91dbc6f7856ba9b2ab0252d45cfdb`. Version 2, attempt 1. Private run: `tools/physical-workcell/output/sim8-qualified-20261011-v2/`. + +Bench identity: `3e46ab9264d148db8f026373a79f0078`. Native generations continued 0 → 10 → 24; simulation time continued 1.000 → 21.414 → 47.084 seconds. No body reinitialization or pose assignment reset occurred. + +| Job | Reserved model calls | Native moves | Measurement generation | Reset generation | Apparent area, mm² | +|---|---:|---:|---:|---:|---:| +| Baseline inspection | 14 | 10 | 2 | 10 | 4794.4 | +| Recovery inspection | 17 | 13 | 16 | 24 | 4802.8 | + +The disturbance moved the block **5.61 cm** out of its target region after the first measurement proposal was saved. The immutable proposal/challenge/result chain and native force receipt bind that decision to the older generation. The stale measurement was refused without a native measurement operation; the next executed model move used the exact post-force image. The AI recovered and later measured successfully. The scorer independently checks force, displacement, freshness, SQLite rows, native placement, contact reset and continuous body/time identity. + +There were 31 reserved CLI requests: 30 typed decisions and one cancelled request. Twenty-nine decisions executed, one stale measurement was refused, and no outcome was unknown. Every launched model process exited and was reaped. Inputs contained exact matching PNG bytes and bounded task/history metadata; detector locations and native poses were absent. + +Pause during actual inference left generation zero and created no proposal or dispatch. Resume obtained fresh evidence. Server restart retained both completed jobs and the same origin/body, paused without replay, and required an explicit resume. The user-facing server is intentionally left running idle at http://127.0.0.1:64370 with no pending model request. Its owned PID is recorded in `final-checks.json`; four queue slots remain. The product limit is six jobs, 32 calls per job and 96 calls per operator lifetime. Stop this instance with `kill -TERM 79998`; restart on the retained port as documented. + +Reported final-trial usage: 388,482 input and 4,804 output tokens across 30 of 31 requests. Cancelled-call usage is unavailable. The CLI is 0.147.0, uses saved authentication and its default model with low reasoning; an exact model name is not reported. No API key was opened or provisioned. Monetary price is unmeasured. + +## Checks and preserved failures + +**54 focused conformance checks passed**: 20 runtime, 3 policy, 15 agent, 14 bench and 2 scorer. Fake policies are labelled diagnostic and supply no model-control evidence. Ruff, JavaScript syntax, whitespace and notebook validation passed. + +The first no-dispatch policy probe returned a valid action but was classified as unexpected tool use because two nonfatal CLI startup diagnostics were misread. It is retained privately; the committed classifier repair accepted a second fresh probe while unknown errors and actual tool items still refuse. + +Version 1's actual AI/browser workflow completed both cycles, but the original scorer first encountered a live ownership lock and then applied a 64 KiB protocol-data bound to a larger PNG. Its frozen qualification is **inconclusive**. Retrospective repaired scoring verifies its native cycles and 3.98 cm disturbance, while retaining a false source-unchanged flag; it is not promoted to a passed frozen run. The initial run and scorer failures are retained under `v1/` and its private run directory. + +Read-only review then found a pause admission race between the outer loop check and step lock. The regression started a queued job/reserved a call after pause before the fix; an in-lock check now prevents both. Cancelled-motion dispatch was already guarded. The scorer was also tightened so an unrelated measurement refusal followed by a move cannot count as disturbance recovery. Both repairs were preregistered and committed before the fresh version 2 model run. + +## Evidence and limits + +- [Independent score](qualification-score.json), [force chain](qualification-score.json), [source freeze](source-freeze.json), [final readback and process checks](final-checks.json). +- [Conformance cases](conformance-summary.json), [all-attempt ledger](attempt-ledger.json), [version 1 retrospective record](v1/qualification-score.json). +- [Product ready](product-ready.png), [paused request](paused.png), [recovery in progress](recovery.png), [completed browser reload](completed-reload.png). + +Adopt as a local simulated product prototype. The result establishes this bounded image-to-action-to-computer loop on the recorded scene and attempts. It does not establish hardware transfer, general manipulation, broad model repeatability, power-loss recovery, production security or customer demand. Optical area is an apparent calibrated image footprint, not the block's true surface area. Python remains adequate; the native integration already runs in MuJoCo. No measured Rust hot path has been identified. + +The next hardware boundary is a persistent body adapter with the same observation/proposal/receipt semantics, qualified first under network delay, disconnects, duplicates and cancellation. The browser product can remain the workcell supervisor while the simulated body is exchanged for a qualified device. diff --git a/jevdual/results/bench-operator-20261011/restart-check.json b/jevdual/results/bench-operator-20261011/restart-check.json new file mode 100644 index 0000000..5a22d08 --- /dev/null +++ b/jevdual/results/bench-operator-20261011/restart-check.json @@ -0,0 +1,231 @@ +{ + "passed": true, + "state": { + "app_id": "dfc891dc85774b2497cd8b306844a96e", + "simulation": true, + "runner_status": "paused", + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "9bd00615-a89f-4c60-99ff-4b3dd5110b4b", + "label": "Baseline inspection", + "last_intent": "Finish reset now that the block center and pusher are both in their home positions.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "status": "completed", + "target": "A" + }, + { + "call_count": 17, + "disturbance_test": true, + "error": null, + "job_id": "9a905ec1-093b-47b6-ba1f-558b736da542", + "label": "Recovery inspection", + "last_intent": "Finish reset now that the block is centered in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "active_job_id": null, + "activity": [ + { + "intent": "Retreat the pusher left to clear contact before measuring the centered block.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 48, + "time": 1791748757.1141179 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 7 reserved", + "seq": 49, + "time": 1791748757.249968 + }, + { + "intent": "Measure with the block centered in the target region and the pusher clear of contact.", + "kind": "dispatch", + "outcome": "executed: measure", + "seq": 50, + "time": 1791748763.792654 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 8 reserved", + "seq": 51, + "time": 1791748764.077049 + }, + { + "intent": "Begin the reset sequence after the verified measurement.", + "kind": "dispatch", + "outcome": "executed: begin_reset", + "seq": 52, + "time": 1791748768.943161 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 9 reserved", + "seq": 53, + "time": 1791748769.218656 + }, + { + "intent": "Enter the upper bypass lane before crossing to the block's right side.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 54, + "time": 1791748776.686862 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 10 reserved", + "seq": 55, + "time": 1791748776.915343 + }, + { + "intent": "Cross the upper bypass lane to position the pusher safely right of the block.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 56, + "time": 1791748784.087341 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 11 reserved", + "seq": 57, + "time": 1791748784.352294 + }, + { + "intent": "Return from the bypass lane to align on the block's right side for a leftward push.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 58, + "time": 1791748791.344933 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 12 reserved", + "seq": 59, + "time": 1791748791.558492 + }, + { + "intent": "Push the block left from its right side until its center reaches the home zone.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 60, + "time": 1791748799.1835608 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 13 reserved", + "seq": 61, + "time": 1791748799.412058 + }, + { + "intent": "Retreat right to clear contact with the home-positioned block before entering the bypass lane.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 62, + "time": 1791748809.6114829 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 14 reserved", + "seq": 63, + "time": 1791748809.81729 + }, + { + "intent": "Enter the upper bypass lane before crossing left toward the pusher home position.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 64, + "time": 1791748817.115353 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 15 reserved", + "seq": 65, + "time": 1791748817.392006 + }, + { + "intent": "Cross the upper bypass lane to align above the pusher home position.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 66, + "time": 1791748824.084478 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 16 reserved", + "seq": 67, + "time": 1791748824.3450701 + }, + { + "intent": "Lower the safely bypassed pusher to its home position while leaving the block centered in the home zone.", + "kind": "dispatch", + "outcome": "executed: move", + "seq": 68, + "time": 1791748831.661006 + }, + { + "intent": "Recovery inspection", + "kind": "inference", + "outcome": "Model call 17 reserved", + "seq": 69, + "time": 1791748831.861367 + }, + { + "intent": "Finish reset now that the block is centered in the home zone and the pusher is at its home position.", + "kind": "dispatch", + "outcome": "executed: finish_reset", + "seq": 70, + "time": 1791748838.118604 + }, + { + "intent": "Recovery inspection", + "kind": "completed", + "outcome": "Saved measurement and contact reset verified", + "seq": 71, + "time": 1791748838.1658392 + } + ], + "usage": { + "calls": 31, + "input_tokens": 388482, + "output_tokens": 4804, + "reported_calls": 30, + "token_usage_complete": false + }, + "readiness": { + "ready": false, + "reason": "Restarted; inspect retained state and explicitly resume" + }, + "current_frame": { + "generation": 24, + "image_url": "/api/frame?generation=24&sha256=010748ac551827d5579da25ccf5ef0a1618f26a60826a7c5d5c452904d1eb68b", + "available": true + } + }, + "scope": "same retained origin and body, no unfinished job replay or additional model call" +} \ No newline at end of file diff --git a/jevdual/results/bench-operator-20261011/source-freeze.json b/jevdual/results/bench-operator-20261011/source-freeze.json new file mode 100644 index 0000000..28c0e59 --- /dev/null +++ b/jevdual/results/bench-operator-20261011/source-freeze.json @@ -0,0 +1,23 @@ +{ + "commit": "1415f594ccc91dbc6f7856ba9b2ab0252d45cfdb", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/bench_operator.html": "f91275d998dc528b7701086def6007d78b30025660bf5076678e23bf63dcbf54", + "tools/physical-workcell/bench_operator.py": "dad93159cad4e9cd8c4d2780af5c495951af9b70f6405f25ca6ed696e6662024", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "40fe73e01d97ee1e5cfdb813650b11edadbea29534784ab10cc1ef4e7f4d5418", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/operator_runtime.py": "13d4c23cc2a95dbd76d408ff5e52b302be0fab791b659fde22839ccf9d5ddb48", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_operator.py": "d6548eadde719517ced687874d88025c00bc7494ef042932c46937e6abd0b220", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + }, + "qualification_version": 2 +} diff --git a/jevdual/results/bench-operator-20261011/v1/completed-reload.png b/jevdual/results/bench-operator-20261011/v1/completed-reload.png new file mode 100644 index 0000000..46cdb3a Binary files /dev/null and b/jevdual/results/bench-operator-20261011/v1/completed-reload.png differ diff --git a/jevdual/results/bench-operator-20261011/v1/conformance-summary.json b/jevdual/results/bench-operator-20261011/v1/conformance-summary.json new file mode 100644 index 0000000..0f8b4c6 --- /dev/null +++ b/jevdual/results/bench-operator-20261011/v1/conformance-summary.json @@ -0,0 +1,93 @@ +{ + "suites": [ + { + "suite": "runtime", + "passed": true, + "tests": 19, + "elapsed_s": 14.808, + "cases": [ + "test_cancelled_reply_stays_withheld_after_event_clears (test_operator_runtime.OperatorTests.test_cancelled_reply_stays_withheld_after_event_clears)", + "test_frame_identity_changes_when_zone_repaint_keeps_generation (test_operator_runtime.OperatorTests.test_frame_identity_changes_when_zone_repaint_keeps_generation)", + "test_get_state_and_frame_do_not_capture_or_call_model (test_operator_runtime.OperatorTests.test_get_state_and_frame_do_not_capture_or_call_model)", + "test_global_reserved_call_budget_blocks_starting_another_job (test_operator_runtime.OperatorTests.test_global_reserved_call_budget_blocks_starting_another_job)", + "test_http_origin_identity_duplicate_keys_and_body_schema (test_operator_runtime.OperatorTests.test_http_origin_identity_duplicate_keys_and_body_schema)", + "test_interrupted_start_intent_blocks_restart_without_replay (test_operator_runtime.OperatorTests.test_interrupted_start_intent_blocks_restart_without_replay)", + "test_invalid_finish_reset_refuses_native_guard (test_operator_runtime.OperatorTests.test_invalid_finish_reset_refuses_native_guard)", + "test_model_cannot_claim_completion_or_bypass_guard (test_operator_runtime.OperatorTests.test_model_cannot_claim_completion_or_bypass_guard)", + "test_model_unavailable_pauses_counts_attempt_and_never_completes (test_operator_runtime.OperatorTests.test_model_unavailable_pauses_counts_attempt_and_never_completes)", + "test_mutated_operator_manifest_and_fabricated_completion_refuse (test_operator_runtime.OperatorTests.test_mutated_operator_manifest_and_fabricated_completion_refuse)", + "test_new_identity_bound_to_separate_persistent_bench (test_operator_runtime.OperatorTests.test_new_identity_bound_to_separate_persistent_bench)", + "test_pause_during_model_prevents_late_reply_dispatch (test_operator_runtime.OperatorTests.test_pause_during_model_prevents_late_reply_dispatch)", + "test_queue_validation_duplicate_and_path_schema (test_operator_runtime.OperatorTests.test_queue_validation_duplicate_and_path_schema)", + "test_restart_retains_body_and_queue_without_replay (test_operator_runtime.OperatorTests.test_restart_retains_body_and_queue_without_replay)", + "test_shutdown_cancelled_model_exit_persists_stopped_after_worker_exits (test_operator_runtime.OperatorTests.test_shutdown_cancelled_model_exit_persists_stopped_after_worker_exits)", + "test_source_change_refuses_mutation (test_operator_runtime.OperatorTests.test_source_change_refuses_mutation)", + "test_stop_decision_pauses_without_completion (test_operator_runtime.OperatorTests.test_stop_decision_pauses_without_completion)", + "test_two_jobs_replay_public_action_fixture_and_disturbance_guard (test_operator_runtime.OperatorTests.test_two_jobs_replay_public_action_fixture_and_disturbance_guard)", + "test_unknown_native_blocks_resume_and_no_reset (test_operator_runtime.OperatorTests.test_unknown_native_blocks_resume_and_no_reset)" + ], + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-final-runtime-tests.log" + }, + { + "suite": "policy", + "passed": true, + "tests": 3, + "elapsed_s": 0.559, + "cases": [ + "test_closed_action_schema_finite_targets_and_no_success_flag (test_codex_policy.PolicyTests.test_closed_action_schema_finite_targets_and_no_success_flag)", + "test_fake_cli_bound_image_schema_tool_isolation_and_reservation (test_codex_policy.PolicyTests.test_fake_cli_bound_image_schema_tool_isolation_and_reservation)", + "test_usage_optional_and_any_tool_event_refuses (test_codex_policy.PolicyTests.test_usage_optional_and_any_tool_event_refuses)" + ], + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-final-policy-tests.log" + }, + { + "suite": "agent", + "passed": true, + "tests": 15, + "elapsed_s": 3.689, + "cases": [ + "test_challenge_has_only_images_task_and_public_observation (test_agent_bench.AgentBenchTests.test_challenge_has_only_images_task_and_public_observation)", + "test_changed_challenge_image_refuses (test_agent_bench.AgentBenchTests.test_changed_challenge_image_refuses)", + "test_changed_proposal_refuses_before_dispatch (test_agent_bench.AgentBenchTests.test_changed_proposal_refuses_before_dispatch)", + "test_error_after_real_move_stays_unknown_even_if_bench_ready (test_agent_bench.AgentBenchTests.test_error_after_real_move_stays_unknown_even_if_bench_ready)", + "test_foreign_challenge_and_unsafe_identity_refuse (test_agent_bench.AgentBenchTests.test_foreign_challenge_and_unsafe_identity_refuse)", + "test_measurement_placement_refusal_does_not_create_sqlite (test_agent_bench.AgentBenchTests.test_measurement_placement_refusal_does_not_create_sqlite)", + "test_measurement_worker_failure_retains_unknown_and_blocks_replay (test_agent_bench.AgentBenchTests.test_measurement_worker_failure_retains_unknown_and_blocks_replay)", + "test_move_executes_once_duplicate_returns_exact_result (test_agent_bench.AgentBenchTests.test_move_executes_once_duplicate_returns_exact_result)", + "test_nonfinite_and_conflicting_arguments_never_create_proposal (test_agent_bench.AgentBenchTests.test_nonfinite_and_conflicting_arguments_never_create_proposal)", + "test_proposal_persists_intent_without_effect (test_agent_bench.AgentBenchTests.test_proposal_persists_intent_without_effect)", + "test_public_cli_measurement_reset_and_status_strip_detector_locations (test_agent_bench.AgentBenchTests.test_public_cli_measurement_reset_and_status_strip_detector_locations)", + "test_source_change_and_foreign_bench_identity_block_dispatch (test_agent_bench.AgentBenchTests.test_source_change_and_foreign_bench_identity_block_dispatch)", + "test_stale_move_refuses_and_returns_fresh_image (test_agent_bench.AgentBenchTests.test_stale_move_refuses_and_returns_fresh_image)", + "test_unresolved_intent_blocks_same_and_new_proposal (test_agent_bench.AgentBenchTests.test_unresolved_intent_blocks_same_and_new_proposal)", + "test_wrong_reset_phase_preserves_bench (test_agent_bench.AgentBenchTests.test_wrong_reset_phase_preserves_bench)" + ], + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-final-agent-tests.log" + }, + { + "suite": "bench", + "passed": true, + "tests": 14, + "elapsed_s": 5.049, + "cases": [ + "test_corrupted_image_refuses_measurement_and_does_not_create_a_database (test_bench.BenchBehaviorTests.test_corrupted_image_refuses_measurement_and_does_not_create_a_database)", + "test_corrupted_native_state_never_causes_an_implicit_reset (test_bench.BenchBehaviorTests.test_corrupted_native_state_never_causes_an_implicit_reset)", + "test_corrupted_retained_model_refuses_without_recreating_the_body (test_bench.BenchBehaviorTests.test_corrupted_retained_model_refuses_without_recreating_the_body)", + "test_corrupted_source_binding_refuses_without_a_new_generation (test_bench.BenchBehaviorTests.test_corrupted_source_binding_refuses_without_a_new_generation)", + "test_diagnostic_force_needs_explicit_enablement_and_finite_bounds (test_bench.BenchBehaviorTests.test_diagnostic_force_needs_explicit_enablement_and_finite_bounds)", + "test_exact_motion_replay_retains_one_native_effect_and_conflicts_refuse (test_bench.BenchBehaviorTests.test_exact_motion_replay_retains_one_native_effect_and_conflicts_refuse)", + "test_external_force_invalidates_stale_motion_and_measurement_before_dispatch (test_bench.BenchBehaviorTests.test_external_force_invalidates_stale_motion_and_measurement_before_dispatch)", + "test_foreign_or_modified_observation_cannot_authorize_a_motion (test_bench.BenchBehaviorTests.test_foreign_or_modified_observation_cannot_authorize_a_motion)", + "test_measurement_column_corruption_is_not_hidden_by_an_unchanged_receipt (test_bench.BenchBehaviorTests.test_measurement_column_corruption_is_not_hidden_by_an_unchanged_receipt)", + "test_measurement_is_a_separate_process_with_exact_persisted_image_binding (test_bench.BenchBehaviorTests.test_measurement_is_a_separate_process_with_exact_persisted_image_binding)", + "test_measurement_receipt_corruption_refuses_historical_replay (test_bench.BenchBehaviorTests.test_measurement_receipt_corruption_refuses_historical_replay)", + "test_measurement_replay_is_historical_and_does_not_run_another_worker (test_bench.BenchBehaviorTests.test_measurement_replay_is_historical_and_does_not_run_another_worker)", + "test_retained_interrupted_intent_blocks_all_new_dispatch (test_bench.BenchBehaviorTests.test_retained_interrupted_intent_blocks_all_new_dispatch)", + "test_successive_job_gate_requires_measurement_and_verified_reset (test_bench.BenchBehaviorTests.test_successive_job_gate_requires_measurement_and_verified_reset)" + ], + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-final-bench-tests.log" + } + ], + "total_tests": 51, + "scope": "labelled conformance; fake policies are not actual AI completion evidence" +} \ No newline at end of file diff --git a/jevdual/results/bench-operator-20261011/v1/initial.json b/jevdual/results/bench-operator-20261011/v1/initial.json new file mode 100644 index 0000000..c0bca72 --- /dev/null +++ b/jevdual/results/bench-operator-20261011/v1/initial.json @@ -0,0 +1,25 @@ +{ + "active_job": null, + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 +} diff --git a/jevdual/results/bench-operator-20261011/v1/pause-check.json b/jevdual/results/bench-operator-20261011/v1/pause-check.json new file mode 100644 index 0000000..57fb772 --- /dev/null +++ b/jevdual/results/bench-operator-20261011/v1/pause-check.json @@ -0,0 +1,93 @@ +{ + "state": { + "app_id": "6f81d519dcfb43c0a7fb05292e0c97d7", + "simulation": true, + "runner_status": "paused", + "jobs": [ + { + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "label": "Baseline inspection", + "target": "A", + "disturbance_test": false, + "status": "paused", + "phase": "prepare", + "call_count": 1, + "last_intent": "", + "measurement": null, + "error": "operator_pause_requested" + } + ], + "model_request_pending": false, + "active_job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "activity": [ + { + "seq": 1, + "time": 1791747955.0840802, + "kind": "queued", + "intent": "Baseline inspection", + "outcome": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef" + }, + { + "seq": 2, + "time": 1791747955.0969112, + "kind": "start_intent", + "intent": "Baseline inspection", + "outcome": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef" + }, + { + "seq": 3, + "time": 1791747955.1905348, + "kind": "inference", + "intent": "Baseline inspection", + "outcome": "Model call 1 reserved" + }, + { + "seq": 4, + "time": 1791747957.950015, + "kind": "pause", + "intent": "", + "outcome": "Paused; an in-flight native action may finish before the next boundary" + }, + { + "seq": 5, + "time": 1791747957.9677608, + "kind": "halt", + "intent": "", + "outcome": "operator_pause_requested" + } + ], + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0, + "token_usage_complete": false + }, + "readiness": { + "ready": false, + "reason": "operator_pause_requested" + }, + "current_frame": { + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "available": true + } + }, + "outcome": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 2.4791216669837013, + "exit_code": 0, + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "outcome": "cancelled", + "pid": 43768, + "process_exit_observed": true, + "provider": "codex_cli", + "reason": "operator_pause_requested", + "request_sha256": "1ea7c320b8e069ddb9033b30e87ef09550f86a19f2d3842f10594ffa83c495e3", + "started_at": 1791747955.481091, + "usage": null + }, + "proposal_exists": false, + "dispatch_exists": false, + "native_generation": 0 +} \ No newline at end of file diff --git a/jevdual/results/bench-operator-20261011/v1/qualification-score.json b/jevdual/results/bench-operator-20261011/v1/qualification-score.json new file mode 100644 index 0000000..fa7d2dd --- /dev/null +++ b/jevdual/results/bench-operator-20261011/v1/qualification-score.json @@ -0,0 +1,1483 @@ +{ + "attempt": 1, + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "calls": [ + { + "call": 1, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-01", + "exact_image": true, + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 2.4791216669837013, + "exit_code": 0, + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "outcome": "cancelled", + "pid": 43768, + "process_exit_observed": true, + "provider": "codex_cli", + "reason": "operator_pause_requested", + "request_sha256": "1ea7c320b8e069ddb9033b30e87ef09550f86a19f2d3842f10594ffa83c495e3", + "started_at": 1791747955.481091, + "usage": null + } + }, + { + "call": 2, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-02", + "decision": { + "action": "move", + "intent": "Push the block right until its center reaches the target zone.", + "target_x": 0.015, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.849983792053536, + "exit_code": 0, + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "outcome": "decision", + "pid": 44387, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "1ea7c320b8e069ddb9033b30e87ef09550f86a19f2d3842f10594ffa83c495e3", + "started_at": 1791748002.4942029, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 12612, + "output_tokens": 205 + } + }, + "reason": "" + }, + { + "call": 3, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-03", + "decision": { + "action": "move", + "intent": "Retract the pusher left to clear contact before measuring.", + "target_x": -0.05, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "cb4221e600bbb24aec549f49924fc9224876a1ad8d04159fe0cc3798658d8357", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.667845832998864, + "exit_code": 0, + "image_sha256": "cb4221e600bbb24aec549f49924fc9224876a1ad8d04159fe0cc3798658d8357", + "outcome": "decision", + "pid": 44900, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "63b5c518ef3926c4dddf0f6134737c5e1dafa71e505a2c0e819017d7eff0842c", + "started_at": 1791748010.667745, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13099, + "output_tokens": 173 + } + }, + "reason": "" + }, + { + "call": 4, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-04", + "decision": { + "action": "measure", + "intent": "Measure with the block centered in the target zone and the pusher clear.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.881987208034843, + "exit_code": 0, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "outcome": "decision", + "pid": 45415, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "3b8abc9063677d968e4568281b662ce535bb78d425d6e2a24249129ae0c113e2", + "started_at": 1791748018.6216419, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 12752, + "output_tokens": 120 + } + }, + "reason": "" + }, + { + "call": 5, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-05", + "decision": { + "action": "begin_reset", + "intent": "Begin reset after the verified measurement.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 4.3645455840742216, + "exit_code": 0, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "outcome": "decision", + "pid": 45925, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "234ec7c30702c2fcff25c81b82a4c9b2e881fdd13325a285df83fe1f13349dd1", + "started_at": 1791748026.124774, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 12819, + "output_tokens": 36 + } + }, + "reason": "" + }, + { + "call": 6, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-06", + "decision": { + "action": "move", + "intent": "Enter the upper bypass lane before crossing to the block's right side.", + "target_x": -0.05, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.16687750001438, + "exit_code": 0, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "outcome": "decision", + "pid": 46434, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "e021570c95c0f3c592f9e9e4dd9557e739b3ce0b5f1c849fd3a1e3ac60582697", + "started_at": 1791748030.9481258, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 12878, + "output_tokens": 191 + } + }, + "reason": "" + }, + { + "call": 7, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-07", + "decision": { + "action": "move", + "intent": "Cross horizontally through the upper bypass lane to clear the block's right side.", + "target_x": 0.14, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "aeb57b730bc64cb1ec47fbf9a42ef0acce36a927702e60d6df1d751e4d240d1e", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 5.60894104198087, + "exit_code": 0, + "image_sha256": "aeb57b730bc64cb1ec47fbf9a42ef0acce36a927702e60d6df1d751e4d240d1e", + "outcome": "decision", + "pid": 46949, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "997c133c62de6f0728296dca32f078c8724c141ae634ba18e418a30929cb1044", + "started_at": 1791748038.544946, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12949, + "output_tokens": 144 + } + }, + "reason": "" + }, + { + "call": 8, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-08", + "decision": { + "action": "move", + "intent": "Descend from the bypass lane to align on the block's right side.", + "target_x": 0.14, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "c62187cd908d71592bdd7193b83ec80666e6f350e90968204d2d87a7169d12dc", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.800459083053283, + "exit_code": 0, + "image_sha256": "c62187cd908d71592bdd7193b83ec80666e6f350e90968204d2d87a7169d12dc", + "outcome": "decision", + "pid": 47455, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "f2a90b5784c11ee2709eb5eb54f01e79c43cc02e7afcbc89443a675705584e0a", + "started_at": 1791748044.5946538, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13021, + "output_tokens": 233 + } + }, + "reason": "" + }, + { + "call": 9, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-09", + "decision": { + "action": "move", + "intent": "Push the block left from its right side until its center reaches the home zone.", + "target_x": -0.065, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "e6f35d68a39eccb69ef08ffb81de2aed231bbe097a530bc31bb48ffda662a3f1", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.44600391597487, + "exit_code": 0, + "image_sha256": "e6f35d68a39eccb69ef08ffb81de2aed231bbe097a530bc31bb48ffda662a3f1", + "outcome": "decision", + "pid": 47967, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "18844fa9c6b4610d70f4d6f818204ab593467ce49d03474eaaa3505facc64594", + "started_at": 1791748052.853769, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13024, + "output_tokens": 191 + } + }, + "reason": "" + }, + { + "call": 10, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-10", + "decision": { + "action": "move", + "intent": "Retreat slightly right to release contact before entering the bypass lane.", + "target_x": -0.03, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "6c4785ad11fbf959867ca24381e9caf3a051c777b86c8e5357697aa2cdea5f67", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.274478291976266, + "exit_code": 0, + "image_sha256": "6c4785ad11fbf959867ca24381e9caf3a051c777b86c8e5357697aa2cdea5f67", + "outcome": "decision", + "pid": 48473, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "fc1027ce3a3f8c2dfbce7515e3f73caad702d7924d5cb0fdd57d3bb9ff214aa2", + "started_at": 1791748059.7891278, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13028, + "output_tokens": 205 + } + }, + "reason": "" + }, + { + "call": 11, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-11", + "decision": { + "action": "move", + "intent": "Enter the upper bypass lane before crossing left to the pusher home position.", + "target_x": -0.03, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "18e16fad85b15cc6b6c095191c6d8f1be9d3e1a605f05ae0ac11954d420d7c38", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 9.480418457998894, + "exit_code": 0, + "image_sha256": "18e16fad85b15cc6b6c095191c6d8f1be9d3e1a605f05ae0ac11954d420d7c38", + "outcome": "decision", + "pid": 48981, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "ed4d631901be126bad3f215cf2680783f6b35a9f8c396a706192f9c2bc77fd9f", + "started_at": 1791748066.594038, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13032, + "output_tokens": 177 + } + }, + "reason": "" + }, + { + "call": 12, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-12", + "decision": { + "action": "move", + "intent": "Cross left through the upper bypass lane to align above the pusher home position.", + "target_x": -0.3, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "18207abc055796d1b37f4b94528d52bfd960be835625169fc997360f0ec4d906", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.573040917050093, + "exit_code": 0, + "image_sha256": "18207abc055796d1b37f4b94528d52bfd960be835625169fc997360f0ec4d906", + "outcome": "decision", + "pid": 49516, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "6b4b53027012b1938db3e9fbb5a86e27954b84c4f9f79deb3fda8e946174ddb5", + "started_at": 1791748076.550199, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13045, + "output_tokens": 139 + } + }, + "reason": "" + }, + { + "call": 13, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-13", + "decision": { + "action": "move", + "intent": "Descend from the bypass lane to place the pusher at its home position.", + "target_x": -0.3, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "cda2378ead3e165186825aa560540c8495d01f07acff067406513a16ee92db65", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 4.563002625014633, + "exit_code": 0, + "image_sha256": "cda2378ead3e165186825aa560540c8495d01f07acff067406513a16ee92db65", + "outcome": "decision", + "pid": 50028, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "6f523f547452e42c1b16fa8bdc3262e08ec177eb04909c34719045c02e58322a", + "started_at": 1791748083.629318, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13047, + "output_tokens": 102 + } + }, + "reason": "" + }, + { + "call": 14, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/089965b9-90b8-4bc3-a4a0-71bcfc9bbeef-14", + "decision": { + "action": "finish_reset", + "intent": "Verify the block center and pusher are both at their home positions.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "input_has_no_private_pose": true, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 5.43770991708152, + "exit_code": 0, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "outcome": "decision", + "pid": 50527, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "d3ef7cb828ecfbcca3c2b6d94b8d74206e4dc3883724628489257a7a7ef9f3d3", + "started_at": 1791748088.6519191, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13048, + "output_tokens": 98 + } + }, + "reason": "" + }, + { + "call": 1, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-01", + "decision": { + "action": "move", + "intent": "Push the block rightward so its center enters the measurement region.", + "target_x": 0.02, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.025500625022687, + "exit_code": 0, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "outcome": "decision", + "pid": 51046, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "2b9935035d2ca76af7eeff020bc20cd89ceeca8ac70b78942df9190e20c0e10a", + "started_at": 1791748094.602582, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12608, + "output_tokens": 214 + } + }, + "reason": "" + }, + { + "call": 2, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-02", + "decision": { + "action": "move", + "intent": "Retreat the pusher left to clear contact before measuring.", + "target_x": -0.02, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "9dcda84f7fbb7dc6fefb739e5c03d41844904990de78d0ff166f200ac4b64d75", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 9.081188458018005, + "exit_code": 0, + "image_sha256": "9dcda84f7fbb7dc6fefb739e5c03d41844904990de78d0ff166f200ac4b64d75", + "outcome": "decision", + "pid": 51559, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "d5da5eeba589aad084d8341bc7c6f0c4ab352c59919387966d4bbe9f14791093", + "started_at": 1791748102.1232471, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12680, + "output_tokens": 194 + } + }, + "reason": "" + }, + { + "call": 3, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-03", + "decision": { + "action": "measure", + "intent": "Measure now that the block center is inside the target region and the pusher is clear.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "fe4391474faf68c9747d98a5ccab2cb3e67bba7c701d34243160ebfa15dc88f3", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "refused", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.434076459030621, + "exit_code": 0, + "image_sha256": "fe4391474faf68c9747d98a5ccab2cb3e67bba7c701d34243160ebfa15dc88f3", + "outcome": "decision", + "pid": 52079, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "8eb42d1af7e9fc2b9c62d8ba5f898a4dad7a2e98a7b7364c07c2dd6f4a97b318", + "started_at": 1791748111.682272, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12749, + "output_tokens": 146 + } + }, + "reason": "observation is stale or foreign; no requested effect dispatched" + }, + { + "call": 4, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-04", + "decision": { + "action": "move", + "intent": "Push the block rightward until its center is securely inside the measurement region.", + "target_x": 0.01, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "b5090ea13b11819d9bb20df20761d158f9fc0371a22b0536b64ae9c75c199ad9", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.509735166095197, + "exit_code": 0, + "image_sha256": "b5090ea13b11819d9bb20df20761d158f9fc0371a22b0536b64ae9c75c199ad9", + "outcome": "decision", + "pid": 52590, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "b9bb9447f22ada733ca7f89cb5eb7331277e8e6097006242db95c5203cc90e31", + "started_at": 1791748118.5814898, + "usage": { + "cached_input_tokens": 10752, + "input_tokens": 13246, + "output_tokens": 213 + } + }, + "reason": "" + }, + { + "call": 5, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-05", + "decision": { + "action": "move", + "intent": "Retreat the pusher left to clear contact before measuring.", + "target_x": -0.03, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "9558a7e6223404d532dbe2fd33ea8423ca309bdbed0426b5f78ddb756dfb3dc0", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 8.427244249964133, + "exit_code": 0, + "image_sha256": "9558a7e6223404d532dbe2fd33ea8423ca309bdbed0426b5f78ddb756dfb3dc0", + "outcome": "decision", + "pid": 53100, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "c20289e474af81f88587306e8a49a38a28da12890457c78e185655845ccc2f48", + "started_at": 1791748126.5655649, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12902, + "output_tokens": 220 + } + }, + "reason": "" + }, + { + "call": 6, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-06", + "decision": { + "action": "measure", + "intent": "Measure now that the block center is inside the target region and the pusher is clear.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "9cb34f24c00f6647c2c9763ca17e4d7189620c96ff40059736b25ff0d67d3dcc", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 8.417126499931328, + "exit_code": 0, + "image_sha256": "9cb34f24c00f6647c2c9763ca17e4d7189620c96ff40059736b25ff0d67d3dcc", + "outcome": "decision", + "pid": 53612, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "25b53c6f4be7e4ce1f606a3585c499d375187b734036d41386e6003ca7ba71f0", + "started_at": 1791748135.447528, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12971, + "output_tokens": 145 + } + }, + "reason": "" + }, + { + "call": 7, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-07", + "decision": { + "action": "begin_reset", + "intent": "Begin resetting after the verified measurement.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "9cb34f24c00f6647c2c9763ca17e4d7189620c96ff40059736b25ff0d67d3dcc", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 4.853458332945593, + "exit_code": 0, + "image_sha256": "9cb34f24c00f6647c2c9763ca17e4d7189620c96ff40059736b25ff0d67d3dcc", + "outcome": "decision", + "pid": 54129, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "80136e11af46f16f2c9bec183a4089c6f16ecbe69116813d6d13d56e46356d6e", + "started_at": 1791748144.692519, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13041, + "output_tokens": 36 + } + }, + "reason": "" + }, + { + "call": 8, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-08", + "decision": { + "action": "move", + "intent": "Enter the upper bypass lane before crossing to the block's right side.", + "target_x": -0.03, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "9cb34f24c00f6647c2c9763ca17e4d7189620c96ff40059736b25ff0d67d3dcc", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 5.961126709007658, + "exit_code": 0, + "image_sha256": "9cb34f24c00f6647c2c9763ca17e4d7189620c96ff40059736b25ff0d67d3dcc", + "outcome": "decision", + "pid": 54630, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "f389429f86fdf134e081e4ecf8755c68bbec9c3bc8580d90c4faf1039a6e56d0", + "started_at": 1791748150.1803892, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13030, + "output_tokens": 154 + } + }, + "reason": "" + }, + { + "call": 9, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-09", + "decision": { + "action": "move", + "intent": "Cross the upper bypass lane to position the pusher safely on the block's right side.", + "target_x": 0.13, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "7aace630613576b58a4a90ab5c31f59a361dcaf33122917a4ecea5b0699de0b3", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 9.262546166894026, + "exit_code": 0, + "image_sha256": "7aace630613576b58a4a90ab5c31f59a361dcaf33122917a4ecea5b0699de0b3", + "outcome": "decision", + "pid": 55230, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "484495d5c496d831f25d497c04c8ae1b75aab4654e0686e0d49b3f3ea9dd4e11", + "started_at": 1791748156.7521942, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13032, + "output_tokens": 194 + } + }, + "reason": "" + }, + { + "call": 10, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-10", + "decision": { + "action": "move", + "intent": "Descend from the bypass lane to align safely on the block's right side.", + "target_x": 0.13, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "7cde902d68c7974bbf1fef5aa77c002f75c09b1f1bf2af83e75656a30212a995", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.034838500083424, + "exit_code": 0, + "image_sha256": "7cde902d68c7974bbf1fef5aa77c002f75c09b1f1bf2af83e75656a30212a995", + "outcome": "decision", + "pid": 55743, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "47eb58a72576319e857f0209c0e2fbbb429b570997c6059ea5213479c5ef2181", + "started_at": 1791748166.634509, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13026, + "output_tokens": 218 + } + }, + "reason": "" + }, + { + "call": 11, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-11", + "decision": { + "action": "move", + "intent": "Push the block left from its right side until its center reaches the home zone.", + "target_x": -0.065, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "a37ce97b1710cb0bdee031f6bfebee784c4f376e2f43a1d9b88883cf7a0b2a22", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.248244917020202, + "exit_code": 0, + "image_sha256": "a37ce97b1710cb0bdee031f6bfebee784c4f376e2f43a1d9b88883cf7a0b2a22", + "outcome": "decision", + "pid": 56253, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "fc697fc9959c09ee32f3171b63f643866e368fcca13632a8fa174052c5bd9d69", + "started_at": 1791748174.340254, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13027, + "output_tokens": 186 + } + }, + "reason": "" + }, + { + "call": 12, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-12", + "decision": { + "action": "move", + "intent": "Retreat slightly right to release contact before entering the bypass lane.", + "target_x": -0.03, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "7b672da1a7c7fc13edb2fce73f7849a65de4fd59fd4d5fbee7b777ae250ff951", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 6.222484749974683, + "exit_code": 0, + "image_sha256": "7b672da1a7c7fc13edb2fce73f7849a65de4fd59fd4d5fbee7b777ae250ff951", + "outcome": "decision", + "pid": 56766, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "cabc9d7bf03d68b5ed1dacf38a501e8f59a73fa3174c1704aed8e10129d0f976", + "started_at": 1791748181.269278, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13031, + "output_tokens": 177 + } + }, + "reason": "" + }, + { + "call": 13, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-13", + "decision": { + "action": "move", + "intent": "Enter the upper bypass lane before returning the pusher to its home position.", + "target_x": -0.03, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "ddd63e8e81f9bda9ae7aa090d918e00f57a11698fbd5de91678b5571b58e449e", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 7.362335458979942, + "exit_code": 0, + "image_sha256": "ddd63e8e81f9bda9ae7aa090d918e00f57a11698fbd5de91678b5571b58e449e", + "outcome": "decision", + "pid": 57270, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "b999d7bcf66542d894b3956c663771fd5e9db9b98215eb124dc34b3ddab1ed50", + "started_at": 1791748188.1343849, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13448, + "output_tokens": 172 + } + }, + "reason": "" + }, + { + "call": 14, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-14", + "decision": { + "action": "move", + "intent": "Cross the upper bypass lane to align above the pusher home position.", + "target_x": -0.3, + "target_y": 0.135 + }, + "exact_image": true, + "image_sha256": "1adaf7fe8dfee1cd5b5d2555f77541dc5fd312d5af140b3e710e69f3c3723568", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 5.290408417000435, + "exit_code": 0, + "image_sha256": "1adaf7fe8dfee1cd5b5d2555f77541dc5fd312d5af140b3e710e69f3c3723568", + "outcome": "decision", + "pid": 57777, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "35aab4696cafad1fb8b4b3800f757cd8db0b9f155c0a307831e8eeda92c37041", + "started_at": 1791748196.1089122, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13045, + "output_tokens": 124 + } + }, + "reason": "" + }, + { + "call": 15, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-15", + "decision": { + "action": "move", + "intent": "Descend from the bypass lane to place the pusher at its home position.", + "target_x": -0.3, + "target_y": 0.0 + }, + "exact_image": true, + "image_sha256": "77c82130db457b8ca7683d63d7869756967ad36785728429d25e99bc489b7b64", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 4.851276458008215, + "exit_code": 0, + "image_sha256": "77c82130db457b8ca7683d63d7869756967ad36785728429d25e99bc489b7b64", + "outcome": "decision", + "pid": 58281, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "60c2e07a3a346f67ac5f2abd81660440a8120b2da58976a777abc53ae08c358d", + "started_at": 1791748202.050539, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13045, + "output_tokens": 109 + } + }, + "reason": "" + }, + { + "call": 16, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim8-qualified-20261011-v1/calls/dcbe6123-1fc6-4442-bea9-e75b683c1f68-16", + "decision": { + "action": "finish_reset", + "intent": "Verify the block center and pusher are both at their home positions.", + "target_x": null, + "target_y": null + }, + "exact_image": true, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "input_has_no_private_pose": true, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "outcome": "executed", + "process": { + "cli_version": "codex-cli 0.147.0", + "elapsed_s": 5.12791329191532, + "exit_code": 0, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "outcome": "decision", + "pid": 58785, + "process_exit_observed": true, + "provider": "codex_cli", + "request_sha256": "e3916fe8fc35c920638457603be4cdca82c8d239dcd4980ca95e2700962edb65", + "started_at": 1791748207.581609, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13043, + "output_tokens": 87 + } + }, + "reason": "" + } + ], + "completed_cycles": 2, + "current_status": { + "active_job": null, + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "diagnostic_enabled": true, + "generation": 23, + "jobs": { + "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef": { + "completed_generation": 10, + "completed_native_time_s": 21.81399999999879, + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "measurement_operation_id": "agent-afd48d867d2346f4a01d3659c4aec982", + "measurement_receipt": { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.533999999999613, + "operation_id": "agent-afd48d867d2346f4a01d3659c4aec982", + "source_kind": "mujoco_simulation", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3", + "version": 1, + "worker_pid": 45916 + }, + "moves": 10, + "phase": "completed", + "reset_receipt": { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "kind": "finish_reset", + "native_time_s": 21.81399999999879, + "operation_id": "agent-a52f04d4553c46fa8023896307981195", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8140, + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + }, + "reset_started_generation": 2, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "dcbe6123-1fc6-4442-bea9-e75b683c1f68": { + "completed_generation": 23, + "completed_native_time_s": 45.86000000001008, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "measurement_operation_id": "agent-ab389fc3848a4fc5a17b18e1fe931de0", + "measurement_receipt": { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "generation": 15, + "image_sha256": "9cb34f24c00f6647c2c9763ca17e4d7189620c96ff40059736b25ff0d67d3dcc", + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "measurement": { + "block_box": [ + 336, + 159, + 378, + 201 + ], + "block_center_xy": [ + 0.06166666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 356.0365217391304, + 179.5304347826087 + ], + "footprint_area_m2": 0.004791666666666666, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1725 + }, + "native_time_s": 29.845999999994337, + "operation_id": "agent-ab389fc3848a4fc5a17b18e1fe931de0", + "source_kind": "mujoco_simulation", + "state_sha256": "99f7e539e079b784ca4c8e530b34d3f5a96514d0c661f2e137c66c34cb2d19f4", + "version": 1, + "worker_pid": 54121 + }, + "moves": 12, + "phase": "completed", + "reset_receipt": { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "block_home_verified": true, + "generation": 23, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "kind": "finish_reset", + "native_time_s": 45.86000000001008, + "operation_id": "agent-86a32a4e78e64aaf9322558f4d9d0853", + "pusher_home_verified": true, + "reset_contact_samples": 3112, + "reset_native_steps": 8007, + "state_sha256": "ab359450848224f47ced5d9c79542dc8a733034a8d40053381de7c7569fd3115" + }, + "reset_started_generation": 15, + "started_generation": 10, + "started_native_time_s": 21.81399999999879, + "target_zone": { + "x_max": 0.115, + "x_min": 0.045, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 45.86000000001008, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "fault_chain": { + "after": { + "block_center_in_zone": false, + "block_xyz": [ + 0.03101368936485668, + -2.3636102442079535e-09, + 0.03499824550643647 + ], + "external_force_cleared": true, + "generation": 13, + "native_time_s": 27.375999999995706, + "pusher_home": false, + "pusher_xyz": [ + -0.023983540317720375, + 1.7203059752249304e-17, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "c231f845db47170cb50d5a98b52fd2304ff320967fd89fae6c21dab71ac24936" + }, + "before": { + "block_center_in_zone": true, + "block_xyz": [ + 0.07079077670575931, + -2.363612923572088e-09, + 0.03499573045536986 + ], + "external_force_cleared": true, + "generation": 12, + "native_time_s": 26.21599999999635, + "pusher_home": false, + "pusher_xyz": [ + -0.019999970650332433, + -3.5065733542877396e-26, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "e9a6f4f2aac925aa4018c27cbb9dfd3fb75852b71e68e8a13d1221605eae01ae" + }, + "displacement_m": 0.039777087420414255, + "force": { + "receipt": { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "force_x_n": -1.5, + "forced_steps": 80, + "generation": 13, + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "kind": "diagnostic_external_force", + "native_steps": 580, + "native_time_s": 27.375999999995706, + "operation_id": "disturb-ae433d5c49634285847f2c50aa67355b", + "state_sha256": "c231f845db47170cb50d5a98b52fd2304ff320967fd89fae6c21dab71ac24936" + }, + "request": { + "force_x_n": -1.5, + "kind": "disturb", + "steps": 80 + } + }, + "fresh_model_move_bound_to_force_image": true, + "immutable_chain_bound": true, + "no_measurement_effect": true, + "passed": true, + "saved_proposal_id": "proposal-d76c7e8666f8478baf4ce0cf6174f8a4", + "stale_call": 3 + }, + "fresh_model_move_after_refusal": true, + "model_boundaries_passed": true, + "passed": false, + "qualification_version": 1, + "scope": "separate post-trial native/SQLite and retained model-boundary scoring; browser evidence recorded separately", + "scores": [ + { + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "label": "Baseline inspection", + "measurement": { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.533999999999613, + "operation_id": "agent-afd48d867d2346f4a01d3659c4aec982", + "source_kind": "mujoco_simulation", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3", + "version": 1, + "worker_pid": 45916 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06582868246696554, + -8.393868978677625e-11, + 0.034995730455369786 + ], + "external_force_cleared": true, + "generation": 2, + "native_time_s": 5.533999999999613, + "pusher_home": false, + "pusher_xyz": [ + -0.049999969408768885, + 8.057882667829012e-26, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3" + }, + "model_calls": 14, + "motion_count": 10, + "passed": true, + "reset": { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "kind": "finish_reset", + "native_time_s": 21.81399999999879, + "operation_id": "agent-a52f04d4553c46fa8023896307981195", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8140, + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11582763630912812, + -2.3633644648884637e-09, + 0.03499573045536985 + ], + "external_force_cleared": true, + "generation": 10, + "native_time_s": 21.81399999999879, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + } + }, + { + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "label": "Recovery inspection", + "measurement": { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "generation": 15, + "image_sha256": "9cb34f24c00f6647c2c9763ca17e4d7189620c96ff40059736b25ff0d67d3dcc", + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "measurement": { + "block_box": [ + 336, + 159, + 378, + 201 + ], + "block_center_xy": [ + 0.06166666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 356.0365217391304, + 179.5304347826087 + ], + "footprint_area_m2": 0.004791666666666666, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1725 + }, + "native_time_s": 29.845999999994337, + "operation_id": "agent-ab389fc3848a4fc5a17b18e1fe931de0", + "source_kind": "mujoco_simulation", + "state_sha256": "99f7e539e079b784ca4c8e530b34d3f5a96514d0c661f2e137c66c34cb2d19f4", + "version": 1, + "worker_pid": 54121 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.060828133227863616, + -2.363604036670241e-09, + 0.03499573045536984 + ], + "external_force_cleared": true, + "generation": 15, + "native_time_s": 29.845999999994337, + "pusher_home": false, + "pusher_xyz": [ + -0.02999997064612875, + -6.834842985587421e-26, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "99f7e539e079b784ca4c8e530b34d3f5a96514d0c661f2e137c66c34cb2d19f4" + }, + "model_calls": 16, + "motion_count": 12, + "passed": true, + "reset": { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "block_home_verified": true, + "generation": 23, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "kind": "finish_reset", + "native_time_s": 45.86000000001008, + "operation_id": "agent-86a32a4e78e64aaf9322558f4d9d0853", + "pusher_home_verified": true, + "reset_contact_samples": 3112, + "reset_native_steps": 8007, + "state_sha256": "ab359450848224f47ced5d9c79542dc8a733034a8d40053381de7c7569fd3115" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11577112750257858, + -5.9658799447144094e-09, + 0.03499573045536983 + ], + "external_force_cleared": true, + "generation": 23, + "native_time_s": 45.86000000001008, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "ab359450848224f47ced5d9c79542dc8a733034a8d40053381de7c7569fd3115" + } + } + ], + "source_commit": "e7b716a8221db6b69c876dde030235f59d643460", + "source_unchanged": false, + "sqlite": { + "count": 2, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "operation_id": "agent-afd48d867d2346f4a01d3659c4aec982", + "receipt": { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "089965b9-90b8-4bc3-a4a0-71bcfc9bbeef", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.533999999999613, + "operation_id": "agent-afd48d867d2346f4a01d3659c4aec982", + "source_kind": "mujoco_simulation", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3", + "version": 1, + "worker_pid": 45916 + }, + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3" + }, + { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "generation": 15, + "image_sha256": "9cb34f24c00f6647c2c9763ca17e4d7189620c96ff40059736b25ff0d67d3dcc", + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "operation_id": "agent-ab389fc3848a4fc5a17b18e1fe931de0", + "receipt": { + "bench_id": "809a7e27db014f759e4e323e83dff8a7", + "generation": 15, + "image_sha256": "9cb34f24c00f6647c2c9763ca17e4d7189620c96ff40059736b25ff0d67d3dcc", + "job_id": "dcbe6123-1fc6-4442-bea9-e75b683c1f68", + "measurement": { + "block_box": [ + 336, + 159, + 378, + 201 + ], + "block_center_xy": [ + 0.06166666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 356.0365217391304, + 179.5304347826087 + ], + "footprint_area_m2": 0.004791666666666666, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1725 + }, + "native_time_s": 29.845999999994337, + "operation_id": "agent-ab389fc3848a4fc5a17b18e1fe931de0", + "source_kind": "mujoco_simulation", + "state_sha256": "99f7e539e079b784ca4c8e530b34d3f5a96514d0c661f2e137c66c34cb2d19f4", + "version": 1, + "worker_pid": 54121 + }, + "state_sha256": "99f7e539e079b784ca4c8e530b34d3f5a96514d0c661f2e137c66c34cb2d19f4" + } + ] + }, + "stale_measure_refused": true, + "usage": { + "calls": 30, + "input_tokens": 376278, + "output_tokens": 4603, + "reported_calls": 29 + }, + "version": 1 +} diff --git a/jevdual/results/bench-operator-20261011/v1/score-attempt1.json b/jevdual/results/bench-operator-20261011/v1/score-attempt1.json new file mode 100644 index 0000000..8fbc17c --- /dev/null +++ b/jevdual/results/bench-operator-20261011/v1/score-attempt1.json @@ -0,0 +1 @@ +{"outcome": "refused", "reason": "another process owns this application directory", "no_score_created": true, "next": "close owned UI/server before independent read-only scoring"} \ No newline at end of file diff --git a/jevdual/results/bench-operator-20261011/v1/score-attempt2.json b/jevdual/results/bench-operator-20261011/v1/score-attempt2.json new file mode 100644 index 0000000..46ef899 --- /dev/null +++ b/jevdual/results/bench-operator-20261011/v1/score-attempt2.json @@ -0,0 +1 @@ +{"outcome": "refused", "reason": "PNG read used 64 KiB protocol-data bound; no score written", "next": "preregistered scorer repair with 1 MiB image bound"} \ No newline at end of file diff --git a/jevdual/results/bench-operator-20261011/v1/source-freeze.json b/jevdual/results/bench-operator-20261011/v1/source-freeze.json new file mode 100644 index 0000000..f6c8ece --- /dev/null +++ b/jevdual/results/bench-operator-20261011/v1/source-freeze.json @@ -0,0 +1,22 @@ +{ + "commit": "e7b716a8221db6b69c876dde030235f59d643460", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/bench_operator.html": "f91275d998dc528b7701086def6007d78b30025660bf5076678e23bf63dcbf54", + "tools/physical-workcell/bench_operator.py": "dad93159cad4e9cd8c4d2780af5c495951af9b70f6405f25ca6ed696e6662024", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "40fe73e01d97ee1e5cfdb813650b11edadbea29534784ab10cc1ef4e7f4d5418", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/operator_runtime.py": "1960a526b47c95d9abdefb92388adacc4d6447706782e10aad61f4b49bf304a3", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_operator.py": "d4846a5d4741f67f1041b6588363c959cb02507638914ea8e6922b7745380b25", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/consolidation-20261014/opus-review.md b/jevdual/results/consolidation-20261014/opus-review.md new file mode 100644 index 0000000..897f97c --- /dev/null +++ b/jevdual/results/consolidation-20261014/opus-review.md @@ -0,0 +1,128 @@ +Read-only static review by Claude Opus 5.5 of target57ed816 againstbase268f0fc. No tools or historical evidence reads; one paid invocation, no follow-up paid review. The review preceded completion of current CI and is preserved as given. + +# Review of Footwork consolidation 57ed816 against base 268f0fc + +**Verdict:** I found no code-level blocker in the shipped product path or in the two repairs you asked me to inspect. The merge still depends on three things the diff can't settle: + +1. The full physical rerun of 314 tests has not finished. The original failures were environmental, so the repaired suite should pass, including on Linux CI, before you merge. +2. Commits 445a348 and d931559 must stay reachable after the merge (finding 1). +3. Before publishing the prerelease, scan the exact ZIP 8dc81c… for personal data (finding 5). This gates publication, not the merge. + +I had no tools, so everything below is static reading of the diff you pasted. + +## The two repairs you asked about + +**Controller socket (`tools/physical-workcell/controller.py`, `socket_path`).** The repair is sound: +- **Path length:** the worst case is about `/private/tmp/fw-c-4294967295/<64 hex>.sock`, roughly 99 bytes, under the 104-byte limit. +- **Ancestor checks:** every ancestor must be owned by root or the user, and any group/world-writable one must be sticky. Ancestors are checked with `lstat`, so symlinked parents are refused. +- **Private directory:** it is created with mode 0700, never chmod'ed or repaired, and a symlink race is refused. +- **Uniqueness:** the full SHA-256 of the control path is the socket name, so different checkouts get different sockets. +- **Stale sockets:** cleanup happens only after a refused probe, and only while the control lock is held. +- **Client side:** the client re-checks the socket's owner and mode. +- **Tests:** the 10 new `SocketPathTests` account for 304 → 314 tests. + +**Frozen wheel verification (`build_onboarding_packet.verify_committed_wheel`, shared by `build_release_candidate`).** Rebuilding the source tree from commit blobs and reusing the existing `verify_wheel` is the right design. The test shows a later runtime change no longer moves the check. Hardening points are in findings 6–8. + +## Findings + +### 1. Medium–High (integration): qualification commits may become unreachable after merge +- **Where:** `build_release_candidate.py` (`QUALIFIED_PRODUCT_COMMIT`, `committed_bytes`) and `build_onboarding_packet.verify_committed_wheel`. +- **Trigger:** + - Both verifiers run `git ls-tree` and `git show` against 445a348 and d931559. + - If the PR is squash- or rebase-merged and the branch deleted, any commit that exists only on this branch becomes unreachable. + - Fresh clones won't fetch it, and local `gc` can eventually delete it. + - From then on, `--verify --source-commit d931559` fails everywhere, and the approved ZIP can no longer be re-verified from the repo. +- **Fix:** + - Use a true merge commit, or + - Push annotated tags (for example `qualified-product/445a348` and `release-candidate/d931559`) before deleting the branch. + - Either way, add a one-line check after merge: `git merge-base --is-ancestor origin/main` (or confirm the tag resolves). + +### 2. Medium (qualification claims): installs from the release bundle record the docs commit as `source_commit` +- **Where:** `capability_clean_install.source_commit`, also used by `capability_installed_trial` and `capability_installed_agent_trial`. +- **Trigger:** + - Inside the release bundle, `footwork/MANIFEST.json` has `source_commit` set to the release commit (d931559). + - The product commit (445a348) appears only in `qualification.json`. + - So any receipt produced from the bundle, including the pending README-only Codex adoption, records d931559 as its provenance. + - That clashes with `approved_summary`/inventory, which require the agent and guest summaries to carry 445a348. + - Comparing the adoption receipt with the existing evidence will either fail or need hand-waving. +- **Fix:** the ZIP is frozen, so don't change the helper. Score adoption evidence by the release ZIP's SHA-256 plus `wheel_sha256`, and treat the receipt's `source_commit` as "bundle manifest commit". State this in the adoption scoring plan before collecting evidence. For future bundles, add a `product_source_commit` field. + +### 3. Medium (incorrect evidence): the order-handoff qualifier checks the old socket path and doesn't enforce a frozen source +- **Where:** `tools/physical-workcell/qualify_order_handoff.py`. +- **Trigger 1:** `Fixture.cleanup` reports `socket_exists` against `BASE/'output/.s'/.sock`. After the repair that path never exists, so the field is always `False` and proves nothing. +- **Fix 1:** use `json.load(control/"controller-binding.json")["socket_path"]`, or `controller.socket_path(control)`. +- **Trigger 2:** `main()` compares only the `HEAD` string and hashes the files. Unlike `qualify_bench`, `qualify_network_body` and the others, it never checks `git show : == current bytes`. A dirty worktree can therefore be qualified under a clean commit ID. +- **Fix 2:** reuse the freeze pattern from `qualify_network_body.freeze` over `SOURCE_FILES`. + +### 4. Medium (claim accuracy): earlier controller and order-handoff results no longer describe current source +- **Trigger:** + - `controller.py` changed in this PR. + - `app_handoff.fingerprints`, `order_app.SOURCES`, `qualify_order_handoff.SOURCE_FILES` and the controller binding (`controller_sha256`, `socket_path`) all include it. + - Historical run directories will refuse to load, and any doc saying the controller/handoff simulation "qualified" now refers to pre-repair bytes. +- **Fix:** in the docs, label those results "qualified at ; post-repair: conformance tests only (28 controller + 15 agent targeted)". Don't present them as qualifying the head commit. + +### 5. Medium (privacy; gates the outward publish): sanitization leaves some identity data in place +- **Where:** `build_release_candidate.sanitized_legacy_json` and the `REPORTS`/`EVIDENCE` copies. +- **Trigger:** + - The URL branch keeps `file:///Users//...` unchanged. TextEdit `AXDocument` values are file URLs, so these can appear in evidence. + - `report.md` files and the two `*-summary.json` files are copied into the public ZIP without any sanitization. +- **Fix, without changing bytes:** before publishing, list and grep the exact ZIP 8dc81c… for `/Users/`, `/home/`, `file://`, `@`, `.local` and the account name. +- **If anything matches:** the bytes can't change without requalification, so treat it as a publication decision. +- **For future builds:** exempt only `http`/`https` URLs, sanitize the path part of `file:` URLs, and sanitize reports and summaries the same way. + +### 6. Low: verified bytes and packaged bytes come from separate reads +- **Where:** + - `build_onboarding_packet.inventory` reads the wheel three times: once for verification, once for the member data, once for `wheel_sha256`. + - `build_release_candidate.qualification_packet` runs `read_archive(path)` and then `sha(path.read_bytes())` separately. +- **Trigger:** a concurrent writer changes the file between reads, so different bytes are verified than are packaged. +- **Fix:** read once into memory, then hash, verify and parse that buffer (`zipfile.ZipFile(io.BytesIO(data))`). + +### 7. Low: the onboarding packet verifier is weaker than the release verifier +- **Where:** `build_onboarding_packet.verify`. +- **Trigger:** member mode bits (a symlink `external_attr`), directory entries and uncompressed size are not checked. A repacked packet with identical member bytes but symlink modes passes this verifier. The guest-side extracted check and the release `read_archive` both catch it. +- **Fix:** reuse `build_release_candidate.read_archive`, or move it into a shared module. + +### 8. Low: wheel source reconstruction uses porcelain `git show` and never checks the object type +- **Where:** `verify_committed_wheel` and `committed_bytes`. +- **Fix:** + - Run `git rev-parse --verify ^{commit}` first, so a tree SHA can't be presented as a "commit". + - Read blobs with `git cat-file blob ` so output is never affected by `show` behaviour or configuration. + - Pass `-c core.quotePath=off` or keep `-z` everywhere; the docs/helpers `ls-tree` call is not `-z`. + +### 9. Low: the measurement database can end up permanently unreadable +- **Where:** `measurement.run`. +- **Trigger:** `sqlite3.connect` creates the file with umask permissions, and `chmod 0600` runs only after the insert. A crash in between leaves a 0644 database, which `read_result` rejects forever, so the bench stays unknown. +- **Fix:** before connecting, pre-create the file with `os.open(path, O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, 0o600)` if it is absent. + +### 10. Low: planner cleanup has two edge cases +- **Where:** `planner_guard.supervise` and `codex_policy.terminate`. +- **Trigger 1:** a descendant that escapes the planner's process group (via `setsid`) is observed but never signalled. Closure proof is then withheld, and every later restart refuses. + - **Fix 1:** send SIGKILL to observed processes whose identity (pid, uid, start time) still matches, then re-check. +- **Trigger 2:** `terminate()` sends SIGKILL to the guardian after 10 s. The guardian's own worst-case cleanup is about 3 + 3 + 3 s, so it can be killed before writing its closure record. That fails closed, but loses the call. + - **Fix 2:** raise the host's escalation timeout to about 15 s. + +### 11. Low: a non-object proposal crashes `app_handoff submit` with a traceback +- **Where:** `app_handoff.submit`. +- **Trigger:** if the JSON proposal is not an object, `proposal.get` raises `AttributeError`, which the CLI's `main()` doesn't catch. +- **Fix:** call `protocol.fields`, or add an `isinstance(proposal, dict)` check right after decoding. + +### 12. Low (documentation friction for the README-only run) +- **Where:** `installed-agent-host.md` step 1. +- **Trigger:** `capability_clean_install` refuses any run directory outside `/output`. The doc only says "a new output directory". +- **Fix:** for future docs, state the location explicitly. For the pending adoption run, expect this friction and record it rather than coaching around it. + +### 13. Informational (fail-closed by design) +- **`capability_*` `source_commit`:** with Git, it records `HEAD` without checking whether the tree is dirty. The wheel SHA-256 remains the binding product identity. +- **`body_network`:** an accepted non-dispatch command with no result (for example `observe`/`start_job` interrupted by a crash) blocks the body permanently, because there is no read-only recovery path. Document this as an operator-reset scenario. +- **`/tmp/fw-c-`:** another local user can pre-create it and cause a refusal (denial of service only). +- **`inbox.source_request`:** it reads the capture after releasing the camera lock and doesn't compare against the receipt's SHA-256. A later `status` catches any mismatch. + +## Not checked (outside this diff, or needs execution) +- **CI:** whether the job's runner matrix includes macOS, where `sudo apt-get` would fail. Whether Linux procps accepts `ps -axo …,lstart=`, which `planner_guard` uses. The Linux EGL rendering path. +- **Release ZIP contents:** + - Whether the bundled 445a348 `capability_fixture.py` writes the `cleanup.json` that the release docs tell users to inspect. + - What the bundled helpers' `source_commit()` actually does (finding 2 assumes it matches this branch). +- **`test_order_app` `retain`:** whether it is exclusive-create. `qualify_order_handoff` rewrites `qualification.json` once per case. +- **`build_capability_wheel.verify`:** its inventory rules (all `.py` files plus `arbiter.toml`) and how they relate to `artifacts.py`/`cli.py` at 445a348 versus the PR head. +- **Agent trial isolation:** whether the trial host (cwd inside the repo, `--setting-sources ""`) loaded the repo's `CLAUDE.md`. Check the init event in the transcript before calling that run independent. +- **Historical evidence:** none of the historical evidence files' contents. diff --git a/jevdual/results/consolidation-20261014/release-publication-scan.json b/jevdual/results/consolidation-20261014/release-publication-scan.json new file mode 100644 index 0000000..ca689ba --- /dev/null +++ b/jevdual/results/consolidation-20261014/release-publication-scan.json @@ -0,0 +1,194 @@ +{ + "artifact_sha256": "8dc81c724bf617a9f3aa4833f250d5124b9e8c5e9a1c504c67d634080f44fd1f", + "text_members_scanned": 104, + "findings": { + "home_path": [ + { + "member": "footwork/qualification.json", + "count": 2 + } + ], + "file_url": [ + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/session.py", + "count": 1 + } + ], + "private_host_name": [], + "account_name": [], + "email": [] + }, + "at_sign_matches": [ + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/arbiter.py", + "count": 6 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/authorize.py", + "count": 3 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/ax.py", + "count": 1 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/bridge.py", + "count": 1 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/browser_driver.py", + "count": 2 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/capability/backend.py", + "count": 3 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/capability/contracts.py", + "count": 2 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/capability/journal.py", + "count": 6 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/capability/observations.py", + "count": 1 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/capability/service.py", + "count": 2 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/carry.py", + "count": 10 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/cli.py", + "count": 4 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/coexist.py", + "count": 8 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/completion.py", + "count": 3 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/contract.py", + "count": 2 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/crossloop.py", + "count": 14 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/decisions.py", + "count": 7 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/delivery.py", + "count": 3 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/desktop.py", + "count": 6 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/desktop_s2.py", + "count": 1 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/effects.py", + "count": 2 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/evidence.py", + "count": 2 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/grants.py", + "count": 2 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/ledger.py", + "count": 1 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/menu.py", + "count": 4 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/native.py", + "count": 6 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/patch.py", + "count": 2 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/policy.py", + "count": 5 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/posture.py", + "count": 2 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/progress.py", + "count": 3 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/receipts.py", + "count": 2 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/s1.py", + "count": 3 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/secrets.py", + "count": 2 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/session.py", + "count": 13 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/testing.py", + "count": 3 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/text.py", + "count": 3 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/tools.py", + "count": 4 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/verify.py", + "count": 6 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/visual_checks.py", + "count": 11 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/visual_decisions.py", + "count": 1 + }, + { + "member": "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl!jevdual/visual_evidence.py", + "count": 5 + } + ], + "passed": true, + "scope": "exact outer ZIP and nested wheel text; literal private home/file URL/account/host and email patterns; @ decorators documented separately", + "reviewed_generic_matches": { + "home_path": "Both hits are the same qualification.json explanatory placeholder: Other /Users/ or /home/ roots from evidence content. No identity is present.", + "file_url": "Single hit is generic session.py construction return f\"file://{doc}\". No concrete URL or document path is present." + }, + "identifying_path_or_address_findings": [] +} diff --git a/jevdual/results/consolidation-20261014/remediation.json b/jevdual/results/consolidation-20261014/remediation.json new file mode 100644 index 0000000..a1141d5 --- /dev/null +++ b/jevdual/results/consolidation-20261014/remediation.json @@ -0,0 +1,70 @@ +{ + "reviewed_target": "57ed816a9f30f122e8be6a6635a9f3a96069f2a8", + "review_model": "claude-opus-5-5", + "scope": "static changed implementation and selected tests/docs; no historical artifact reads by reviewer", + "findings": [ + { + "id": 1, + "resolution": "Use a true merge and verify445a348/d931559 ancestry onmain aftermerge; no squash or requiredrefdeletion." + }, + { + "id": 2, + "resolution": "A3 scoring explicitlybinds ZIP+wheeldigests and distinguishes bundlemanifestcommit from qualifiedproductcommit." + }, + { + "id": 3, + "resolution": "Cleanup validates exactsocket location plusinteger version/simulationsourcekind/currentcontrollerhash; mismatches stayunconfirmed. NamedSOURCE_FILES freeze checkscommitbytes beforeeffects. Independentpeer P2resolved." + }, + { + "id": 4, + "resolution": "Physical README labels historicalsourcecommit qualification versus changedheadconformance." + }, + { + "id": 5, + "resolution": "ExactZIPandnestedwheel scan104textmembers; matchesonly genericmetadata pathplaceholders and genericcodefileURLconstruction; noidentifyingpaths/address/account/privatehostname." + }, + { + "id": 6, + "resolution": "Single bounded snapshotbytes feedverify/inventory/hash; mutationregressionspass." + }, + { + "id": 7, + "resolution": "Sharedstrictarchiveparser rejectsunsafe modes/members/sizes; regressionpasses." + }, + { + "id": 8, + "resolution": "Commitobjectsrequired; NULls-tree andcat-fileblob reads; tree/blobrefusals pass." + }, + { + "id": 9, + "resolution": "Exclusive0600 databasecreation beforeSQLiteopen; unsafeexistingfilesrefused without takeover; corruption/history/duplicates tests pass." + }, + { + "id": 10, + "resolution": "Observed escapeddescendants intentionallywithholdclosure andblockrecovery; retainedPIDsignalling wouldbroadenauthority and contradict existingtestcontract. Retainfailclosedbehavior anddocumentit. Guardian grace15s repairs supportedtimeoutcase; newregression failsbefore/passafter." + }, + { + "id": 11, + "resolution": "Nonobjectproposalrefuses beforeguard/controller/effects; CLIboundedrefusal pass." + }, + { + "id": 12, + "resolution": "Immutablebundlefriction notcoachedaround; A3recordsactualadoption attempt and demonstratedfailures before futureartifactrepair." + }, + { + "id": 13, + "resolution": "Informationalexistingfailclosedlimits retained; no automatic unknown-bodyrelease or unobserveddetachmentclaim." + } + ], + "gate": "All actionable scoped review findings repaired or explicitly resolved within existingauthority. CurrenthostedCIand true-mergeancestry checks pending; A3user8GiBRAMgate pending.", + "verification": { + "source_python": "1594passed27skipped12browserdeselected,80.28s", + "physical_simulation": "330passed269.527s at0e5a59a beforefinal2produceridentitytests", + "packet_release_targeted": "44passed; exact205577and8dcverify", + "simulation_boundary_targeted": "42passed afterfinalproduceridentityrepair; two newregressions before2fail after10pass", + "fixture_native": "56passed", + "fixture_linux_import_simulation": "55passed1existingmacgateSkipped; actualLinuxCIpending", + "review_model_cost_usd": 3.615095, + "review_call_count": 1 + } +} diff --git a/jevdual/results/desk-inbox-20261010/environment.json b/jevdual/results/desk-inbox-20261010/environment.json new file mode 100644 index 0000000..04c37a6 --- /dev/null +++ b/jevdual/results/desk-inbox-20261010/environment.json @@ -0,0 +1,14 @@ +{ + "macos_version": "26.5.1", + "machine": "arm64", + "python_version": "3.14.5", + "ocr": "Apple Vision via installed Swift; actual request revision captured by helper", + "reference_seams": [ + "/Volumes/External SSD/referencerepos/cua/libs/lume/src/Utils/VNCDriver.swift:272", + "/Volumes/External SSD/referencerepos/rerun/examples/python/live_camera_edge_detection/live_camera_edge_detection.py" + ], + "source_references": [ + "https://developer.apple.com/documentation/vision/recognizing-text-in-images", + "https://developer.apple.com/documentation/imageio/cgimagepropertyorientation/upmirrored" + ] +} diff --git a/jevdual/results/desk-inbox-20261010/fixtures.json b/jevdual/results/desk-inbox-20261010/fixtures.json new file mode 100644 index 0000000..a640d17 --- /dev/null +++ b/jevdual/results/desk-inbox-20261010/fixtures.json @@ -0,0 +1,30 @@ +{ + "source_kind": "synthetic_fixture", + "generator_sha256": "3f1d8d8d6a369709429f71da3449f9b69fc27dfe644a27be847a7e7d07080d6a", + "images": { + "printed-note.png": "740cfe0c4055553b732356cbee75c7797919fc3f3624d9e8f9459a626609edeb", + "blank.png": "a6c04d1da5a290c892b9aa0a837cdbb19c7015bf5a744512754578c4bc06f2b7", + "normal-calibration.png": "8cf6377c803c75b66500bfa7c1364e852320f8f50becca5e60e04398574f081a", + "mirror-note.png": "c01b5aa2d71bfd4f895af215c6e01eb4baf722a45a0226b175e36ce02faad121" + }, + "expected_lines": { + "printed-note.png": [ + "Buy coffee", + "Measure desk", + "Fix camera" + ], + "normal-calibration.png": [ + "LEFT 123 RIGHT", + "Buy coffee", + "Measure desk" + ], + "mirror-note.png": [ + "LEFT 123 RIGHT", + "Buy coffee", + "Measure desk" + ], + "blank.png": [] + }, + "fixture_directory": "jevdual/output/desk-inbox-verification", + "live_camera_evidence": false +} diff --git a/jevdual/results/desk-inbox-20261010/report.md b/jevdual/results/desk-inbox-20261010/report.md new file mode 100644 index 0000000..aff7309 --- /dev/null +++ b/jevdual/results/desk-inbox-20261010/report.md @@ -0,0 +1,48 @@ +# Camera paper inbox: first desk experiment + +**The live physical-to-digital handoff worked after review.** A deliberately captured handwritten note was recognized locally, corrected in a separate file, confirmed by the user, saved once and independently checked through the real Footwork CLI. The saved note equals all 187 approved bytes. A fresh-process accept/status repeat preserves the same manifest and one note. The original frame and 180-byte raw OCR candidate remain unchanged. + +## What was built + +- `tools/desk-inbox-experiment/`: saved-frame preparation, local Apple Vision OCR, private review HTML, explicit digest-bound acceptance and status. +- `footwork check-artifact DEST --expected-file REVIEWED --expected-sha256 DIGEST`: a provider-free, read-only exact-byte check. It uses no fabricated native/browser Carry and does not grant action or native-completion authority. +- Original capture stays in `tools/camera-capture-experiment/`; the new tool reads acquired receipts without reopening the camera. + +Frozen implementation: `6e93c35`; preregistration: `e62fb47`, mirror amendment: `3e63656`. Source identity and commands are in `source.json`. All 31 focused tests and scoped Ruff passed. Existing CLI/carry tests are included. No provider calls or downloads were needed. + +## Observed evidence + +| Check | Evidence | Result | +| --- | --- | --- | +| Printed note, real Vision | Independently drawn known three-line fixture | Exact text match | +| Normal and mirrored text | Known calibration fixtures; explicit up/upMirrored | Same expected text and candidate digest; source bytes retained | +| Blank image | Real Vision | needs_text; no note; acceptance refused | +| Wrong approval digest | Public accept | Refused before writing a note | +| Tampered saved fixture | Public status and repeat accept | Current unknown, historical save retained; no overwrite/replay | +| Live handwritten note | User-positioned physical paper and actual camera receipt | Acquired, recognized and reviewed | +| Live mirror direction | Readable calibration line in original frame | Not mirrored; explicit up used | +| Saved text | User-confirmed correction and independent public Footwork check | Exact bytes match, one note | +| Restart/repeat | Public fresh-process accept/status and unchanged manifest | Existing note reused; raw OCR preserved | + +Private live output is under `tools/desk-inbox-experiment/output/live-paper-v1/`; camera output is under `tools/camera-capture-experiment/output/desk-paper-v1/`. Images, OCR, candidate/corrected text and note contents are ignored and excluded from this report. `verification.json` records hashes, byte counts, checks and limits. Fixture identities are in `fixtures.json`; fixtures prove synthetic behavior rather than human handwriting quality. + +## What the handwriting test showed + +Raw OCR contained missing spaces, casing/punctuation differences and word substitutions. It did not match the user's note cleanly. The separate corrected text was explicitly confirmed before saving, with the original header retained. This qualifies a reviewed workflow on one note, not autonomous transcription or calibrated handwriting accuracy. + +The helper uses [Apple Vision text recognition](https://developer.apple.com/documentation/vision/vnrecognizetextrequest) in accurate mode, en-US, with language correction disabled. An explicit --mirror-input chooses [upMirrored orientation](https://developer.apple.com/documentation/imageio/cgimagepropertyorientation/upmirrored); it never rewrites the original PNG or classifies direction from confidence. Lines retain confidence and bounds, which do not establish correctness. + +Local reference seams: Cua `libs/lume/src/Utils/VNCDriver.swift:272` for Vision recognition; Rerun `examples/python/live_camera_edge_detection/` for acquisition/visual inspection. Footwork's existing Carry semantics were preserved because they require real native/browser observations. + +## Readback + +```sh +python3 tools/desk-inbox-experiment/inbox.py status --run-dir tools/desk-inbox-experiment/output/live-paper-v1 +jevdual/.venv/bin/footwork check-artifact tools/desk-inbox-experiment/output/live-paper-v1/inbox/note.txt --expected-file tools/desk-inbox-experiment/output/live-paper-v1/reviewed.txt --expected-sha256 6d4fb067193c2c1f48d3e57c130fee9cf9540651c7b77946e020b13a8e71847c +``` + +## Decision + +Keep the workflow isolated in Footwork's tools directory. The integration proves exact saved-artifact equality to an explicitly reviewed source. It does not establish exposure freshness, authenticated physical page/hardware/review identity, exactly-once physical effects, power-loss recovery or native UI completion. + +Improve capture/review friction next: paper-region selection, a legibility check and word-level review against the image. Compare this with ordinary note entry before adding automated task creation. The technical opportunity is a source-preserving physical observation that can support a later bounded digital action; demand and daily utility remain unvalidated. diff --git a/jevdual/results/desk-inbox-20261010/source.json b/jevdual/results/desk-inbox-20261010/source.json new file mode 100644 index 0000000..367fed1 --- /dev/null +++ b/jevdual/results/desk-inbox-20261010/source.json @@ -0,0 +1,14 @@ +{ + "source_commit": "6e93c35", + "source_sha256": { + "jevdual/python/jevdual/artifacts.py": "f2d52806bef26654aa733fc1a8a02079088a08c2c4d4f05f6594880fc648ef8a", + "jevdual/python/jevdual/cli.py": "a397139e56a7ba576f51344131e859e3a9c2c3fcaad5d04bc46f15a2d66f5d7c", + "tools/desk-inbox-experiment/inbox.py": "13aeee66b25cd11b6a741e6798206c5f154bcb4d1605fd41dd44f6b99776e51b", + "tools/desk-inbox-experiment/recognize.swift": "340d9969f84f4bc892483c2a7fd4786f6bf4a6fef964f9cf6ba9b956389d2b85" + }, + "test_command": "uv run --no-sync pytest tests/test_artifacts.py tests/test_cli.py tests/test_carry.py ../tools/desk-inbox-experiment/test_inbox.py -q", + "test_cwd": "jevdual", + "tests_passed": 31, + "test_exit_code": 0, + "ruff_exit_code": 0 +} diff --git a/jevdual/results/desk-inbox-20261010/verification.json b/jevdual/results/desk-inbox-20261010/verification.json new file mode 100644 index 0000000..8190584 --- /dev/null +++ b/jevdual/results/desk-inbox-20261010/verification.json @@ -0,0 +1,57 @@ +{ + "source_commit": "6e93c35", + "preregistration_commits": [ + "e62fb47", + "3e63656" + ], + "conformance": { + "tests": 31, + "pytest_exit_code": 0, + "ruff_exit_code": 0 + }, + "synthetic_real_vision": { + "printed_three_lines": "exact match", + "normal_calibration_three_lines": "exact match with up", + "mirrored_calibration_three_lines": "exact match with upMirrored; same resulting text digest as normal", + "blank": "needs_text; acceptance refused; no note", + "wrong_approval_digest": "refused before note creation", + "tampered_saved_fixture": "current unknown; historical saved preserved; repeated accept did not overwrite/replay" + }, + "live_paper": { + "source_kind": "native_camera_frame", + "capture_run": "tools/camera-capture-experiment/output/desk-paper-v1", + "desk_run": "tools/desk-inbox-experiment/output/live-paper-v1", + "camera_sha256": "afd69ee24f1252dccbabd9956717106447927db2044ce596f769c53f61a5327c", + "raw_ocr_sha256": "9e86fa8843213b560211b2d9fe43e5983c5de0fc0c10f77fd97880967525dc3d", + "raw_ocr_bytes": 180, + "reviewed_sha256": "6d4fb067193c2c1f48d3e57c130fee9cf9540651c7b77946e020b13a8e71847c", + "reviewed_bytes": 187, + "user_review": "explicitly confirmed corrected transcription; retained original header", + "mirror_observation": "calibration text reads normally in raw frame; no mirror correction used", + "ocr_accuracy": "raw handwritten OCR contained spacing, punctuation/case and word errors; separate correction required", + "state": "saved", + "fresh_process_repeat_accept_status": "same manifest; one note; candidate retained unchanged", + "independent_footwork_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 187, + "expected_file": "/Users/thomasmain/projects/footwork/tools/desk-inbox-experiment/output/live-paper-v1/reviewed.txt", + "expected_sha256": "6d4fb067193c2c1f48d3e57c130fee9cf9540651c7b77946e020b13a8e71847c", + "limit_bytes": 1048576, + "observed_bytes": 187, + "observed_sha256": "6d4fb067193c2c1f48d3e57c130fee9cf9540651c7b77946e020b13a8e71847c", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/desk-inbox-experiment/output/live-paper-v1/inbox/note.txt", + "source_sha256": "6d4fb067193c2c1f48d3e57c130fee9cf9540651c7b77946e020b13a8e71847c" + }, + "changed_mirror_request": "refused without manifest change" + }, + "limits": [ + "No paid provider or robot control", + "No calibrated handwriting accuracy or automatic task execution", + "No authenticated review identity, hardware identity, or exposure freshness", + "Images and note contents stay private/ignored" + ] +} diff --git a/jevdual/results/image-guided-workcell-20261010/environment.json b/jevdual/results/image-guided-workcell-20261010/environment.json new file mode 100644 index 0000000..2ffd085 --- /dev/null +++ b/jevdual/results/image-guided-workcell-20261010/environment.json @@ -0,0 +1,11 @@ +{ + "source_kind": "mujoco_simulation", + "python": "3.12.6", + "mujoco": "3.15.0", + "numpy": "2.5.3", + "work_order_sha256": "4c09d883e84b18c1d47adfc1674bfcaf05c8dd7aa03194f41e0e763df1138b1b", + "planner": "current Codex session views RGB packets and submits structured decisions", + "provider_api_calls": 0, + "physical_hardware_effects": 0, + "external_project_writes": 0 +} diff --git a/jevdual/results/image-guided-workcell-20261010/packet-admission-negative.json b/jevdual/results/image-guided-workcell-20261010/packet-admission-negative.json new file mode 100644 index 0000000..89f3378 --- /dev/null +++ b/jevdual/results/image-guided-workcell-20261010/packet-admission-negative.json @@ -0,0 +1,16 @@ +{ + "source_commit": "f96ac4a", + "mutated_key": "injected_native_oracle", + "public_view": { + "source_kind": "mujoco_simulation", + "phase": "refused", + "reason": "retained planner image packet has invalid fields" + }, + "public_apply": { + "source_kind": "mujoco_simulation", + "phase": "refused", + "reason": "retained planner image packet has invalid fields" + }, + "no_effect_intents_or_dispatch": true, + "injected_field_not_returned": true +} diff --git a/jevdual/results/image-guided-workcell-20261010/report.md b/jevdual/results/image-guided-workcell-20261010/report.md new file mode 100644 index 0000000..e34f4f0 --- /dev/null +++ b/jevdual/results/image-guided-workcell-20261010/report.md @@ -0,0 +1,36 @@ +# SIM2: AI image-guided physical and computer loop + +The final public trial passed on frozen source `f96ac4a`. The current Codex session viewed each native RGB image, identified the orange block, supplied a bounded move proposal, inspected the resulting frame and chose subsequent corrections. A smaller final correction resolved visible placement error before the visually gated local computer action. The wrapper did not choose targets or call a model API. + +## Final trial + +`trial-v2.json` retains source/runtime/driver hashes, frame identities, exact agent proposals and concise feedback, with a reference and digest for the private raw public-CLI trajectory. The run is `tools/physical-workcell/output/image-demo-v2`; six inspected images span generations 0 through 5. Initialization's native pose output was withheld from the planner. The native oracle was separately read after image-guided planning/completion. + +| Criterion | Observed | +|---|---| +| Image-guided actions | Five bounded moves, each chosen after viewing its source frame | +| Before final correction | RGB block center x=0.170833333 m; target center x=0.22 m; completion refused | +| Corrective decision | Smaller 0.05 m commanded-target increment, then a fresh image | +| Final visual placement | RGB center x=0.220833333 m, y=0; conservative center-in-zone check passed | +| Separate native oracle | Block x=0.220743231 m; native center-in-zone predicate passed | +| Modeled force envelope | Peak axis actuator force 1.501413009 N against a 4 N cap | +| Computer effect | One local staged work-order file, then separate public Footwork byte check passed | +| Wrong-object/stale requests | Refused without dispatch | +| Repeated completion/motion | Every run artifact byte unchanged; no second motion or order | +| Injected cached packet | Public view/apply refused extra oracle field; no field returned or effect intent created | + +The image estimate and native oracle are observations in this scene, not a subpixel accuracy claim. Scale is 0.001666667 m per pixel. Completion checks the known block center with a 0.005 m margin; it does not certify an entire object placement or inspection procedure. + +## Verification and version history + +Prediction `fa41529` preceded implementation tests. Version 1 source `fbc9815` also completed an AI-viewed public loop and is preserved in `trial-v1.json`. Source review identified that cached packets could contain extra fields. The amendment and SIM2A prediction were committed in `36c44d1` before the strict-admission tests. Version 2 changed only packet field/scope validation; dynamics, camera, detector thresholds, target intent and motion settings stayed unchanged. The final trial used a deliberate new run and fresh AI decisions, not an upgraded version-1 receipt. + +`verification.json` records 26 passing tests: 13 native renderer/image-route cases and 13 SIM1 native-engine regressions, plus Ruff. Image cases cover malformed/ambiguous/off-frame silhouettes, wrong boxes and motion bounds, premature completion, stale/tampered metadata, lost receipts/partial views, no replay, current software tamper without recreation, filtered planner outputs and evaluator-only oracle agreement. The independent extra-field check is in `packet-admission-negative.json`. + +## Interface and limits + +`image_guided.py view` returns a calibrated native RGB frame and task intent. An image-capable agent supplies a digest-bound proposal to `apply`. The wrapper verifies RGB evidence, exact identity and freshness; the unchanged native controller executes admitted motion. Completion additionally checks native physical state and saved software bytes. The code lives inside `tools/physical-workcell`; no production Footwork model or authority default changed. + +This is an agent-driven simulated camera loop. There is no unattended model runner, real-camera acquisition, robot hardware, provider call or external project write. The RGB checker depends on known orange/blue silhouettes and camera calibration; general recognition, varied scenes/lighting, substantial occlusion and same-color impostors are unqualified. SIM2 guards apply to its own proposal route; the original SIM1 CLI remains available. Root/owner modification of all private state is outside the local receipt consistency contract. + +The next useful experiment is variation in starting position, lighting and viewpoint with separately chosen task cases, followed by real-camera calibration. That extension is proposed, not implemented or qualified here. diff --git a/jevdual/results/image-guided-workcell-20261010/trial-v1.json b/jevdual/results/image-guided-workcell-20261010/trial-v1.json new file mode 100644 index 0000000..3513d3c --- /dev/null +++ b/jevdual/results/image-guided-workcell-20261010/trial-v1.json @@ -0,0 +1,704 @@ +{ + "version": "v1", + "source_commit": "fbc9815", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "planner": "current Codex session, inspected each RGB frame before its next decision; no API calls", + "planner_packets_have_native_poses": false, + "driver_sha256": "bd9647d37f3be6f2f38a86fb3527406fe1503833ed71ad122a29d403e33e8424", + "post_run_native_oracle": { + "block_center_in_zone": true, + "block_xyz": [ + 0.2207798003506426, + -1.7471077498589427e-10, + 0.03499834074306675 + ], + "claims": [ + "native_simulator_state_observation" + ], + "current_software_artifact_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1196, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/expected-order.json", + "expected_sha256": "1fffd9b46917dc6117da3f0138be079cf67fe9db82f3dca4749380944b3ed1df", + "limit_bytes": 1048576, + "observed_bytes": 1196, + "observed_sha256": "1fffd9b46917dc6117da3f0138be079cf67fe9db82f3dca4749380944b3ed1df", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/order.json", + "source_sha256": "1fffd9b46917dc6117da3f0138be079cf67fe9db82f3dca4749380944b3ed1df" + }, + "frame": "sim_world", + "generation": 5, + "historical_software_outcome": "staged", + "order_id": "footwork-image-stage-block-001", + "phase": "completed", + "physical_condition_verified": true, + "pusher_xyz": [ + 0.1657825172065318, + 4.697137197931729e-19, + 0.06 + ], + "simulation_time_s": 9.137999999999721, + "source_kind": "mujoco_simulation", + "state_authority": "current_persisted_simulator_state", + "state_sha256": "274cfa97631a8aaa8b01b9a307d2c15c2f795313407f4f347144a8dc88e1f126", + "target_zone": { + "x_max": 0.26, + "x_min": 0.18, + "y_max": 0.04, + "y_min": -0.04 + } + }, + "independent_software_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1196, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/expected-order.json", + "expected_sha256": "1fffd9b46917dc6117da3f0138be079cf67fe9db82f3dca4749380944b3ed1df", + "limit_bytes": 1048576, + "observed_bytes": 1196, + "observed_sha256": "1fffd9b46917dc6117da3f0138be079cf67fe9db82f3dca4749380944b3ed1df", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/order.json", + "source_sha256": "1fffd9b46917dc6117da3f0138be079cf67fe9db82f3dca4749380944b3ed1df" + }, + "limitations": [ + "clean generated image packets only; extra cached packet fields not yet rejected", + "fixed camera and object colors", + "agent-driven trial, no unattended model runner" + ], + "trajectory_summary": [ + { + "label": "initialize", + "mode": "init", + "reason": "", + "exit_code": 0, + "phase": "ready", + "note": "Native initialization result withheld from planner; raw public CLI result in private trajectory." + }, + { + "label": "image-0", + "mode": "view", + "reason": "", + "exit_code": 0, + "phase": "ready", + "generation": 0, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/vision-frame-000000.png", + "image_sha256": "824f565207bef6625da7fbcbf289ed6cfb50b7f19fc503e1fe8bad51187d7ea0", + "state_sha256": "3a8c7d1b2c047630a067a0590f939c066b6c621349b6021d26609c85b2e4644b", + "fingerprint": { + "base_fingerprints": { + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "810d343cf500526ac884badc31bae095f819a3c3e469a3d4a3155c7a20960d3b", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "state_spec": 16383 + }, + "schema_sha256": "4f632336093c6b9d81741a4deff879b07eea7c9290eb34cbfa75ed84359b90e6", + "settings_sha256": "a4b1820eb44423efa3f5c230b9afa30007ac1608033ffa1aa1594419a3868bea", + "wrapper_sha256": "976484a743fa59afad9489d6e0f349ec3199752450ea4d459d0aa05f49f67624" + } + }, + { + "label": "wrong-object-box", + "mode": "apply", + "reason": "Negative check: identifies the blue pusher instead of the visible orange block.", + "exit_code": 2, + "phase": "refused", + "proposal": { + "block_box": [ + 128, + 138, + 152, + 222 + ], + "decision": "move", + "generation": 0, + "image_sha256": "824f565207bef6625da7fbcbf289ed6cfb50b7f19fc503e1fe8bad51187d7ea0", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "target": { + "x": -0.19, + "y": 0.0 + }, + "token": "wrong-box-001", + "version": 1 + }, + "feedback": { + "reason": "proposed block box does not match independent RGB silhouette evidence" + } + }, + { + "label": "premature-visual-completion", + "mode": "apply", + "reason": "Negative check: the visible orange block is left of the green zone.", + "exit_code": 2, + "phase": "refused", + "proposal": { + "block_box": [ + 226, + 158, + 270, + 202 + ], + "decision": "complete", + "generation": 0, + "image_sha256": "824f565207bef6625da7fbcbf289ed6cfb50b7f19fc503e1fe8bad51187d7ea0", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "token": "early-complete-001", + "version": 1 + }, + "feedback": { + "reason": "fresh RGB block center is outside the conservative inspection zone" + } + }, + { + "label": "approach-1", + "mode": "apply", + "reason": "Image shows a gap between the blue pusher and orange block; approach by 11 cm and inspect a fresh frame.", + "exit_code": 0, + "phase": "executed", + "proposal": { + "block_box": [ + 226, + 158, + 270, + 202 + ], + "decision": "move", + "generation": 0, + "image_sha256": "824f565207bef6625da7fbcbf289ed6cfb50b7f19fc503e1fe8bad51187d7ea0", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "target": { + "x": -0.19, + "y": 0.0 + }, + "token": "move-001", + "version": 1 + }, + "feedback": { + "after_generation": 1, + "after_state_sha256": "3f9c81b61415826a5127bf21026fffb5d4155b58bf7a9dff2c34563105d3327b", + "historical_visual_check": { + "block_box": [ + 227, + 159, + 269, + 201 + ], + "image_center_world_xy": [ + -0.12, + 0.0 + ], + "method": "color_specific_RGB" + }, + "contact_statistics": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 0.38498849079591624, + "native_steps": 867, + "pusher_block_contact_samples": 0 + }, + "replayed": false + } + }, + { + "label": "image-1", + "mode": "view", + "reason": "", + "exit_code": 0, + "phase": "ready", + "generation": 1, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/vision-frame-000001.png", + "image_sha256": "8bbcc9598b0f34a363640dfc8b5d1cce0faeb192676ccff872e8c5372a4390fd", + "state_sha256": "3f9c81b61415826a5127bf21026fffb5d4155b58bf7a9dff2c34563105d3327b", + "fingerprint": "same frozen binding" + }, + { + "label": "repeated-approach", + "mode": "replay", + "reason": "Repeat the identical image-bound move to check that it returns history without advancing physics.", + "exit_code": 0, + "phase": "executed", + "proposal": { + "block_box": [ + 226, + 158, + 270, + 202 + ], + "decision": "move", + "generation": 0, + "image_sha256": "824f565207bef6625da7fbcbf289ed6cfb50b7f19fc503e1fe8bad51187d7ea0", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "target": { + "x": -0.19, + "y": 0.0 + }, + "token": "move-001", + "version": 1 + }, + "feedback": { + "after_generation": 1, + "after_state_sha256": "3f9c81b61415826a5127bf21026fffb5d4155b58bf7a9dff2c34563105d3327b", + "historical_visual_check": { + "block_box": [ + 227, + 159, + 269, + 201 + ], + "image_center_world_xy": [ + -0.12, + 0.0 + ], + "method": "color_specific_RGB" + }, + "contact_statistics": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 0.38498849079591624, + "native_steps": 867, + "pusher_block_contact_samples": 0 + }, + "replayed": false + } + }, + { + "label": "stale-image-generation", + "mode": "apply", + "reason": "Negative check: current image paired with stale generation zero cannot authorize movement.", + "exit_code": 2, + "phase": "refused", + "proposal": { + "block_box": [ + 226, + 158, + 270, + 202 + ], + "decision": "move", + "generation": 0, + "image_sha256": "8bbcc9598b0f34a363640dfc8b5d1cce0faeb192676ccff872e8c5372a4390fd", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "target": { + "x": -0.08, + "y": 0.0 + }, + "token": "stale-001", + "version": 1 + }, + "feedback": { + "reason": "image proposal generation is stale" + } + }, + { + "label": "contact-2", + "mode": "apply", + "reason": "Fresh image shows pusher just left of block and block still far left of green zone; push forward by another bounded increment.", + "exit_code": 0, + "phase": "executed", + "proposal": { + "block_box": [ + 226, + 158, + 270, + 202 + ], + "decision": "move", + "generation": 1, + "image_sha256": "8bbcc9598b0f34a363640dfc8b5d1cce0faeb192676ccff872e8c5372a4390fd", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "target": { + "x": -0.08, + "y": 0.0 + }, + "token": "move-002", + "version": 1 + }, + "feedback": { + "after_generation": 2, + "after_state_sha256": "ae0f44570751e44aa7f2a4ddee84c56068862cc3b6cb30e10e35464583dca9fb", + "historical_visual_check": { + "block_box": [ + 227, + 159, + 269, + 201 + ], + "image_center_world_xy": [ + -0.12, + 0.0 + ], + "method": "color_specific_RGB" + }, + "contact_statistics": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 1.3053232843406164, + "native_steps": 867, + "pusher_block_contact_samples": 2418 + }, + "replayed": false + } + }, + { + "label": "image-2", + "mode": "view", + "reason": "", + "exit_code": 0, + "phase": "ready", + "generation": 2, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/vision-frame-000002.png", + "image_sha256": "f2b40181eea33a16fd5c978bbb41957559d95c52db3821fe8305a282ed6d3420", + "state_sha256": "ae0f44570751e44aa7f2a4ddee84c56068862cc3b6cb30e10e35464583dca9fb", + "fingerprint": "same frozen binding" + }, + { + "label": "feedback-3", + "mode": "apply", + "reason": "Fresh image shows orange block near image center but still about 148 pixels left of the target center; continue with a bounded forward push.", + "exit_code": 0, + "phase": "executed", + "proposal": { + "block_box": [ + 282, + 158, + 326, + 202 + ], + "decision": "move", + "generation": 2, + "image_sha256": "f2b40181eea33a16fd5c978bbb41957559d95c52db3821fe8305a282ed6d3420", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "target": { + "x": 0.03, + "y": 0.0 + }, + "token": "move-003", + "version": 1 + }, + "feedback": { + "after_generation": 3, + "after_state_sha256": "be74c592aab457899905eaa9c639a541ac5fdee2f61eb5f22ccb983dac8a34e0", + "historical_visual_check": { + "block_box": [ + 282, + 159, + 323, + 201 + ], + "image_center_world_xy": [ + -0.029166666666666664, + 0.0 + ], + "method": "color_specific_RGB" + }, + "contact_statistics": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 1.4681925458159029, + "native_steps": 867, + "pusher_block_contact_samples": 2646 + }, + "replayed": false + } + }, + { + "label": "image-3", + "mode": "view", + "reason": "", + "exit_code": 0, + "phase": "ready", + "generation": 3, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/vision-frame-000003.png", + "image_sha256": "e71daf97e86c4bdd3aa48b87d93f0a26920d4a096c83a3a5ae60efa4b5c8d3e6", + "state_sha256": "be74c592aab457899905eaa9c639a541ac5fdee2f61eb5f22ccb983dac8a34e0", + "fingerprint": "same frozen binding" + }, + { + "label": "feedback-4", + "mode": "apply", + "reason": "Fresh image places orange center about 83 pixels left of green center; move pusher forward 10 cm, then reobserve remaining placement error.", + "exit_code": 0, + "phase": "executed", + "proposal": { + "block_box": [ + 347, + 158, + 392, + 202 + ], + "decision": "move", + "generation": 3, + "image_sha256": "e71daf97e86c4bdd3aa48b87d93f0a26920d4a096c83a3a5ae60efa4b5c8d3e6", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "target": { + "x": 0.13, + "y": 0.0 + }, + "token": "move-004", + "version": 1 + }, + "feedback": { + "after_generation": 4, + "after_state_sha256": "6fe0eeb74fc98873c125bb83023e3015359c2976de173fd695a912a91616a594", + "historical_visual_check": { + "block_box": [ + 348, + 159, + 390, + 201 + ], + "image_center_world_xy": [ + 0.08166666666666667, + 0.0 + ], + "method": "color_specific_RGB" + }, + "contact_statistics": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 1.4877311733115093, + "native_steps": 834, + "pusher_block_contact_samples": 2626 + }, + "replayed": false + } + }, + { + "label": "image-4", + "mode": "view", + "reason": "", + "exit_code": 0, + "phase": "ready", + "generation": 4, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/vision-frame-000004.png", + "image_sha256": "65c811d81c78b8d6b69f9593b8cdcf4e449de8ac8a67f19bb3609a1f2980f13b", + "state_sha256": "6fe0eeb74fc98873c125bb83023e3015359c2976de173fd695a912a91616a594", + "fingerprint": "same frozen binding" + }, + { + "label": "short-placement-completion", + "mode": "apply", + "reason": "Fresh image shows orange center near the green left boundary, about 4 cm short of center; test that visual completion refuses.", + "exit_code": 2, + "phase": "refused", + "proposal": { + "block_box": [ + 407, + 158, + 451, + 202 + ], + "decision": "complete", + "generation": 4, + "image_sha256": "65c811d81c78b8d6b69f9593b8cdcf4e449de8ac8a67f19bb3609a1f2980f13b", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "token": "short-complete-001", + "version": 1 + }, + "feedback": { + "reason": "fresh RGB block center is outside the conservative inspection zone" + } + }, + { + "label": "final-correction-5", + "mode": "apply", + "reason": "Fresh image shows about 24 pixels of remaining horizontal error; add a smaller 4 cm correction toward the green center.", + "exit_code": 0, + "phase": "executed", + "proposal": { + "block_box": [ + 407, + 158, + 451, + 202 + ], + "decision": "move", + "generation": 4, + "image_sha256": "65c811d81c78b8d6b69f9593b8cdcf4e449de8ac8a67f19bb3609a1f2980f13b", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "target": { + "x": 0.17, + "y": 0.0 + }, + "token": "move-005", + "version": 1 + }, + "feedback": { + "after_generation": 5, + "after_state_sha256": "274cfa97631a8aaa8b01b9a307d2c15c2f795313407f4f347144a8dc88e1f126", + "historical_visual_check": { + "block_box": [ + 408, + 159, + 449, + 201 + ], + "image_center_world_xy": [ + 0.18083333333333332, + 0.0 + ], + "method": "color_specific_RGB" + }, + "contact_statistics": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 1.499700450030204, + "native_steps": 634, + "pusher_block_contact_samples": 2124 + }, + "replayed": false + } + }, + { + "label": "image-5", + "mode": "view", + "reason": "", + "exit_code": 0, + "phase": "ready", + "generation": 5, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/vision-frame-000005.png", + "image_sha256": "f9b70fdbed5e1edb27ffc4cdf9337c985d1e4e4a5f4f3f88a9f70c9e71db9f24", + "state_sha256": "274cfa97631a8aaa8b01b9a307d2c15c2f795313407f4f347144a8dc88e1f126", + "fingerprint": "same frozen binding" + }, + { + "label": "visual-completion", + "mode": "apply", + "reason": "Fresh final image shows the orange block centered on the green zone with its visible face contained; request the gated computer action.", + "exit_code": 0, + "phase": "staged", + "proposal": { + "block_box": [ + 431, + 158, + 475, + 202 + ], + "decision": "complete", + "generation": 5, + "image_sha256": "f9b70fdbed5e1edb27ffc4cdf9337c985d1e4e4a5f4f3f88a9f70c9e71db9f24", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "token": "complete-001", + "version": 1 + }, + "feedback": { + "after_generation": 5, + "after_state_sha256": "274cfa97631a8aaa8b01b9a307d2c15c2f795313407f4f347144a8dc88e1f126", + "historical_visual_check": { + "block_box": [ + 432, + 159, + 473, + 201 + ], + "image_center_world_xy": [ + 0.22083333333333333, + 0.0 + ], + "inside_with_margin": true, + "method": "color_specific_RGB" + }, + "replayed": false, + "current_software_artifact_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1196, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/expected-order.json", + "expected_sha256": "1fffd9b46917dc6117da3f0138be079cf67fe9db82f3dca4749380944b3ed1df", + "limit_bytes": 1048576, + "observed_bytes": 1196, + "observed_sha256": "1fffd9b46917dc6117da3f0138be079cf67fe9db82f3dca4749380944b3ed1df", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/order.json", + "source_sha256": "1fffd9b46917dc6117da3f0138be079cf67fe9db82f3dca4749380944b3ed1df" + } + } + }, + { + "label": "repeated-completion", + "mode": "replay", + "reason": "Repeat the exact completion proposal; verify current software bytes without creating a second result.", + "exit_code": 0, + "phase": "staged", + "proposal": { + "block_box": [ + 431, + 158, + 475, + 202 + ], + "decision": "complete", + "generation": 5, + "image_sha256": "f9b70fdbed5e1edb27ffc4cdf9337c985d1e4e4a5f4f3f88a9f70c9e71db9f24", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1", + "token": "complete-001", + "version": 1 + }, + "feedback": { + "after_generation": 5, + "after_state_sha256": "274cfa97631a8aaa8b01b9a307d2c15c2f795313407f4f347144a8dc88e1f126", + "historical_visual_check": { + "block_box": [ + 432, + 159, + 473, + 201 + ], + "image_center_world_xy": [ + 0.22083333333333333, + 0.0 + ], + "inside_with_margin": true, + "method": "color_specific_RGB" + }, + "replayed": false, + "current_software_artifact_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1196, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/expected-order.json", + "expected_sha256": "1fffd9b46917dc6117da3f0138be079cf67fe9db82f3dca4749380944b3ed1df", + "limit_bytes": 1048576, + "observed_bytes": 1196, + "observed_sha256": "1fffd9b46917dc6117da3f0138be079cf67fe9db82f3dca4749380944b3ed1df", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v1/order.json", + "source_sha256": "1fffd9b46917dc6117da3f0138be079cf67fe9db82f3dca4749380944b3ed1df" + } + } + } + ], + "private_raw_trajectory": { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim2-inputs/trajectory.jsonl", + "sha256": "5f83803b75ff20cae184a8fae4298cf104ac7c70b403448cd2bb775ff8f030f8" + }, + "metrics": { + "moves": 5, + "viewed_frames": 6, + "target_center_xy": [ + 0.22, + 0 + ], + "before_final_correction_image_xy": [ + 0.18083333333333332, + 0.0 + ], + "final_image_xy": [ + 0.22083333333333333, + 0.0 + ], + "final_native_xyz": [ + 0.2207798003506426, + -1.7471077498589427e-10, + 0.03499834074306675 + ], + "final_horizontal_image_error_m": 0.0008333333333333248, + "native_horizontal_error_m": 0.0007798003506425955, + "maximum_axis_actuator_force_n": 1.499700450030204 + } +} diff --git a/jevdual/results/image-guided-workcell-20261010/trial-v2.json b/jevdual/results/image-guided-workcell-20261010/trial-v2.json new file mode 100644 index 0000000..6e7204f --- /dev/null +++ b/jevdual/results/image-guided-workcell-20261010/trial-v2.json @@ -0,0 +1,673 @@ +{ + "version": "v2", + "source_commit": "f96ac4a", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2", + "planner": "current Codex session, inspected each RGB frame before its next decision; no API calls", + "planner_packets_have_native_poses": false, + "driver_sha256": "259394899c189a14dbb7f19781914c47cb6aa42f5e26cb28803628ac197cfcfc", + "post_run_native_oracle": { + "block_center_in_zone": true, + "block_xyz": [ + 0.2207432310034855, + -1.73716030348864e-10, + 0.034998235824722744 + ], + "claims": [ + "native_simulator_state_observation" + ], + "current_software_artifact_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1195, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/expected-order.json", + "expected_sha256": "ec275d09fa202e3b3e3d88022bc042b84e2fcb448d0f514727055cdd235ecf2f", + "limit_bytes": 1048576, + "observed_bytes": 1195, + "observed_sha256": "ec275d09fa202e3b3e3d88022bc042b84e2fcb448d0f514727055cdd235ecf2f", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/order.json", + "source_sha256": "ec275d09fa202e3b3e3d88022bc042b84e2fcb448d0f514727055cdd235ecf2f" + }, + "frame": "sim_world", + "generation": 5, + "historical_software_outcome": "staged", + "order_id": "footwork-image-stage-block-001", + "phase": "completed", + "physical_condition_verified": true, + "pusher_xyz": [ + 0.1657457661178629, + 8.161743913677396e-19, + 0.06 + ], + "simulation_time_s": 9.137999999999721, + "source_kind": "mujoco_simulation", + "state_authority": "current_persisted_simulator_state", + "state_sha256": "e0eae3b6b3c86335bb69b885d8dd4a051285634e4e31598eb7c926f1ff63dac1", + "target_zone": { + "x_max": 0.26, + "x_min": 0.18, + "y_max": 0.04, + "y_min": -0.04 + } + }, + "independent_software_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1195, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/expected-order.json", + "expected_sha256": "ec275d09fa202e3b3e3d88022bc042b84e2fcb448d0f514727055cdd235ecf2f", + "limit_bytes": 1048576, + "observed_bytes": 1195, + "observed_sha256": "ec275d09fa202e3b3e3d88022bc042b84e2fcb448d0f514727055cdd235ecf2f", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/order.json", + "source_sha256": "ec275d09fa202e3b3e3d88022bc042b84e2fcb448d0f514727055cdd235ecf2f" + }, + "repeated_proposals_all_artifacts_unchanged": true, + "limits": [ + "fixed camera and object colors", + "agent-driven trial, no unattended model runner", + "completion verifies block center with margin, not general manipulation" + ], + "trajectory_summary": [ + { + "label": "initialize", + "mode": "init", + "reason": "", + "exit_code": 0, + "phase": "ready", + "note": "Native initialization result withheld from planner; raw public CLI result in private trajectory." + }, + { + "label": "image-0", + "mode": "view", + "reason": "", + "exit_code": 0, + "phase": "ready", + "generation": 0, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/vision-frame-000000.png", + "image_sha256": "824f565207bef6625da7fbcbf289ed6cfb50b7f19fc503e1fe8bad51187d7ea0", + "state_sha256": "3a8c7d1b2c047630a067a0590f939c066b6c621349b6021d26609c85b2e4644b", + "fingerprint": { + "base_fingerprints": { + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "810d343cf500526ac884badc31bae095f819a3c3e469a3d4a3155c7a20960d3b", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "state_spec": 16383 + }, + "schema_sha256": "4f632336093c6b9d81741a4deff879b07eea7c9290eb34cbfa75ed84359b90e6", + "settings_sha256": "a4b1820eb44423efa3f5c230b9afa30007ac1608033ffa1aa1594419a3868bea", + "wrapper_sha256": "d77da7013cefd6ea3344878332450b1835a207716fa0ed0fdb42f5f376334494" + } + }, + { + "label": "approach-1", + "mode": "apply", + "reason": "The overhead image shows a large gap before contact; choose a 10 cm approach and inspect the new view.", + "exit_code": 0, + "phase": "executed", + "proposal": { + "block_box": [ + 226, + 158, + 270, + 202 + ], + "decision": "move", + "generation": 0, + "image_sha256": "824f565207bef6625da7fbcbf289ed6cfb50b7f19fc503e1fe8bad51187d7ea0", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2", + "target": { + "x": -0.2, + "y": 0.0 + }, + "token": "move-001", + "version": 1 + }, + "feedback": { + "after_generation": 1, + "after_state_sha256": "1d8919b516bf48038bcae73cd39a79177fe329c40e1e2ae1a2aaf6d7a4476219", + "historical_visual_check": { + "block_box": [ + 227, + 159, + 269, + 201 + ], + "image_center_world_xy": [ + -0.12, + 0.0 + ], + "method": "color_specific_RGB" + }, + "contact_statistics": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 0.38456934164970424, + "native_steps": 834, + "pusher_block_contact_samples": 0 + }, + "replayed": false + } + }, + { + "label": "image-1", + "mode": "view", + "reason": "", + "exit_code": 0, + "phase": "ready", + "generation": 1, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/vision-frame-000001.png", + "image_sha256": "1f687fc7db0ad2f12862b06de9765440542041084fcdb51d58e5793f5cfa74e4", + "state_sha256": "1d8919b516bf48038bcae73cd39a79177fe329c40e1e2ae1a2aaf6d7a4476219", + "fingerprint": "same frozen binding" + }, + { + "label": "contact-2", + "mode": "apply", + "reason": "Fresh frame shows pusher close to the orange block, which is still far left of target; make an 11 cm contact push.", + "exit_code": 0, + "phase": "executed", + "proposal": { + "block_box": [ + 226, + 158, + 270, + 202 + ], + "decision": "move", + "generation": 1, + "image_sha256": "1f687fc7db0ad2f12862b06de9765440542041084fcdb51d58e5793f5cfa74e4", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2", + "target": { + "x": -0.09, + "y": 0.0 + }, + "token": "move-002", + "version": 1 + }, + "feedback": { + "after_generation": 2, + "after_state_sha256": "9a85e6b1420b674bae3b79e6ddc8cf20c2951645cd9e8f780ee61b5f3623808b", + "historical_visual_check": { + "block_box": [ + 227, + 159, + 269, + 201 + ], + "image_center_world_xy": [ + -0.12, + 0.0 + ], + "method": "color_specific_RGB" + }, + "contact_statistics": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 1.2989950287851713, + "native_steps": 867, + "pusher_block_contact_samples": 2274 + }, + "replayed": false + } + }, + { + "label": "image-2", + "mode": "view", + "reason": "", + "exit_code": 0, + "phase": "ready", + "generation": 2, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/vision-frame-000002.png", + "image_sha256": "1a13c5aabcbb34787bddb302c6324eafd206b1c557d44dc7b11f08b2876013a7", + "state_sha256": "9a85e6b1420b674bae3b79e6ddc8cf20c2951645cd9e8f780ee61b5f3623808b", + "fingerprint": "same frozen binding" + }, + { + "label": "feedback-3", + "mode": "apply", + "reason": "New view confirms contact moved the block, but it remains about 155 pixels left of green center; continue an 11 cm bounded push.", + "exit_code": 0, + "phase": "executed", + "proposal": { + "block_box": [ + 275, + 158, + 320, + 202 + ], + "decision": "move", + "generation": 2, + "image_sha256": "1a13c5aabcbb34787bddb302c6324eafd206b1c557d44dc7b11f08b2876013a7", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2", + "target": { + "x": 0.02, + "y": 0.0 + }, + "token": "move-003", + "version": 1 + }, + "feedback": { + "after_generation": 3, + "after_state_sha256": "ffbb07ff0d2af9873a95a133760f2eeb21ff21b5eac73ba6080bff437227cf0e", + "historical_visual_check": { + "block_box": [ + 276, + 159, + 317, + 201 + ], + "image_center_world_xy": [ + -0.03916666666666666, + 0.0 + ], + "method": "color_specific_RGB" + }, + "contact_statistics": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 1.4476469920290285, + "native_steps": 867, + "pusher_block_contact_samples": 2596 + }, + "replayed": false + } + }, + { + "label": "image-3", + "mode": "view", + "reason": "", + "exit_code": 0, + "phase": "ready", + "generation": 3, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/vision-frame-000003.png", + "image_sha256": "4b560931a6cd39894da76d0c0c4bac2d7b64961fb9bf4dfcc9af658fd759a780", + "state_sha256": "ffbb07ff0d2af9873a95a133760f2eeb21ff21b5eac73ba6080bff437227cf0e", + "fingerprint": "same frozen binding" + }, + { + "label": "feedback-4", + "mode": "apply", + "reason": "The orange center is still about 89 pixels left of the green center; use a 10 cm push and check the remaining error in a new frame.", + "exit_code": 0, + "phase": "executed", + "proposal": { + "block_box": [ + 341, + 158, + 385, + 202 + ], + "decision": "move", + "generation": 3, + "image_sha256": "4b560931a6cd39894da76d0c0c4bac2d7b64961fb9bf4dfcc9af658fd759a780", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2", + "target": { + "x": 0.12, + "y": 0.0 + }, + "token": "move-004", + "version": 1 + }, + "feedback": { + "after_generation": 4, + "after_state_sha256": "322055163bb36712b6382e33d3ca2fbf6d053129182b4737a6416d479f2eba48", + "historical_visual_check": { + "block_box": [ + 342, + 159, + 384, + 201 + ], + "image_center_world_xy": [ + 0.07166666666666666, + 0.0 + ], + "method": "color_specific_RGB" + }, + "contact_statistics": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 1.467647819345494, + "native_steps": 834, + "pusher_block_contact_samples": 2548 + }, + "replayed": false + } + }, + { + "label": "image-4", + "mode": "view", + "reason": "", + "exit_code": 0, + "phase": "ready", + "generation": 4, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/vision-frame-000004.png", + "image_sha256": "e7376fea396aa33836c4e9202df010feff5df00875e76a4eb580153d0c7b21cb", + "state_sha256": "322055163bb36712b6382e33d3ca2fbf6d053129182b4737a6416d479f2eba48", + "fingerprint": "same frozen binding" + }, + { + "label": "short-placement-completion", + "mode": "apply", + "reason": "The new frame shows orange center still about 5 cm left of green center; completion must refuse before correction.", + "exit_code": 2, + "phase": "refused", + "proposal": { + "block_box": [ + 401, + 158, + 445, + 202 + ], + "decision": "complete", + "generation": 4, + "image_sha256": "e7376fea396aa33836c4e9202df010feff5df00875e76a4eb580153d0c7b21cb", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2", + "token": "short-complete-001", + "version": 1 + }, + "feedback": { + "reason": "fresh RGB block center is outside the conservative inspection zone" + } + }, + { + "label": "final-correction-5", + "mode": "apply", + "reason": "Fresh image shows roughly 30 pixels remaining to green center; choose a smaller 5 cm correction and require another image.", + "exit_code": 0, + "phase": "executed", + "proposal": { + "block_box": [ + 401, + 158, + 445, + 202 + ], + "decision": "move", + "generation": 4, + "image_sha256": "e7376fea396aa33836c4e9202df010feff5df00875e76a4eb580153d0c7b21cb", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2", + "target": { + "x": 0.17, + "y": 0.0 + }, + "token": "move-005", + "version": 1 + }, + "feedback": { + "after_generation": 5, + "after_state_sha256": "e0eae3b6b3c86335bb69b885d8dd4a051285634e4e31598eb7c926f1ff63dac1", + "historical_visual_check": { + "block_box": [ + 402, + 159, + 443, + 201 + ], + "image_center_world_xy": [ + 0.1708333333333333, + 0.0 + ], + "method": "color_specific_RGB" + }, + "contact_statistics": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 1.501413009316252, + "native_steps": 667, + "pusher_block_contact_samples": 2162 + }, + "replayed": false + } + }, + { + "label": "image-5", + "mode": "view", + "reason": "", + "exit_code": 0, + "phase": "ready", + "generation": 5, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/vision-frame-000005.png", + "image_sha256": "6d822e4ccf21b34da2e81505f30995b7ac21a4e6d50cdce16bfbfb55eb22602c", + "state_sha256": "e0eae3b6b3c86335bb69b885d8dd4a051285634e4e31598eb7c926f1ff63dac1", + "fingerprint": "same frozen binding" + }, + { + "label": "stale-image-generation", + "mode": "apply", + "reason": "Negative check: a stale generation cannot complete even when the latest visible placement is correct.", + "exit_code": 2, + "phase": "refused", + "proposal": { + "block_box": [ + 431, + 158, + 475, + 202 + ], + "decision": "complete", + "generation": 4, + "image_sha256": "6d822e4ccf21b34da2e81505f30995b7ac21a4e6d50cdce16bfbfb55eb22602c", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2", + "token": "stale-001", + "version": 1 + }, + "feedback": { + "reason": "image proposal generation is stale" + } + }, + { + "label": "wrong-object-box", + "mode": "apply", + "reason": "Negative check: a box around the blue pusher cannot serve as block placement evidence.", + "exit_code": 2, + "phase": "refused", + "proposal": { + "block_box": [ + 407, + 138, + 431, + 222 + ], + "decision": "complete", + "generation": 5, + "image_sha256": "6d822e4ccf21b34da2e81505f30995b7ac21a4e6d50cdce16bfbfb55eb22602c", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2", + "token": "wrong-box-001", + "version": 1 + }, + "feedback": { + "reason": "proposed block box does not match independent RGB silhouette evidence" + } + }, + { + "label": "visual-completion", + "mode": "apply", + "reason": "Final fresh frame shows the orange face centered on the green area; request visual and physical verification followed by the local computer action.", + "exit_code": 0, + "phase": "staged", + "proposal": { + "block_box": [ + 431, + 158, + 475, + 202 + ], + "decision": "complete", + "generation": 5, + "image_sha256": "6d822e4ccf21b34da2e81505f30995b7ac21a4e6d50cdce16bfbfb55eb22602c", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2", + "token": "complete-001", + "version": 1 + }, + "feedback": { + "after_generation": 5, + "after_state_sha256": "e0eae3b6b3c86335bb69b885d8dd4a051285634e4e31598eb7c926f1ff63dac1", + "historical_visual_check": { + "block_box": [ + 432, + 159, + 473, + 201 + ], + "image_center_world_xy": [ + 0.22083333333333333, + 0.0 + ], + "inside_with_margin": true, + "method": "color_specific_RGB" + }, + "replayed": false, + "current_software_artifact_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1195, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/expected-order.json", + "expected_sha256": "ec275d09fa202e3b3e3d88022bc042b84e2fcb448d0f514727055cdd235ecf2f", + "limit_bytes": 1048576, + "observed_bytes": 1195, + "observed_sha256": "ec275d09fa202e3b3e3d88022bc042b84e2fcb448d0f514727055cdd235ecf2f", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/order.json", + "source_sha256": "ec275d09fa202e3b3e3d88022bc042b84e2fcb448d0f514727055cdd235ecf2f" + } + } + }, + { + "label": "repeated-completion", + "mode": "replay", + "reason": "Repeat the exact completion; current bytes must be rechecked without another write.", + "exit_code": 0, + "phase": "staged", + "proposal": { + "block_box": [ + 431, + 158, + 475, + 202 + ], + "decision": "complete", + "generation": 5, + "image_sha256": "6d822e4ccf21b34da2e81505f30995b7ac21a4e6d50cdce16bfbfb55eb22602c", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2", + "token": "complete-001", + "version": 1 + }, + "feedback": { + "after_generation": 5, + "after_state_sha256": "e0eae3b6b3c86335bb69b885d8dd4a051285634e4e31598eb7c926f1ff63dac1", + "historical_visual_check": { + "block_box": [ + 432, + 159, + 473, + 201 + ], + "image_center_world_xy": [ + 0.22083333333333333, + 0.0 + ], + "inside_with_margin": true, + "method": "color_specific_RGB" + }, + "replayed": false, + "current_software_artifact_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1195, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/expected-order.json", + "expected_sha256": "ec275d09fa202e3b3e3d88022bc042b84e2fcb448d0f514727055cdd235ecf2f", + "limit_bytes": 1048576, + "observed_bytes": 1195, + "observed_sha256": "ec275d09fa202e3b3e3d88022bc042b84e2fcb448d0f514727055cdd235ecf2f", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2/order.json", + "source_sha256": "ec275d09fa202e3b3e3d88022bc042b84e2fcb448d0f514727055cdd235ecf2f" + } + } + }, + { + "label": "repeated-motion", + "mode": "replay", + "reason": "Repeat the original move after completion; retained receipt must not step physics.", + "exit_code": 0, + "phase": "executed", + "proposal": { + "block_box": [ + 226, + 158, + 270, + 202 + ], + "decision": "move", + "generation": 0, + "image_sha256": "824f565207bef6625da7fbcbf289ed6cfb50b7f19fc503e1fe8bad51187d7ea0", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/image-demo-v2", + "target": { + "x": -0.2, + "y": 0.0 + }, + "token": "move-001", + "version": 1 + }, + "feedback": { + "after_generation": 1, + "after_state_sha256": "1d8919b516bf48038bcae73cd39a79177fe329c40e1e2ae1a2aaf6d7a4476219", + "historical_visual_check": { + "block_box": [ + 227, + 159, + 269, + 201 + ], + "image_center_world_xy": [ + -0.12, + 0.0 + ], + "method": "color_specific_RGB" + }, + "contact_statistics": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 0.38456934164970424, + "native_steps": 834, + "pusher_block_contact_samples": 0 + }, + "replayed": false + } + } + ], + "private_raw_trajectory": { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim2-v2-inputs/trajectory.jsonl", + "sha256": "00e767974d0475ef3baf80f22d111a38d9e23dfdc64485592061bf6ea3a191c7" + }, + "metrics": { + "moves": 5, + "viewed_frames": 6, + "target_center_xy": [ + 0.22, + 0 + ], + "before_final_correction_image_xy": [ + 0.1708333333333333, + 0.0 + ], + "final_image_xy": [ + 0.22083333333333333, + 0.0 + ], + "final_native_xyz": [ + 0.2207432310034855, + -1.73716030348864e-10, + 0.034998235824722744 + ], + "final_horizontal_image_error_m": 0.0008333333333333248, + "native_horizontal_error_m": 0.0007432310034855005, + "maximum_axis_actuator_force_n": 1.501413009316252 + } +} diff --git a/jevdual/results/image-guided-workcell-20261010/verification.json b/jevdual/results/image-guided-workcell-20261010/verification.json new file mode 100644 index 0000000..b8adbfc --- /dev/null +++ b/jevdual/results/image-guided-workcell-20261010/verification.json @@ -0,0 +1,30 @@ +{ + "source_commit": "f96ac4a", + "checks": [ + { + "command": "tools/physical-workcell/.venv/bin/python -m unittest discover -s tools/physical-workcell -p test_*.py -v", + "exit_code": 0, + "tests": 26, + "duration_seconds": 9.281, + "outcome": "OK; actual renderer/engine conformance and SIM1 regressions" + }, + { + "command": "jevdual/.venv/bin/ruff check tools/physical-workcell", + "exit_code": 0, + "outcome": "All checks passed" + }, + { + "command": "uv run python -m evals.notebook docs/notebook --check", + "exit_code": 0, + "outcome": "Notebook format and prediction resolution checks passed" + } + ], + "source_sha256": { + "image_guided.py": "d77da7013cefd6ea3344878332450b1835a207716fa0ed0fdb42f5f376334494", + "proposal.schema.json": "4f632336093c6b9d81741a4deff879b07eea7c9290eb34cbfa75ed84359b90e6", + "test_image_guided.py": "17915d8c426b29973b97065180c7cab848581a579562f5763bffb675d7f8eb1d", + "test_workcell.py": "dc5687ce21e69a34f5d7c65e3036e5b42202f7a6e10adb92611fe8c279751ee9", + "workcell.py": "810d343cf500526ac884badc31bae095f819a3c3e469a3d4a3155c7a20960d3b", + "scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f" + } +} diff --git a/jevdual/results/independent-controller-20261010/checks.json b/jevdual/results/independent-controller-20261010/checks.json new file mode 100644 index 0000000..e7e0caf --- /dev/null +++ b/jevdual/results/independent-controller-20261010/checks.json @@ -0,0 +1,40 @@ +{ + "checks": [ + { + "label": "full_native_conformance", + "command": [ + "tools/physical-workcell/.venv/bin/python", + "-m", + "unittest", + "discover", + "-s", + "tools/physical-workcell", + "-p", + "test_*.py", + "-v" + ], + "exit_code": 0, + "stdout": "", + "stderr": "test_accepted_undispatched_restart_never_replays_and_old_incarnation_refuses (test_controller.ControllerProcessTests.test_accepted_undispatched_restart_never_replays_and_old_incarnation_refuses) ... ok\ntest_active_return_reports_busy_without_native_pose (test_controller.ControllerProcessTests.test_active_return_reports_busy_without_native_pose) ... ok\ntest_changed_case_and_malformed_controller_binding_refuse_reuse (test_controller.ControllerProcessTests.test_changed_case_and_malformed_controller_binding_refuse_reuse) ... ok\ntest_controller_death_after_cancel_request_never_claims_verified_stop (test_controller.ControllerProcessTests.test_controller_death_after_cancel_request_never_claims_verified_stop) ... ok\ntest_current_state_corruption_preserves_history_and_blocks_new_effects (test_controller.ControllerProcessTests.test_current_state_corruption_preserves_history_and_blocks_new_effects) ... ok\ntest_exact_lower_receipt_reconciles_lost_controller_final_write (test_controller.ControllerProcessTests.test_exact_lower_receipt_reconciles_lost_controller_final_write) ... ok\ntest_image_feedback_contact_and_conditional_saved_json (test_controller.ControllerProcessTests.test_image_feedback_contact_and_conditional_saved_json) ... ok\ntest_late_cancellation_preserves_terminal_effect (test_controller.ControllerProcessTests.test_late_cancellation_preserves_terminal_effect) ... ok\ntest_lifetime_excludes_second_owner_and_legacy_cli (test_controller.ControllerProcessTests.test_lifetime_excludes_second_owner_and_legacy_cli) ... ok\ntest_live_retained_binding_tamper_blocks_health_and_new_acceptance (test_controller.ControllerProcessTests.test_live_retained_binding_tamper_blocks_health_and_new_acceptance) ... ok\ntest_malformed_id_digest_and_stale_proposal_refuse_without_effect (test_controller.ControllerProcessTests.test_malformed_id_digest_and_stale_proposal_refuse_without_effect) ... ok\ntest_queued_cancellation_does_not_dispatch_or_increment_generation (test_controller.ControllerProcessTests.test_queued_cancellation_does_not_dispatch_or_increment_generation) ... ok\ntest_running_cancel_verifies_stop_after_real_native_progress (test_controller.ControllerProcessTests.test_running_cancel_verifies_stop_after_real_native_progress) ... ok\ntest_satisfied_current_scene_cannot_replace_missing_execution_history (test_controller.ControllerProcessTests.test_satisfied_current_scene_cannot_replace_missing_execution_history) ... ok\ntest_saved_unknown_fallback_reconciles_exact_terminal_lower_history (test_controller.ControllerProcessTests.test_saved_unknown_fallback_reconciles_exact_terminal_lower_history) ... ok\ntest_shutdown_join_timeout_never_releases_ownership_to_live_writer (test_controller.ControllerProcessTests.test_shutdown_join_timeout_never_releases_ownership_to_live_writer) ... ok\ntest_sigkill_restart_new_incarnation_unknown_and_no_replay (test_controller.ControllerProcessTests.test_sigkill_restart_new_incarnation_unknown_and_no_replay) ... ok\ntest_submitter_exit_and_lost_reply_leave_one_durable_effect (test_controller.ControllerProcessTests.test_submitter_exit_and_lost_reply_leave_one_durable_effect) ... ok\ntest_budget_exhaustion_refuses_before_wrapper_or_native_intent (test_episode_cases.EpisodeCaseTests.test_budget_exhaustion_refuses_before_wrapper_or_native_intent) ... ok\ntest_case_source_symlink_and_bad_digest_refuse_before_initialization (test_episode_cases.EpisodeCaseTests.test_case_source_symlink_and_bad_digest_refuse_before_initialization) ... ok\ntest_case_validation_exact_fields_finite_bounds_and_duplicates (test_episode_cases.EpisodeCaseTests.test_case_validation_exact_fields_finite_bounds_and_duplicates) ... ok\ntest_changed_source_retained_case_and_partial_initialization_refuse (test_episode_cases.EpisodeCaseTests.test_changed_source_retained_case_and_partial_initialization_refuse) ... ok\ntest_completion_is_an_admitted_action_without_manufactured_motion (test_episode_cases.EpisodeCaseTests.test_completion_is_an_admitted_action_without_manufactured_motion) ... ok\ntest_false_visual_verdict_and_exact_request_retained_before_oracle (test_episode_cases.EpisodeCaseTests.test_false_visual_verdict_and_exact_request_retained_before_oracle) ... ok\ntest_initial_xy_applied_once_and_repeat_never_resets_motion (test_episode_cases.EpisodeCaseTests.test_initial_xy_applied_once_and_repeat_never_resets_motion) ... ok\ntest_initialization_marker_exact_case_timing_and_no_stray_file_exception (test_episode_cases.EpisodeCaseTests.test_initialization_marker_exact_case_timing_and_no_stray_file_exception) ... ok\ntest_offset_image_has_invalid_calibration_and_no_world_center (test_episode_cases.EpisodeCaseTests.test_offset_image_has_invalid_calibration_and_no_world_center) ... ok\ntest_positive_visual_verdict_survives_native_gate_failure_as_unknown (test_episode_cases.EpisodeCaseTests.test_positive_visual_verdict_survives_native_gate_failure_as_unknown) ... ok\ntest_unavailable_and_occluded_images_remain_inspectable_without_effects (test_episode_cases.EpisodeCaseTests.test_unavailable_and_occluded_images_remain_inspectable_without_effects) ... ok\ntest_actual_rgb_baseline_completes_with_contact_and_independent_saved_file (test_episode_runner.EpisodeTests.test_actual_rgb_baseline_completes_with_contact_and_independent_saved_file) ... ok\ntest_already_placed_case_real_visual_and_software_completion_has_zero_motion (test_episode_runner.EpisodeTests.test_already_placed_case_real_visual_and_software_completion_has_zero_motion) ... ok\ntest_bound_generation_verdict_counts_hidden_false_positive (test_episode_runner.EpisodeTests.test_bound_generation_verdict_counts_hidden_false_positive) ... ok\ntest_case_binding_no_implicit_reset_and_public_init_filtering (test_episode_runner.EpisodeTests.test_case_binding_no_implicit_reset_and_public_init_filtering) ... ok\ntest_doctor_has_no_episode_or_persisted_simulator_effect (test_episode_runner.EpisodeTests.test_doctor_has_no_episode_or_persisted_simulator_effect) ... ok\ntest_failed_rgb_recognition_retains_image_and_declares_unresolved (test_episode_runner.EpisodeTests.test_failed_rgb_recognition_retains_image_and_declares_unresolved) ... ok\ntest_failed_setup_is_durable_counted_and_never_retried (test_episode_runner.EpisodeTests.test_failed_setup_is_durable_counted_and_never_retried) ... ok\ntest_false_completion_claim_is_visible_without_software (test_episode_runner.EpisodeTests.test_false_completion_claim_is_visible_without_software) ... ok\ntest_finish_declaration_elapsed_idempotency_conflict_and_no_effects (test_episode_runner.EpisodeTests.test_finish_declaration_elapsed_idempotency_conflict_and_no_effects) ... ok\ntest_invalid_calibration_retains_image_and_blocks_baseline (test_episode_runner.EpisodeTests.test_invalid_calibration_retains_image_and_blocks_baseline) ... ok\ntest_satisfactory_current_scene_does_not_manufacture_history (test_episode_runner.EpisodeTests.test_satisfactory_current_scene_does_not_manufacture_history) ... ok\ntest_small_budget_reserves_completion_slot_and_performs_no_unbudgeted_motion (test_episode_runner.EpisodeTests.test_small_budget_reserves_completion_slot_and_performs_no_unbudgeted_motion) ... ok\ntest_unexpected_baseline_driver_failure_is_finished_and_counted (test_episode_runner.EpisodeTests.test_unexpected_baseline_driver_failure_is_finished_and_counted) ... ok\ntest_bad_silhouettes_reject_blank_multiple_occluded_and_off_frame (test_image_guided.ImageGuidedTests.test_bad_silhouettes_reject_blank_multiple_occluded_and_off_frame) ... ok\ntest_current_result_tamper_or_missing_preserves_historical_staging_without_replay (test_image_guided.ImageGuidedTests.test_current_result_tamper_or_missing_preserves_historical_staging_without_replay) ... ok\ntest_forged_oracle_extra_field_or_perception_scope_is_not_returned (test_image_guided.ImageGuidedTests.test_forged_oracle_extra_field_or_perception_scope_is_not_returned) ... ok\ntest_lost_wrapper_receipt_blocks_replay_and_new_views (test_image_guided.ImageGuidedTests.test_lost_wrapper_receipt_blocks_replay_and_new_views) ... ok\ntest_native_rgb_calibration_full_silhouette_and_no_oracle_fields (test_image_guided.ImageGuidedTests.test_native_rgb_calibration_full_silhouette_and_no_oracle_fields) ... ok\ntest_partial_view_is_not_rerendered_or_promoted (test_image_guided.ImageGuidedTests.test_partial_view_is_not_rerendered_or_promoted) ... ok\ntest_premature_visual_completion_refuses_before_native_effect (test_image_guided.ImageGuidedTests.test_premature_visual_completion_refuses_before_native_effect) ... ok\ntest_public_fresh_process_view_apply_repeats_are_filtered (test_image_guided.ImageGuidedTests.test_public_fresh_process_view_apply_repeats_are_filtered) ... ok\ntest_rgb_feedback_correction_and_final_private_oracle_agreement (test_image_guided.ImageGuidedTests.test_rgb_feedback_correction_and_final_private_oracle_agreement) ... ok\ntest_source_freeze_and_png_limits_are_enforced (test_image_guided.ImageGuidedTests.test_source_freeze_and_png_limits_are_enforced) ... ok\ntest_tampered_image_stale_proposal_and_changed_token_never_step (test_image_guided.ImageGuidedTests.test_tampered_image_stale_proposal_and_changed_token_never_step) ... ok\ntest_tampered_task_metadata_and_historical_receipt_refuse (test_image_guided.ImageGuidedTests.test_tampered_task_metadata_and_historical_receipt_refuse) ... ok\ntest_wrong_box_reverse_oversize_and_lateral_targets_refuse (test_image_guided.ImageGuidedTests.test_wrong_box_reverse_oversize_and_lateral_targets_refuse) ... ok\ntest_before_condition_false_and_completion_creates_nothing (test_workcell.WorkcellTests.test_before_condition_false_and_completion_creates_nothing) ... ok\ntest_cross_process_lock_prevents_second_owner (test_workcell.WorkcellTests.test_cross_process_lock_prevents_second_owner) ... ok\ntest_fresh_process_continuity_repeat_and_one_digital_result (test_workcell.WorkcellTests.test_fresh_process_continuity_repeat_and_one_digital_result) ... ok\ntest_init_lookup_is_repeat_safe_and_changed_order_refused (test_workcell.WorkcellTests.test_init_lookup_is_repeat_safe_and_changed_order_refused) ... ok\ntest_interrupted_state_publication_does_not_implicitly_reset_or_reexecute (test_workcell.WorkcellTests.test_interrupted_state_publication_does_not_implicitly_reset_or_reexecute) ... ok\ntest_lost_motion_receipt_blocks_replay_and_completion (test_workcell.WorkcellTests.test_lost_motion_receipt_blocks_replay_and_completion) ... ok\ntest_lost_software_receipt_never_promotes_valid_bytes_or_replays (test_workcell.WorkcellTests.test_lost_software_receipt_never_promotes_valid_bytes_or_replays) ... ok\ntest_malformed_and_nonfinite_inputs_fail_without_traceback (test_workcell.WorkcellTests.test_malformed_and_nonfinite_inputs_fail_without_traceback) ... ok\ntest_model_state_order_and_runtime_tamper_refuse_implicit_reset (test_workcell.WorkcellTests.test_model_state_order_and_runtime_tamper_refuse_implicit_reset) ... ok\ntest_noncontact_command_does_not_fulfill_order (test_workcell.WorkcellTests.test_noncontact_command_does_not_fulfill_order) ... ok\ntest_real_contact_changes_block_and_enables_independent_computer_result (test_workcell.WorkcellTests.test_real_contact_changes_block_and_enables_independent_computer_result) ... ok\ntest_tampered_or_missing_result_preserves_history_without_recreation (test_workcell.WorkcellTests.test_tampered_or_missing_result_preserves_history_without_recreation) ... ok\ntest_token_arguments_stale_generation_and_workspace_refuse_before_step (test_workcell.WorkcellTests.test_token_arguments_stale_generation_and_workspace_refuse_before_step) ... ok\n\n----------------------------------------------------------------------\nRan 68 tests in 45.872s\n\nOK\n" + }, + { + "label": "ruff", + "command": [ + "jevdual/.venv/bin/ruff", + "check", + "tools/physical-workcell" + ], + "exit_code": 0, + "output": "All checks passed!\n" + } + ], + "sha256": { + "controller.py": "29131e49c0b19ec59b957cf52a56aa573242ea3d3ee6851ebf4627c52a8c1bfd", + "workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "episode_runner.py": "530c8e65d2b4c613e3c02c8dcc5e9f8d56559e5f119b011bb3e138667b1474e7", + "test_controller.py": "3cb5863cab8c8920f4d56e746823bbc4ca900e96336235f4357bea19a5205f32", + "CONTROLLER.md": "356f8a67255941932a20c91c7f58aaf7630fd6eb2415ebe4d5d4463a582b2742" + } +} diff --git a/jevdual/results/independent-controller-20261010/closure-checks.json b/jevdual/results/independent-controller-20261010/closure-checks.json new file mode 100644 index 0000000..d43b599 --- /dev/null +++ b/jevdual/results/independent-controller-20261010/closure-checks.json @@ -0,0 +1,19 @@ +{ + "recorded_at": "2026-10-10T18:35:12.167113-04:00", + "runtime_commit": "5713173", + "full_scope_tests_passed": 68, + "ruff_exit_code": 0, + "required_lifecycle_scenarios_supported": 8, + "public_response_history_checks": 9, + "supplemental_native_progress_probe": 1, + "qualification_runtime_hashes_unchanged": true, + "parent_scoring_run_file_hashes_unchanged": true, + "all_owned_diagnostic_processes_exited": true, + "all_owned_diagnostic_sockets_removed": true, + "notebook_check_exit_code": 0, + "git_diff_check_exit_code": 0, + "prose_hygiene_removed": 0, + "prose_hygiene_replaced": 0, + "no_visible_desktop_required": true, + "screensaver_or_sleep_behavior_tested": false +} diff --git a/jevdual/results/independent-controller-20261010/development-attempts.json b/jevdual/results/independent-controller-20261010/development-attempts.json new file mode 100644 index 0000000..553771b --- /dev/null +++ b/jevdual/results/independent-controller-20261010/development-attempts.json @@ -0,0 +1,133 @@ +{ + "initial_socket_driver_attempt": { + "command": "tools/physical-workcell/.venv/bin/python -m unittest discover -s tools/physical-workcell -p test_controller.py -v", + "runner_exit_code": 130, + "completed_failures": 9, + "interrupted_test": "test_queued_cancellation_does_not_dispatch_or_increment_generation", + "cause": "test helper expected control/controller.sock; service publishes hashed socket path in controller-binding.json", + "no_goals_submitted": true, + "test_source_sha256": "ce706968173ca29ba85030306e928da5d8605253062dbea73aed98f21ba9575c", + "records": [ + { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c4100d0f/conformance.json", + "test_id": "test_controller.ControllerProcessTests.test_changed_case_and_malformed_controller_binding_refuse_reuse", + "passed": false, + "public_reply_count": 0 + }, + { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c4260d7a/conformance.json", + "test_id": "test_controller.ControllerProcessTests.test_exact_lower_receipt_reconciles_lost_controller_final_write", + "passed": false, + "public_reply_count": 0 + }, + { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c43cc3ed/conformance.json", + "test_id": "test_controller.ControllerProcessTests.test_malformed_id_digest_and_stale_proposal_refuse_without_effect", + "passed": false, + "public_reply_count": 0 + }, + { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c451f0a7/conformance.json", + "test_id": "test_controller.ControllerProcessTests.test_image_feedback_contact_and_conditional_saved_json", + "passed": false, + "public_reply_count": 0 + }, + { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c45d0483/conformance.json", + "test_id": "test_controller.ControllerProcessTests.test_accepted_undispatched_restart_never_replays_and_old_incarnation_refuses", + "passed": false, + "public_reply_count": 0 + }, + { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c4627383/conformance.json", + "test_id": "test_controller.ControllerProcessTests.test_lifetime_excludes_second_owner_and_legacy_cli", + "passed": false, + "public_reply_count": 0 + }, + { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c464b5b7/conformance.json", + "test_id": "test_controller.ControllerProcessTests.test_controller_death_after_cancel_request_never_claims_verified_stop", + "passed": false, + "public_reply_count": 0 + }, + { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c4b8b1af/conformance.json", + "test_id": "test_controller.ControllerProcessTests.test_active_return_reports_busy_without_native_pose", + "passed": false, + "public_reply_count": 0 + }, + { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c4e6418a/conformance.json", + "test_id": "test_controller.ControllerProcessTests.test_late_cancellation_preserves_terminal_effect", + "passed": false, + "public_reply_count": 0 + } + ] + }, + "development_attempts": [ + { + "kind": "development_process_conformance_with_harness_enum_mismatch", + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c4-process-run-v2/unittest.log", + "sha256": "550f311d2340a40feb85dfb103e65ca916bf61845e3657321220da9caf6d9f86", + "summary": [ + "Ran 15 tests in 27.435s", + "FAILED (failures=1)" + ] + }, + { + "kind": "product_recovery_failure_before_fix", + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c4-unknown-fallback-before-fix/unittest.log", + "sha256": "4f1e16df0b1e44323773b8ac61beb59e41e29b4b197d42fe7fc64802280003d2", + "summary": [ + "Ran 1 test in 1.487s", + "FAILED (failures=1)" + ] + }, + { + "kind": "product_binding_failure_before_fix", + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c4-live-binding-before-fix/unittest.log", + "sha256": "4c8f20707c38fed9a8d1ce2710ac844a718dc6303836d49c9ee4d718b3113b8e", + "summary": [ + "Ran 1 test in 0.732s", + "FAILED (failures=1)" + ] + }, + { + "kind": "development_process_conformance", + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c4-process-run-v3/unittest.log", + "sha256": "ab7b4e384f5a4ac093ae05c5a8fb135aeb886db436d9b44efa9765cf51c3310b", + "summary": [ + "Ran 17 tests in 30.871s", + "OK" + ] + }, + { + "kind": "targeted_final_recovery_and_artifact_checks", + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c4-final-focused/unittest.log", + "sha256": "fe7c8144fdf8963440f862402b450460be7f054e5de7847264e3d0f6a479e8be", + "summary": [ + "Ran 3 tests in 9.797s", + "OK" + ] + }, + { + "kind": "product_ownership_failure_before_fix_with_timing_fault", + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c4-shutdown-before-fix/unittest.log", + "sha256": "801bb031e5c18bcd9b61012083ad519a304f9c8d8e978e8659d29c1e13193f89", + "summary": [ + "Ran 1 test in 3.400s", + "FAILED (failures=1)" + ] + }, + { + "kind": "targeted_ownership_check_after_fix_with_timing_fault", + "private_log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/c4-shutdown-after-fix/unittest.log", + "sha256": "627705ff66652c1879bd9771dbc3a779f64be5565da923657fbe95455e02a28e", + "summary": [ + "Ran 1 test in 1.536s", + "OK" + ] + } + ], + "limits": "Development repetitions are separate from frozen diagnostic denominators. Controlled journal/timing faults are not native-stop or power-loss qualifications." +} diff --git a/jevdual/results/independent-controller-20261010/diagnostic-plan.json b/jevdual/results/independent-controller-20261010/diagnostic-plan.json new file mode 100644 index 0000000..023b572 --- /dev/null +++ b/jevdual/results/independent-controller-20261010/diagnostic-plan.json @@ -0,0 +1,43 @@ +{ + "preregistered_commit": "c78d2a4", + "mode": "headless actual-process diagnostic; not robot or model qualification", + "required_scenarios": [ + { + "id": "disconnect", + "expected": "durable submit survives caller loss; repeated identity changes no native state" + }, + { + "id": "active-return", + "expected": "nonterminal status while execution proceeds; observation busy without image authority" + }, + { + "id": "cancel-before-dispatch", + "expected": "cancelled with no native operation or generation change" + }, + { + "id": "cancel-during-motion", + "expected": "retained bounded stop evidence for cancelled, otherwise truthful unknown" + }, + { + "id": "lost-controller-result", + "expected": "restart reconciles intact exact lower terminal receipts without dispatch" + }, + { + "id": "controller-kill", + "expected": "new incarnation retains unknown interrupted operation; no replay" + }, + { + "id": "satisfied-scene-without-history", + "expected": "current placement cannot create missing execution history" + }, + { + "id": "physical-and-computer", + "expected": "image-derived contact movement plus fresh visual/native gate and checked saved order" + } + ], + "scope_limits": [ + "SIM4 does not repair a pending vision wrapper from a native-only terminal receipt", + "no hardware, camera, desktop interaction, production capability-service integration or real app workflow", + "shared filesystem permission boundary; no adversarial isolation claim" + ] +} diff --git a/jevdual/results/independent-controller-20261010/notebook-check.json b/jevdual/results/independent-controller-20261010/notebook-check.json new file mode 100644 index 0000000..24fcf66 --- /dev/null +++ b/jevdual/results/independent-controller-20261010/notebook-check.json @@ -0,0 +1,6 @@ +{ + "command": "uv run python -m evals.notebook docs/notebook --check", + "exit_code": 0, + "summary": "344 entries, 116 predictions: 82 held, 23 failed, 10 inconclusive, 1 open", + "errors": "" +} diff --git a/jevdual/results/independent-controller-20261010/pre-shutdown-fix-checks.json b/jevdual/results/independent-controller-20261010/pre-shutdown-fix-checks.json new file mode 100644 index 0000000..b4a463b --- /dev/null +++ b/jevdual/results/independent-controller-20261010/pre-shutdown-fix-checks.json @@ -0,0 +1,30 @@ +{ + "checks": [ + { + "label": "full_native_conformance", + "command": [ + "tools/physical-workcell/.venv/bin/python", + "-m", + "unittest", + "discover", + "-s", + "tools/physical-workcell", + "-p", + "test_*.py", + "-v" + ], + "exit_code": 0, + "stdout": "", + "stderr": "test_accepted_undispatched_restart_never_replays_and_old_incarnation_refuses (test_controller.ControllerProcessTests.test_accepted_undispatched_restart_never_replays_and_old_incarnation_refuses) ... ok\ntest_active_return_reports_busy_without_native_pose (test_controller.ControllerProcessTests.test_active_return_reports_busy_without_native_pose) ... ok\ntest_changed_case_and_malformed_controller_binding_refuse_reuse (test_controller.ControllerProcessTests.test_changed_case_and_malformed_controller_binding_refuse_reuse) ... ok\ntest_controller_death_after_cancel_request_never_claims_verified_stop (test_controller.ControllerProcessTests.test_controller_death_after_cancel_request_never_claims_verified_stop) ... ok\ntest_current_state_corruption_preserves_history_and_blocks_new_effects (test_controller.ControllerProcessTests.test_current_state_corruption_preserves_history_and_blocks_new_effects) ... ok\ntest_exact_lower_receipt_reconciles_lost_controller_final_write (test_controller.ControllerProcessTests.test_exact_lower_receipt_reconciles_lost_controller_final_write) ... ok\ntest_image_feedback_contact_and_conditional_saved_json (test_controller.ControllerProcessTests.test_image_feedback_contact_and_conditional_saved_json) ... ok\ntest_late_cancellation_preserves_terminal_effect (test_controller.ControllerProcessTests.test_late_cancellation_preserves_terminal_effect) ... ok\ntest_lifetime_excludes_second_owner_and_legacy_cli (test_controller.ControllerProcessTests.test_lifetime_excludes_second_owner_and_legacy_cli) ... ok\ntest_live_retained_binding_tamper_blocks_health_and_new_acceptance (test_controller.ControllerProcessTests.test_live_retained_binding_tamper_blocks_health_and_new_acceptance) ... ok\ntest_malformed_id_digest_and_stale_proposal_refuse_without_effect (test_controller.ControllerProcessTests.test_malformed_id_digest_and_stale_proposal_refuse_without_effect) ... ok\ntest_queued_cancellation_does_not_dispatch_or_increment_generation (test_controller.ControllerProcessTests.test_queued_cancellation_does_not_dispatch_or_increment_generation) ... ok\ntest_running_cancel_verifies_stop_after_real_native_progress (test_controller.ControllerProcessTests.test_running_cancel_verifies_stop_after_real_native_progress) ... ok\ntest_satisfied_current_scene_cannot_replace_missing_execution_history (test_controller.ControllerProcessTests.test_satisfied_current_scene_cannot_replace_missing_execution_history) ... ok\ntest_saved_unknown_fallback_reconciles_exact_terminal_lower_history (test_controller.ControllerProcessTests.test_saved_unknown_fallback_reconciles_exact_terminal_lower_history) ... ok\ntest_sigkill_restart_new_incarnation_unknown_and_no_replay (test_controller.ControllerProcessTests.test_sigkill_restart_new_incarnation_unknown_and_no_replay) ... ok\ntest_submitter_exit_and_lost_reply_leave_one_durable_effect (test_controller.ControllerProcessTests.test_submitter_exit_and_lost_reply_leave_one_durable_effect) ... ok\ntest_budget_exhaustion_refuses_before_wrapper_or_native_intent (test_episode_cases.EpisodeCaseTests.test_budget_exhaustion_refuses_before_wrapper_or_native_intent) ... ok\ntest_case_source_symlink_and_bad_digest_refuse_before_initialization (test_episode_cases.EpisodeCaseTests.test_case_source_symlink_and_bad_digest_refuse_before_initialization) ... ok\ntest_case_validation_exact_fields_finite_bounds_and_duplicates (test_episode_cases.EpisodeCaseTests.test_case_validation_exact_fields_finite_bounds_and_duplicates) ... ok\ntest_changed_source_retained_case_and_partial_initialization_refuse (test_episode_cases.EpisodeCaseTests.test_changed_source_retained_case_and_partial_initialization_refuse) ... ok\ntest_completion_is_an_admitted_action_without_manufactured_motion (test_episode_cases.EpisodeCaseTests.test_completion_is_an_admitted_action_without_manufactured_motion) ... ok\ntest_false_visual_verdict_and_exact_request_retained_before_oracle (test_episode_cases.EpisodeCaseTests.test_false_visual_verdict_and_exact_request_retained_before_oracle) ... ok\ntest_initial_xy_applied_once_and_repeat_never_resets_motion (test_episode_cases.EpisodeCaseTests.test_initial_xy_applied_once_and_repeat_never_resets_motion) ... ok\ntest_initialization_marker_exact_case_timing_and_no_stray_file_exception (test_episode_cases.EpisodeCaseTests.test_initialization_marker_exact_case_timing_and_no_stray_file_exception) ... ok\ntest_offset_image_has_invalid_calibration_and_no_world_center (test_episode_cases.EpisodeCaseTests.test_offset_image_has_invalid_calibration_and_no_world_center) ... ok\ntest_positive_visual_verdict_survives_native_gate_failure_as_unknown (test_episode_cases.EpisodeCaseTests.test_positive_visual_verdict_survives_native_gate_failure_as_unknown) ... ok\ntest_unavailable_and_occluded_images_remain_inspectable_without_effects (test_episode_cases.EpisodeCaseTests.test_unavailable_and_occluded_images_remain_inspectable_without_effects) ... ok\ntest_actual_rgb_baseline_completes_with_contact_and_independent_saved_file (test_episode_runner.EpisodeTests.test_actual_rgb_baseline_completes_with_contact_and_independent_saved_file) ... ok\ntest_already_placed_case_real_visual_and_software_completion_has_zero_motion (test_episode_runner.EpisodeTests.test_already_placed_case_real_visual_and_software_completion_has_zero_motion) ... ok\ntest_bound_generation_verdict_counts_hidden_false_positive (test_episode_runner.EpisodeTests.test_bound_generation_verdict_counts_hidden_false_positive) ... ok\ntest_case_binding_no_implicit_reset_and_public_init_filtering (test_episode_runner.EpisodeTests.test_case_binding_no_implicit_reset_and_public_init_filtering) ... ok\ntest_doctor_has_no_episode_or_persisted_simulator_effect (test_episode_runner.EpisodeTests.test_doctor_has_no_episode_or_persisted_simulator_effect) ... ok\ntest_failed_rgb_recognition_retains_image_and_declares_unresolved (test_episode_runner.EpisodeTests.test_failed_rgb_recognition_retains_image_and_declares_unresolved) ... ok\ntest_failed_setup_is_durable_counted_and_never_retried (test_episode_runner.EpisodeTests.test_failed_setup_is_durable_counted_and_never_retried) ... ok\ntest_false_completion_claim_is_visible_without_software (test_episode_runner.EpisodeTests.test_false_completion_claim_is_visible_without_software) ... ok\ntest_finish_declaration_elapsed_idempotency_conflict_and_no_effects (test_episode_runner.EpisodeTests.test_finish_declaration_elapsed_idempotency_conflict_and_no_effects) ... ok\ntest_invalid_calibration_retains_image_and_blocks_baseline (test_episode_runner.EpisodeTests.test_invalid_calibration_retains_image_and_blocks_baseline) ... ok\ntest_satisfactory_current_scene_does_not_manufacture_history (test_episode_runner.EpisodeTests.test_satisfactory_current_scene_does_not_manufacture_history) ... ok\ntest_small_budget_reserves_completion_slot_and_performs_no_unbudgeted_motion (test_episode_runner.EpisodeTests.test_small_budget_reserves_completion_slot_and_performs_no_unbudgeted_motion) ... ok\ntest_unexpected_baseline_driver_failure_is_finished_and_counted (test_episode_runner.EpisodeTests.test_unexpected_baseline_driver_failure_is_finished_and_counted) ... ok\ntest_bad_silhouettes_reject_blank_multiple_occluded_and_off_frame (test_image_guided.ImageGuidedTests.test_bad_silhouettes_reject_blank_multiple_occluded_and_off_frame) ... ok\ntest_current_result_tamper_or_missing_preserves_historical_staging_without_replay (test_image_guided.ImageGuidedTests.test_current_result_tamper_or_missing_preserves_historical_staging_without_replay) ... ok\ntest_forged_oracle_extra_field_or_perception_scope_is_not_returned (test_image_guided.ImageGuidedTests.test_forged_oracle_extra_field_or_perception_scope_is_not_returned) ... ok\ntest_lost_wrapper_receipt_blocks_replay_and_new_views (test_image_guided.ImageGuidedTests.test_lost_wrapper_receipt_blocks_replay_and_new_views) ... ok\ntest_native_rgb_calibration_full_silhouette_and_no_oracle_fields (test_image_guided.ImageGuidedTests.test_native_rgb_calibration_full_silhouette_and_no_oracle_fields) ... ok\ntest_partial_view_is_not_rerendered_or_promoted (test_image_guided.ImageGuidedTests.test_partial_view_is_not_rerendered_or_promoted) ... ok\ntest_premature_visual_completion_refuses_before_native_effect (test_image_guided.ImageGuidedTests.test_premature_visual_completion_refuses_before_native_effect) ... ok\ntest_public_fresh_process_view_apply_repeats_are_filtered (test_image_guided.ImageGuidedTests.test_public_fresh_process_view_apply_repeats_are_filtered) ... ok\ntest_rgb_feedback_correction_and_final_private_oracle_agreement (test_image_guided.ImageGuidedTests.test_rgb_feedback_correction_and_final_private_oracle_agreement) ... ok\ntest_source_freeze_and_png_limits_are_enforced (test_image_guided.ImageGuidedTests.test_source_freeze_and_png_limits_are_enforced) ... ok\ntest_tampered_image_stale_proposal_and_changed_token_never_step (test_image_guided.ImageGuidedTests.test_tampered_image_stale_proposal_and_changed_token_never_step) ... ok\ntest_tampered_task_metadata_and_historical_receipt_refuse (test_image_guided.ImageGuidedTests.test_tampered_task_metadata_and_historical_receipt_refuse) ... ok\ntest_wrong_box_reverse_oversize_and_lateral_targets_refuse (test_image_guided.ImageGuidedTests.test_wrong_box_reverse_oversize_and_lateral_targets_refuse) ... ok\ntest_before_condition_false_and_completion_creates_nothing (test_workcell.WorkcellTests.test_before_condition_false_and_completion_creates_nothing) ... ok\ntest_cross_process_lock_prevents_second_owner (test_workcell.WorkcellTests.test_cross_process_lock_prevents_second_owner) ... ok\ntest_fresh_process_continuity_repeat_and_one_digital_result (test_workcell.WorkcellTests.test_fresh_process_continuity_repeat_and_one_digital_result) ... ok\ntest_init_lookup_is_repeat_safe_and_changed_order_refused (test_workcell.WorkcellTests.test_init_lookup_is_repeat_safe_and_changed_order_refused) ... ok\ntest_interrupted_state_publication_does_not_implicitly_reset_or_reexecute (test_workcell.WorkcellTests.test_interrupted_state_publication_does_not_implicitly_reset_or_reexecute) ... ok\ntest_lost_motion_receipt_blocks_replay_and_completion (test_workcell.WorkcellTests.test_lost_motion_receipt_blocks_replay_and_completion) ... ok\ntest_lost_software_receipt_never_promotes_valid_bytes_or_replays (test_workcell.WorkcellTests.test_lost_software_receipt_never_promotes_valid_bytes_or_replays) ... ok\ntest_malformed_and_nonfinite_inputs_fail_without_traceback (test_workcell.WorkcellTests.test_malformed_and_nonfinite_inputs_fail_without_traceback) ... ok\ntest_model_state_order_and_runtime_tamper_refuse_implicit_reset (test_workcell.WorkcellTests.test_model_state_order_and_runtime_tamper_refuse_implicit_reset) ... ok\ntest_noncontact_command_does_not_fulfill_order (test_workcell.WorkcellTests.test_noncontact_command_does_not_fulfill_order) ... ok\ntest_real_contact_changes_block_and_enables_independent_computer_result (test_workcell.WorkcellTests.test_real_contact_changes_block_and_enables_independent_computer_result) ... ok\ntest_tampered_or_missing_result_preserves_history_without_recreation (test_workcell.WorkcellTests.test_tampered_or_missing_result_preserves_history_without_recreation) ... ok\ntest_token_arguments_stale_generation_and_workspace_refuse_before_step (test_workcell.WorkcellTests.test_token_arguments_stale_generation_and_workspace_refuse_before_step) ... ok\n\n----------------------------------------------------------------------\nRan 67 tests in 42.826s\n\nOK\n", + "sha256": { + "controller.py": "11607e206f1c4b929ad11e1b59a527617e3728344acb4ac0609c2b1460c0cf4d", + "workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "episode_runner.py": "530c8e65d2b4c613e3c02c8dcc5e9f8d56559e5f119b011bb3e138667b1474e7", + "test_controller.py": "056b8ffce3c91b9c7c2d91b4dada17cefe42027ad731934741e5588907512695", + "CONTROLLER.md": "bbd6f58979da8db0c2b4b4ccf86c30484d9264474e9eb1eca1cd6e22faef99cf" + } + } + ] +} diff --git a/jevdual/results/independent-controller-20261010/prose-hygiene.json b/jevdual/results/independent-controller-20261010/prose-hygiene.json new file mode 100644 index 0000000..05f776c --- /dev/null +++ b/jevdual/results/independent-controller-20261010/prose-hygiene.json @@ -0,0 +1,27 @@ +[ + { + "file": "README.md", + "removed": 0, + "replaced": 0 + }, + { + "file": "tools/physical-workcell/README.md", + "removed": 0, + "replaced": 0 + }, + { + "file": "jevdual/docs/experiments/SIM4-independent-controller-recovery.md", + "removed": 0, + "replaced": 0 + }, + { + "file": "jevdual/docs/notebook/2026-10-10-independent-controller-recovery.md", + "removed": 0, + "replaced": 0 + }, + { + "file": "jevdual/results/independent-controller-20261010/report.md", + "removed": 0, + "replaced": 0 + } +] diff --git a/jevdual/results/independent-controller-20261010/qualification.json b/jevdual/results/independent-controller-20261010/qualification.json new file mode 100644 index 0000000..766d035 --- /dev/null +++ b/jevdual/results/independent-controller-20261010/qualification.json @@ -0,0 +1,178 @@ +{ + "source_commit": "57131732d548556d6c2687175e629422c1ae2686", + "public_diagnostics": { + "attempted": 9, + "response_history_checks_passed": 9, + "raw_private_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/verification.json", + "sha256": "dc855a06d7d622377dce27ba375973a4e6618c16d3ed14454b32565ca992e2a3", + "driver_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/driver.py", + "driver_sha256": "c91a6bae5d866f6ddc53b74b38fcba35dac43e8a3acb56dc25be18243d67f455", + "scenarios": [ + { + "case_id": "01-disconnect", + "events_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/01-disconnect/events.json", + "passed": true + }, + { + "case_id": "02-active", + "events_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/02-active/events.json", + "passed": true + }, + { + "case_id": "03-queued-cancel", + "events_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/03-queued-cancel/events.json", + "passed": true + }, + { + "case_id": "04-running-cancel", + "events_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/04-running-cancel/events.json", + "passed": true + }, + { + "case_id": "05-lost-controller-record", + "events_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/05-lost-controller-record/events.json", + "passed": true + }, + { + "case_id": "06-running-kill", + "events_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/06-running-kill/events.json", + "passed": true + }, + { + "case_id": "07-scene-without-history", + "events_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/07-scene-without-history/events.json", + "passed": true + }, + { + "case_id": "08-fulfillment", + "events_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/08-fulfillment/events.json", + "passed": true + }, + { + "case_id": "09-admission-negatives", + "events_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/09-admission-negatives/events.json", + "passed": true + } + ] + }, + "supplemental_native_step_crash": { + "attempted": 1, + "classification": "Supplemental actual process fault with observation-only real native-step tracing", + "cleanup": { + "all_owned_processes_exited": true, + "automatic_socket_remaining": false, + "kind": "cleanup", + "owned_processes": [ + { + "exit_code": -9, + "pid": 11039 + }, + { + "exit_code": 0, + "pid": 11049 + } + ], + "time_unix": 1791671435.950979 + }, + "declared_limit": "Observed first native step only; no unretained final pose or actual hardware recovery claim", + "driver_sha256": "29bc280eb848b7ecee7646458ed84544e6187223c481d8d235b7cebc3f2fd2aa", + "events_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4parent/10-confirmed-native-kill/events.json", + "frozen_runtime_commit": "5713173", + "kill_event": { + "kind": "live_process_signal", + "pid": 11039, + "signal": "SIGKILL", + "time_unix": 1791671435.1098812 + }, + "native_state_sha256": { + "state-000000.json": "75efe776a84f3d408c1afbbedaacf4458bb57353a8ba16a82a5434a7f81d458f" + }, + "native_step_proof": { + "caller_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/workcell.py", + "caller_function": "motion", + "native_inputs_outputs_modified": false, + "original_native_step_returned": true, + "pid": 11039, + "thread": "native-controller-operation", + "time_unix": 1791671435.105224 + }, + "passed": true, + "prediction_commit": "578d73d", + "recovered_status": { + "accepted_at": 1791671435.084705, + "accepted_incarnation": "f53490f373c745bf8b684658ffd87685", + "active_operation_id": null, + "cancel_requested": false, + "controller_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4parent/10-confirmed-native-kill/c", + "current_health": "blocked", + "current_incarnation": "863316e3053f44b28757f533b25fc3fd", + "finished_at": 1791671435.210648, + "historical_outcome": "unknown", + "image_generation": 0, + "operation_id": "confirmed-kill", + "phase": "unknown", + "pid": 11049, + "proposal_sha256": "8eb20509c915d7f1fe506df1cfc1e6dabe8ed7f2f3abd94963176ef0206beec0", + "reason": "no exact terminal image/native receipt; unfinished work will not replay", + "reconciled": false, + "replayed": false, + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4parent/10-confirmed-native-kill/r", + "source_kind": "mujoco_simulation" + } + }, + "parent_read_only_scoring": { + "unchanged_run_file_hashes": true, + "disconnect_single_native_effect": true, + "queued_cancel_no_native_effect": true, + "running_cancel_exact_retained_state_and_velocity_verified": true, + "running_cancel_native_steps_before_cancel": 156, + "running_cancel_settle_steps": 500, + "independent_native_velocity_readback": { + "block_linear_m_s": 1.4834374904384974e-10, + "block_angular_rad_s": 3.572847576536275e-09, + "pusher_m_s": 4.0626940998161883e-07 + }, + "satisfied_scene_without_history_stays_unknown": true, + "fulfillment_native_condition": true, + "historical_original_order_exact_byte_check": { + "command": [ + "/Users/thomasmain/projects/footwork/jevdual/.venv/bin/footwork", + "check-artifact", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/08-fulfillment/original-order.json", + "--expected-file", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/08-fulfillment/r/expected-order.json", + "--expected-sha256", + "f0da7633fe1c3691bc91abe270769c61d669514a4702771895fb356c67206b06" + ], + "exit_code": 0, + "result": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1260, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/08-fulfillment/r/expected-order.json", + "expected_sha256": "f0da7633fe1c3691bc91abe270769c61d669514a4702771895fb356c67206b06", + "limit_bytes": 1048576, + "observed_bytes": 1260, + "observed_sha256": "f0da7633fe1c3691bc91abe270769c61d669514a4702771895fb356c67206b06", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s4audit/08-fulfillment/original-order.json", + "source_sha256": "f0da7633fe1c3691bc91abe270769c61d669514a4702771895fb356c67206b06" + } + }, + "current_fulfillment_file_intentionally_tampered_and_blocked": true, + "killed_runs_generation_zero_no_terminal_native_receipt_or_replay": true + }, + "required_lifecycle_scenarios_qualified": 8, + "required_lifecycle_scenarios_total": 8, + "all_owned_processes_exited": true, + "frozen_hashes_unchanged": true, + "limits": [ + "Controlled lost-controller-record and missing-history faults are offline retained-file perturbations, not live write-window crashes.", + "During killed motion, native progress is timed from public running status; no native pose inspection guides decisions. Parent separate scoring can inspect private history.", + "Process-fault durability only; no machine/power-loss, real hardware or application handoff qualification.", + "Original running-kill trial alone does not prove a native step; supplemental observation-only tracing closes that required evidence gap.", + "Historical original-order bytes were checked after the explicit current-artifact tamper; current tampered bytes are not claimed correct." + ] +} diff --git a/jevdual/results/independent-controller-20261010/report.md b/jevdual/results/independent-controller-20261010/report.md new file mode 100644 index 0000000..82bbd80 --- /dev/null +++ b/jevdual/results/independent-controller-20261010/report.md @@ -0,0 +1,118 @@ +# SIM4: Independent simulated controller recovery + +The simulated body now has a separate process owner. A caller can submit an image-bound +operation, disconnect and return to its retained result without repeating movement. +Cancellation, completed history and current state have distinct evidence. Restart resolves +a lost controller result only when exact lower-layer receipts support it; otherwise the +operation stays unknown and blocks effects. + +Frozen runtime: `5713173`. Original preregistration: `c78d2a4`; corrected prediction: +`637666c`; supplemental observation prediction: `578d73d`. All 68 scoped native/renderer +and process tests passed, including 18 controller scenarios; Ruff passed. The independent +driver ran nine public-service checks once each, followed by one fresh supplemental crash +probe. Together they support all eight required lifecycle scenarios, including deliberately +blocked outcomes. This is a small process diagnostic, not a reliability estimate or AI benchmark. + +| Required scenario | Observed outcome | +|---|---| +| Caller disconnects before receiving reply | Controller finished; same ID returned exact history, generation and state without another effect | +| Caller returns during work | Status stayed nonterminal; observe returned busy without an authoritative image | +| Cancel before dispatch | Cancelled, with no native operation or generation change | +| Cancel during movement | 156 native steps before cancellation, 500 settling steps, retained generation and independently checked velocity limits | +| Lost controller terminal result | Controlled saved unknown record reconciled from intact exact image/native receipts under a new incarnation | +| Controller dies during movement | Restart stayed unknown; no replay or stale-incarnation effect; supplemental trace confirmed a real native step before death | +| Satisfied scene without execution history | Native condition held, but missing receipts kept controller history unknown | +| Physical fulfillment and computer result | Four contact movements followed by fresh visual/native completion and a Footwork-checked saved order | + +Additional admission checks covered malformed/conflicting identity, stale frame/incarnation, +exclusive ownership and changed bindings. Deliberate artifact-byte tamper preserved historical +staging while current checking failed and new work was refused. + +## Implementation and use + +[controller.py](../../../tools/physical-workcell/controller.py) exposes private Unix-socket +health, observe, submit, status and cancel commands. The service owns both its controller +directory lock and the episode lock for its lifetime. Clients bind effect requests to the +current incarnation and an operation ID with exact proposal bytes. A second owner and legacy +episode commands cannot race it. Acceptance and dispatch intent are persisted before their +respective acknowledgement and execution. + +The existing image gate admits proposals. The controller chooses no target and provides no +native pose guidance. Native stepping runs in a worker; rendering stays on the service thread +when idle. Optional diagnostic pacing changes wall time, not simulation timestep or command +bounds. Default legacy motion behavior remains unchanged. + +During cancellation, the controller holds the last actuator command and settles through real +native dynamics. It does not assign position or velocity. A cancelled goal can include executed +partial movement; it does not mean the original target was reached. Stop evidence binds the +retained generation and state digest. The independent parent restored that state and measured +block linear speed below 0.002 m/s, angular speed below 0.02 rad/s and pusher speed below +0.002 m/s. These are simulated velocity predicates, not hardware interlocks. + +History stays readable when current integration state is unavailable. Fresh observation and +new effects remain withheld. Exact recovery checks proposal/frame/source/case, native request, +work-order/model and historical integration-state digests. It never dispatches during restart +and never substitutes current placement for missing execution history. + +SIGTERM requests cooperative cancellation and joins the worker. If its bounded join expires, +the whole owning process aborts before either lock scope unwinds. Unfinished history remains +unknown, with no stopped claim. Restart handles the owned stale socket. This avoids a live +worker writing after ownership was released. + +Use the [public controller recipe](../../../tools/physical-workcell/CONTROLLER.md) with a +fresh initialized episode and a separate private controller directory. Code remains Python +inside `tools/physical-workcell`, with MuJoCo doing native physics. No core Footwork physical +adapter or extra runtime dependency was introduced. + +## Evidence and retained failures + +[qualification.json](qualification.json) contains the independent case summary, original +private evidence/driver paths and hashes, supplemental native-step proof, cleanup and parent +read-only scoring. [checks.json](checks.json) records the final 68-test suite and Ruff; +[source-freeze.json](source-freeze.json) identifies exact runtime/test/documentation hashes. + +The private reproducer and full public replies remain at +`tools/physical-workcell/output/s4audit/driver.py` and `verification.json`. The supplemental +driver/proof remain under `output/s4parent/`. Both preserve their owned inputs and processes. +Use a new namespace for another diagnostic; do not overwrite the retained runs. + +The original timed running-kill trial established dispatched uncertainty and no replay, but +could not prove native stepping from its post-crash files. A separately preregistered fresh +probe wrapped the real `mj_step` call, forwarded its inputs and return unchanged, and wrote +an observation-only marker after its first return. The marker identifies the killed PID and +the native motion caller before SIGKILL. Restart used the uninstrumented frozen controller. +Tracing added observation overhead; no source, native dynamics or proposal settings changed. +Both trials are retained, rather than presenting the first trial as stronger evidence. + +[development-attempts.json](development-attempts.json) retains failure summaries and private +log hashes. The initial driver had nine startup failures from incorrect socket discovery; +no goals were sent. Another driver assertion excluded a correctly blocked observation. +Product regressions failed before fixes for unknown-fallback reconciliation, live retained +binding validation and shutdown ownership. A source review also separated historical status +from unreadable current state and added directory sync before acceptance acknowledgement. +The pre-shutdown 67-test pass remains separately recorded: passing that suite did not cover +the ownership defect later reproduced with a slow read-only checker and shortened join. + +The first notebook prediction is marked failed; the corrected and supplemental predictions +are resolved against the frozen independent evidence. Controlled saved-record/history faults +and the shutdown timing injection are explicitly distinct from live socket loss and SIGKILL. + +## Limits and next decision + +All owned diagnostic processes exited and their automatic sockets were removed. Parent +native/file scoring preserved run-file hashes. The physical/software trial's original saved +order was independently checked again after its deliberate current-file tamper; the tampered +current file is not claimed correct. Raw negative-test artifacts remain intact. + +This qualifies simulated process ownership and bounded recovery paths. It does not qualify +power loss, hardware, a real camera, general perception, real-time scheduling or application +fulfillment. A native-only terminal receipt with a pending image wrapper stays unknown; this +package does not repair every lower-layer interruption window. The comparative value of AI +planning remains unmeasured here; diagnostic targets came from a deterministic RGB helper. +Screensaver/sleep behavior was not tested, and the workflow required no visible desktop. + +Adopt this controller in the experiment area. The next useful package is a versioned local +browser work-order application: bind an order, stage the part, verify placement, update that +same order and read its saved state. Include an order changed/cancelled during movement and +a lost application reply. Promote a physical adapter into Footwork core only when a real +capability-service caller needs the qualified lifecycle. diff --git a/jevdual/results/independent-controller-20261010/source-freeze.json b/jevdual/results/independent-controller-20261010/source-freeze.json new file mode 100644 index 0000000..594193f --- /dev/null +++ b/jevdual/results/independent-controller-20261010/source-freeze.json @@ -0,0 +1,14 @@ +{ + "source_commit": "57131732d548556d6c2687175e629422c1ae2686", + "corrected_prediction_commit": "637666c", + "sha256": { + "controller.py": "29131e49c0b19ec59b957cf52a56aa573242ea3d3ee6851ebf4627c52a8c1bfd", + "workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "episode_runner.py": "530c8e65d2b4c613e3c02c8dcc5e9f8d56559e5f119b011bb3e138667b1474e7", + "test_controller.py": "3cb5863cab8c8920f4d56e746823bbc4ca900e96336235f4357bea19a5205f32", + "CONTROLLER.md": "356f8a67255941932a20c91c7f58aaf7630fd6eb2415ebe4d5d4463a582b2742" + }, + "diagnostic_trials_started_at_freeze": false +} diff --git a/jevdual/results/independent-onboarding-preparation-20261010/packet-v1.json b/jevdual/results/independent-onboarding-preparation-20261010/packet-v1.json new file mode 100644 index 0000000..b27a785 --- /dev/null +++ b/jevdual/results/independent-onboarding-preparation-20261010/packet-v1.json @@ -0,0 +1,58 @@ +{ + "passed": true, + "source_commit": "f39b76a81559903addea4c712f999b4740970450", + "imports": { + "helpers": { + "capability_clean_install": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v1/unpacked/footwork/jevdual/scripts/capability_clean_install.py", + "capability_fixture": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v1/unpacked/footwork/jevdual/scripts/capability_fixture.py", + "capability_fixture_config": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v1/unpacked/footwork/jevdual/scripts/capability_fixture_config.py", + "capability_host_trial": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v1/unpacked/footwork/jevdual/scripts/capability_host_trial.py", + "capability_interrupt_trial": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v1/unpacked/footwork/jevdual/scripts/capability_interrupt_trial.py", + "capability_restart_trial": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v1/unpacked/footwork/jevdual/scripts/capability_restart_trial.py", + "capability_installed_trial": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v1/unpacked/footwork/jevdual/scripts/capability_installed_trial.py" + }, + "product": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/delivery-dependencies-py312-v1/venv/lib/python3.12/site-packages/jevdual/__init__.py", + "provider_imported": false + }, + "help_exits": { + "capability_clean_install.py": 0, + "capability_fixture.py": 0, + "capability_fixture_config.py": 0, + "capability_installed_trial.py": 0 + }, + "config_command": { + "command": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/delivery-dependencies-py312-v1/venv/bin/python", + "env": { + "PYTHONPATH": "", + "PYTHONHOME": "", + "PYTHONNOUSERSITE": "1", + "ANONYMIZED_TELEMETRY": "false", + "BROWSER_USE_CLOUD_SYNC": "false" + }, + "args": [ + "-I", + "-m", + "jevdual.capability.cli", + "server", + "--scope", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v1/unpacked/footwork/job-test/scope.json", + "--journal", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v1/unpacked/footwork/job-test/operations.sqlite" + ] + }, + "fresh_task_generated": true, + "journal_created": false, + "tamper_rejected": true, + "first_use_qualification": "not_performed", + "limitations": "Already installed dependency environment on existing Mac; helper transport/configuration only. Placeholder window IDs were not bound.", + "extra_member_rejected": true, + "py314": { + "helper_imports": 7, + "config_exit": 0, + "reuse_exit": 2, + "reuse_bytes_unchanged": true, + "recursive_input_exit": 2, + "invalid_run_not_created": true, + "private_config": true + } +} diff --git a/jevdual/results/independent-onboarding-preparation-20261010/packet-v2.json b/jevdual/results/independent-onboarding-preparation-20261010/packet-v2.json new file mode 100644 index 0000000..5afd802 --- /dev/null +++ b/jevdual/results/independent-onboarding-preparation-20261010/packet-v2.json @@ -0,0 +1,58 @@ +{ + "passed": true, + "source_commit": "aa971782964a42d12d1ea8a52d6617476e8e3c63", + "artifact": { + "verified": true, + "members": 13, + "sha256": "1eed24b44e1749ccee387a1677d89e43d4ac4616554eac4f80f02cb75e5d6a31", + "source_commit": "aa971782964a42d12d1ea8a52d6617476e8e3c63" + }, + "profiles": { + "312": { + "imports": { + "capability_clean_install": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_clean_install.py", + "capability_fixture": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_fixture.py", + "capability_fixture_config": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_fixture_config.py", + "capability_host_trial": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_host_trial.py", + "capability_interrupt_trial": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_interrupt_trial.py", + "capability_restart_trial": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_restart_trial.py", + "capability_installed_trial": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_installed_trial.py" + }, + "helps": { + "capability_clean_install.py": 0, + "capability_fixture.py": 0, + "capability_fixture_config.py": 0, + "capability_installed_trial.py": 0 + } + }, + "314": { + "imports": { + "capability_clean_install": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_clean_install.py", + "capability_fixture": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_fixture.py", + "capability_fixture_config": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_fixture_config.py", + "capability_host_trial": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_host_trial.py", + "capability_interrupt_trial": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_interrupt_trial.py", + "capability_restart_trial": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_restart_trial.py", + "capability_installed_trial": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-packet-smoke-v2/unpacked/footwork/jevdual/scripts/capability_installed_trial.py" + }, + "helps": { + "capability_clean_install.py": 0, + "capability_fixture.py": 0, + "capability_fixture_config.py": 0, + "capability_installed_trial.py": 0 + } + } + }, + "changed_payload_members_from_v1": [ + "footwork/MANIFEST.json", + "footwork/README.md", + "footwork/vm-plan.md" + ], + "runtime_wheel_and_helpers_unchanged": true, + "foreign_ref_rejected": true, + "old_packet_verifiable_with_frozen_ref": true, + "tamper_and_extra_member_rejected": true, + "first_use_qualification": "not_performed", + "claude_review": "queued_unread", + "limits": "Preparation only on existing Mac; no independent OS/TCC transition or native input." +} diff --git a/jevdual/results/independent-onboarding-preparation-20261010/report.md b/jevdual/results/independent-onboarding-preparation-20261010/report.md new file mode 100644 index 0000000..2a84956 --- /dev/null +++ b/jevdual/results/independent-onboarding-preparation-20261010/report.md @@ -0,0 +1,29 @@ +# Independent onboarding preparation + +The self-checked U2 packet is prepared from frozen source aa971782964a42d12d1ea8a52d6617476e8e3c63 on local branch codex/independent-onboarding. PR #2 remains at reviewed commit 7669d3d with four passing CI checks. No new PR, push, merge or registry publication occurred. + +The 13-member packet SHA256 is 1eed24b44e1749ccee387a1677d89e43d4ac4616554eac4f80f02cb75e5d6a31. Its wheel remains the reviewed 9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7 artifact. Only the packet manifest, runbook and VM-plan text changed between v1/v2; every qualification helper and runtime wheel byte stayed identical. + +Observed preparation checks: + +- Two frozen v2 builds match and verify every permitted Git-snapshot payload member, manifest digest and wheel RECORD/source inventory. +- All seven helpers import from the unpacked packet outside the checkout with installed Python 3.12.6 and 3.14.7 environments; four documented help entry points pass on both. Provider packages were not imported during the v1 probe. +- The new fixture-to-config helper creates a fresh private task/configuration with isolated commands. Placeholder resource IDs are configuration inputs only; no bind, native input or journal creation occurred. +- Malformed/reused configuration attempts refuse and preserve existing bytes. Changed helper bytes, an extra archive member and a foreign expected source commit are rejected. +- Frozen v1 can still be verified from the later checkout by explicitly selecting f39b76a. The initial verifier failed after the committed evidence-only 16c13ac change; aa971782 reads fixed payloads from an immutable Git snapshot and accepts an explicit expected commit. The earlier staging-path baseline did not changeHEAD and is not repair evidence. +- Seventeen selected notebook/packaging tests and Ruff pass. These are preparation checks, not native first-use qualification. + +Archive paths remain under output/u2-packet-v1, output/u2-packet-v2 and their separate repeat builds. Smoke environments, tampered negative artifacts and receipts remain under output/u2-packet-smoke-v1/v2. Reverify from the repository using the explicit frozen commit: + +~~~sh +uv run --no-sync python scripts/build_onboarding_packet.py \ + --wheel ../output/delivery-package-py312-v4/build-one/jevdual-0.0.1-py3-none-any.whl \ + --verify ../output/u2-packet-v2/footwork-onboarding-u2.zip \ + --source-commit aa971782964a42d12d1ea8a52d6617476e8e3c63 +~~~ + +The packet's guides require an independently provisioned SIP-enabled Mac/guest and an observed denied Accessibility baseline, then an explicit human permission transition from the actual responsible host, actual installed native oracle/restart/report and exact cleanup. Chrome, TextEdit and compatible Python/uv must be present. Screen Recording remains advisory for the DOM/AX slice and is not inferred from an AX success. + +Reference mechanisms came from local Cua's separate SIP-on permission lane, responsible-host guidance and Arc's Accessibility-only route. The proposed [Lume VM setup](../../docs/onboarding-vm-plan.md) was checked against the [quickstart](https://cua.ai/docs/lume/quickstart) and [CLI reference](https://cua.ai/docs/lume/reference/cli/vms); it is not locally executed. This host had 7.9 GiB free disk and neither launcher on PATH. Lume's about 50 GB free-disk prerequisite makes storage selection necessary before VM creation. + +Independent environment selection and actual U2 runtime qualification remain pending. Claude review was requested through the authorized mailbox but is still queued/unread at this recording boundary; there is no independent approval claim. The user-authorized two-hour caffeinate hold is running PID 40049, from about 19:15 to 21:15 EDT, with idle system/display and user-active assertions verified. The lock probe reported unlocked after launch. Persistent sleep/lock settings and OS permissions were not changed. diff --git a/jevdual/results/independent-onboarding-preparation-20261010/summary.json b/jevdual/results/independent-onboarding-preparation-20261010/summary.json new file mode 100644 index 0000000..6edd750 --- /dev/null +++ b/jevdual/results/independent-onboarding-preparation-20261010/summary.json @@ -0,0 +1,47 @@ +{ + "source_commit": "aa971782964a42d12d1ea8a52d6617476e8e3c63", + "branch": "codex/independent-onboarding", + "product_pr": "https://github.com/Tom-R-Main/Footwork/pull/2", + "product_pr_head": "7669d3db3a02edadd1a9cc65479d6f737544f4fc", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7", + "packet_sha256": "1eed24b44e1749ccee387a1677d89e43d4ac4616554eac4f80f02cb75e5d6a31", + "packet_members": 13, + "local_transport_checks": "passed", + "first_use_qualification": "not_performed", + "independent_environment": "not_selected", + "claude_review": "requested_queued_unread", + "claude_request_id": "1791674683421027000", + "selected_tests": 17, + "ruff": "passed", + "native_input": false, + "vm_created": false, + "os_permission_changes": false, + "host_disk_available_gib_observed": 7.9, + "hold": { + "pid": 40049, + "argv": [ + "/usr/bin/caffeinate", + "-diu", + "-t", + "7200" + ], + "started_local": "2026-10-10 19:15:03 EDT", + "expires_approximately_local": "2026-10-10 21:15:03 EDT", + "persistent_settings_changed": false + }, + "limits": [ + "Transport/configuration checked with already installed dependencies on existing Mac, not an independent OS permission transition.", + "Placeholder resource IDs were never bound or used for input.", + "VM launcher installation, restore image and storage choices remain pending.", + "Independent Claude verdict unavailable." + ], + "retained_attempts": { + "initial_frozen_verifier_baseline": "A staging-path failure left HEAD at f39b76a, so the initial baseline did not exercise a changed-HEAD condition; not used as repair evidence.", + "committed_frozen_verifier_baseline": { + "head": "16c13acf8649c13a364286669623837ae61f89b4", + "exit_code": 1, + "log": "output/u2-frozen-verifier-baseline-committed.log" + }, + "frozen_ref_remedy": "aa971782 verifies v1 with its explicit f39b76a ref, and rejects a foreign ref for v2." + } +} diff --git a/jevdual/results/installed-agent-release-20261012-v2/cleanup.json b/jevdual/results/installed-agent-release-20261012-v2/cleanup.json new file mode 100644 index 0000000..19b7232 --- /dev/null +++ b/jevdual/results/installed-agent-release-20261012-v2/cleanup.json @@ -0,0 +1,25 @@ +{ + "document": { + "status": "confirmed", + "reason": "window_disappeared", + "pid": 73067, + "file_before": { + "status": "observed", + "size_bytes": 15, + "sha256": "ac11060594d5bcde616f5e88156ea7c0f55e2cca74c2de52c4da375fbde33339" + }, + "file_after": { + "status": "observed", + "size_bytes": 24, + "sha256": "783172d2e6ac11f2c5450015856703ebe655b8c2aa86ca81b802ab1d8d210aee" + } + }, + "browser": { + "status": "confirmed", + "pid": 89354, + "returncode": 0 + }, + "listener": { + "status": "confirmed" + } +} diff --git a/jevdual/results/installed-agent-release-20261012-v2/document-absence.json b/jevdual/results/installed-agent-release-20261012-v2/document-absence.json new file mode 100644 index 0000000..eae6e4b --- /dev/null +++ b/jevdual/results/installed-agent-release-20261012-v2/document-absence.json @@ -0,0 +1,7 @@ +{ + "AXWindows_error": 0, + "windows_scanned": 0, + "owned_matches": 0, + "unreadable_errors": [], + "confirmed_absent": true +} diff --git a/jevdual/results/installed-agent-release-20261012-v2/host-receipt.json b/jevdual/results/installed-agent-release-20261012-v2/host-receipt.json new file mode 100644 index 0000000..4f31437 --- /dev/null +++ b/jevdual/results/installed-agent-release-20261012-v2/host-receipt.json @@ -0,0 +1,264 @@ +{ + "passed": true, + "source_commit": "d52d32d0dd6f6fd0b308ea6b9c6a271597c2fc39", + "effect_retry": false, + "configured_softcap_usd": 1.0, + "scheduling_reserve_usd": 2.0, + "limits": "One Claude Code host, existing authenticated account and existing macOS permissions. No general host, clean OS or disk persistence qualification.", + "oracle_before": "Invoice report\n", + "returncode": 0, + "timed_out": false, + "host": { + "valid_environment": true, + "tools_offered": [ + "StructuredOutput", + "mcp__footwork__act", + "mcp__footwork__bind", + "mcp__footwork__observe", + "mcp__footwork__report", + "mcp__footwork__status" + ], + "mcp_servers": [ + { + "name": "footwork", + "status": "connected", + "source": "dynamic" + } + ], + "model": "claude-opus-5-5", + "host_version": "2.1.296", + "calls": [ + "mcp__footwork__bind", + "mcp__footwork__bind", + "mcp__footwork__status", + "mcp__footwork__act", + "mcp__footwork__observe", + "mcp__footwork__report", + "StructuredOutput" + ], + "out_of_arm": [], + "final": "{\"completion\":\"completed\",\"operations\":[{\"operation\":\"op_741d45670d09\",\"disposition\":\"applied\"}],\"persistence\":\"not_checked\"}", + "result_is_error": false, + "result_subtype": "success", + "cost_usd": 0.09900719999999999, + "model_usage": { + "claude-opus-5-5": { + "inputTokens": 10, + "outputTokens": 1029, + "cacheReadInputTokens": 29336, + "cacheCreationInputTokens": 9065, + "webSearchRequests": 0, + "costUSD": 0.09900719999999999, + "contextWindow": 1000000, + "maxOutputTokens": 128000, + "thinkingTokens": 419, + "canonicalModel": "claude-opus-5-5", + "provider": "firstParty", + "costBasis": "list" + } + }, + "claim_review": null, + "schema_finalizer_offered": true, + "effect_calls": [ + "mcp__footwork__bind", + "mcp__footwork__bind", + "mcp__footwork__status", + "mcp__footwork__act", + "mcp__footwork__observe", + "mcp__footwork__report" + ], + "structured_output_present": true, + "budget": { + "configured_softcap_usd": 1.0, + "scheduling_reserve_usd": 2.0, + "reported_cost_usd": 0.09900719999999999, + "available": true, + "finite": true, + "valid": true + } + }, + "report": { + "accepted": true, + "returncode": 0, + "validated": { + "schema_version": "1", + "scope": "all_admitted_task_operations", + "validation": { + "accepted": true, + "reasons": [] + }, + "operation_completion": "completed", + "operations": [ + { + "operation": "op_741d45670d09", + "state": "final", + "disposition": "applied", + "updated": 1791835734.906624, + "created": 1791835732.11695 + } + ], + "persistence": "not_checked", + "current_state": "not_checked", + "task": "task_1ea760ed83ec4c7a88cdee3e2a62da40", + "snapshot_digest": "8afdbaa2a4f2cb3703dc1c4503c0dd5e38dcc200ce3b4b269d6c70802b54a6da", + "ledger_span": { + "first_created": 1791835732.11695, + "last_updated": 1791835734.906624, + "count": 1 + }, + "accepted_host_claim": "completed", + "as_of": 1791835751.512595, + "trusted_summary": "1 recorded operations confirmed applied; 0 attempts confirmed no input. These are historical effects, not certification of the host job. Full task ledger: 1 records, 2026-10-12T20:08:52.116950+00:00 through 2026-10-12T20:08:54.906624+00:00. Current document state and disk persistence are not certified by this report." + } + }, + "worker_absent": true, + "journal_rows": [ + { + "id": "op_741d45670d09", + "state": "final", + "disposition": "applied", + "phase": "sent", + "execution_stage": "started" + } + ], + "runtime": { + "contexts": [ + { + "role": "server", + "operation": null, + "pid": 95359, + "python": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": null, + "mcp": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/mcp/__init__.py" + ], + "ApplicationServices": null, + "Quartz": null, + "Foundation": null + }, + "cua_library": null, + "home": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791835719881086000 + }, + { + "role": "server", + "operation": null, + "pid": 95359, + "python": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/cua_driver/__init__.py" + ], + "mcp": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/mcp/__init__.py" + ], + "ApplicationServices": null, + "Quartz": null, + "Foundation": null + }, + "cua_library": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/cua_driver/libcua_driver_sdk.dylib", + "home": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791835723113013000 + }, + { + "role": "server", + "operation": null, + "pid": 95359, + "python": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/cua_driver/__init__.py" + ], + "mcp": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/mcp/__init__.py" + ], + "ApplicationServices": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/ApplicationServices/__init__.py" + ], + "Quartz": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/Quartz/__init__.py" + ], + "Foundation": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/Foundation/__init__.py" + ] + }, + "cua_library": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/cua_driver/libcua_driver_sdk.dylib", + "home": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791835725507991000 + }, + { + "role": "worker", + "operation": "op_741d45670d09", + "pid": 97620, + "python": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/cua_driver/__init__.py" + ], + "mcp": null, + "ApplicationServices": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/ApplicationServices/__init__.py" + ], + "Quartz": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/Quartz/__init__.py" + ], + "Foundation": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/Foundation/__init__.py" + ] + }, + "cua_library": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/cua_driver/libcua_driver_sdk.dylib", + "home": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791835733222761000 + } + ], + "registrations": [ + { + "nonce": "7428a642714149f1857b3f834409d85f", + "operation_id": "op_741d45670d09", + "packet_digest": "39c0b829d152a5d7abd90149d7b0b91e9d9fcfd3747a39a85773c4c8d5b52575", + "pgid": 97620, + "pid": 97620, + "request_digest": "334befe675b6348da305da0b918336318e92c708c1d9b34f0e42e7d733d00954", + "start": "0x1.ab34f950798eep+30", + "version": 1 + } + ], + "failures": [] + }, + "server_absent": true, + "oracle_actual": "Invoice report\n17.25 USD", + "elapsed_seconds": 33.11132416711189 +} diff --git a/jevdual/results/installed-agent-release-20261012-v2/host-report.json b/jevdual/results/installed-agent-release-20261012-v2/host-report.json new file mode 100644 index 0000000..2d01ea2 --- /dev/null +++ b/jevdual/results/installed-agent-release-20261012-v2/host-report.json @@ -0,0 +1,10 @@ +{ + "completion": "completed", + "operations": [ + { + "operation": "op_741d45670d09", + "disposition": "applied" + } + ], + "persistence": "not_checked" +} diff --git a/jevdual/results/installed-agent-release-20261012-v2/install-receipt.json b/jevdual/results/installed-agent-release-20261012-v2/install-receipt.json new file mode 100644 index 0000000..003ef51 --- /dev/null +++ b/jevdual/results/installed-agent-release-20261012-v2/install-receipt.json @@ -0,0 +1,1519 @@ +{ + "source_commit": "445a348ff31243c2f3d90baae9ce209d89bd7eac", + "host": { + "platform": "Darwin", + "machine": "arm64" + }, + "interpreter_requested": "/Users/thomasmain/projects/footwork/jevdual/.venv/bin/python", + "wheel": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/artifacts/jevdual-0.0.1-py3-none-any.whl", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7", + "cache_initially_empty": true, + "system_site_packages": false, + "borrowed_dependencies": false, + "registry": "https://pypi.org/simple", + "binary_only": true, + "commands": [ + { + "stage": "venv", + "argv": [ + "/opt/homebrew/bin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "venv", + "--python", + "/Users/thomasmain/projects/footwork/jevdual/.venv/bin/python", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv" + ], + "returncode": 0 + }, + { + "stage": "install", + "argv": [ + "/opt/homebrew/bin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "pip", + "install", + "--python", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/bin/python", + "--default-index", + "https://pypi.org/simple", + "--only-binary", + ":all:", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/artifacts/jevdual-0.0.1-py3-none-any.whl" + ], + "returncode": 0 + }, + { + "stage": "consistency", + "argv": [ + "/opt/homebrew/bin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "pip", + "check", + "--python", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/bin/python" + ], + "returncode": 0 + }, + { + "stage": "inventory", + "argv": [ + "/opt/homebrew/bin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "pip", + "list", + "--python", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/bin/python", + "--format", + "json" + ], + "returncode": 0 + }, + { + "stage": "origins", + "argv": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/bin/python", + "-I", + "-c", + "\nimport sys,json,importlib.metadata,importlib\nfrom pathlib import Path\nimport jevdual,jevdual.capability.execution_worker as worker\nfrom jevdual._native import BACKEND\nnames=(\"cua_driver\",\"psutil\",\"mcp\",\"ApplicationServices\",\"Quartz\",\"Foundation\",\"packaging\")\nmodules={n:str(importlib.import_module(n).__file__) for n in names}\nprint(json.dumps({\"version\":sys.version,\"prefix\":sys.prefix,\"base_prefix\":sys.base_prefix,\"python\":sys.executable,\"package\":jevdual.__file__,\"worker\":worker.__file__,\"backend\":BACKEND,\"module_origins\":modules,\"sys_path\":sys.path}))\n" + ], + "returncode": 0 + }, + { + "stage": "doctor", + "argv": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/bin/footwork-capability", + "doctor" + ], + "returncode": 0 + }, + { + "stage": "wheel-tags", + "argv": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/bin/python", + "-I", + "-c", + "import json,importlib.metadata;print(json.dumps([{\"name\":d.metadata[\"Name\"],\"version\":d.version,\"tags\":[l[5:] for l in (d.read_text(\"WHEEL\") or \"\").splitlines() if l.startswith(\"Tag: \")]} for d in importlib.metadata.distributions()]))" + ], + "returncode": 0 + } + ], + "passed": true, + "limits": "Fresh dependencies on existing host OS/interpreter, not a fresh machine or TCC permission database.", + "origins": { + "version": "3.12.6 (v3.12.6:a4a2d2b0d85, Sep 6 2024, 16:08:03) [Clang 13.0.0 (clang-1300.0.29.30)]", + "prefix": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv", + "base_prefix": "/Library/Frameworks/Python.framework/Versions/3.12", + "python": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/bin/python", + "package": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/jevdual/__init__.py", + "worker": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/jevdual/capability/execution_worker.py", + "backend": "pure-python", + "module_origins": { + "cua_driver": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/cua_driver/__init__.py", + "psutil": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/psutil/__init__.py", + "mcp": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/mcp/__init__.py", + "ApplicationServices": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/ApplicationServices/__init__.py", + "Quartz": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/Quartz/__init__.py", + "Foundation": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/Foundation/__init__.py", + "packaging": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/packaging/__init__.py" + }, + "sys_path": [ + "/Library/Frameworks/Python.framework/Versions/3.12/lib/python312.zip", + "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12", + "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages" + ] + }, + "doctor": { + "platform": "darwin", + "supported": true, + "runtime": { + "python_version": "3.12.6", + "python_requirement": ">=3.11,<4.0", + "python_compatible": true, + "interpreter": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/bin/python", + "architecture": "arm64", + "distribution": { + "version": "0.0.1", + "location": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages" + }, + "loaded_modules": { + "jevdual": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/jevdual/__init__.py", + "jevdual.capability.setup": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/jevdual/capability/setup.py", + "ApplicationServices": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/ApplicationServices/__init__.py", + "Quartz": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages/Quartz/__init__.py" + } + }, + "dependencies": { + "browser-use": { + "version": "0.13.10", + "requirement": "==0.13.10", + "compatible": true, + "location": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages" + }, + "typesafe-sdk": { + "version": "0.7.4", + "requirement": "<1,>=0.7", + "compatible": true, + "location": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages" + }, + "orjson": { + "version": "3.13.0", + "requirement": ">=3.10", + "compatible": true, + "location": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages" + }, + "psutil": { + "version": "7.2.2", + "requirement": "==7.2.2", + "compatible": true, + "location": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages" + }, + "mcp": { + "version": "2.1.1", + "requirement": "==2.1.1", + "compatible": true, + "location": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages" + }, + "packaging": { + "version": "26.3", + "requirement": "<27,>=25", + "compatible": true, + "location": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages" + }, + "cua-driver": { + "version": "0.28.2", + "requirement": "==0.28.2", + "compatible": true, + "location": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages" + }, + "pyobjc-framework-applicationservices": { + "version": "12.2.2", + "requirement": ">=12", + "compatible": true, + "location": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages" + }, + "pyobjc-framework-quartz": { + "version": "12.2.2", + "requirement": ">=12", + "compatible": true, + "location": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages" + }, + "pyobjc-framework-cocoa": { + "version": "12.2.2", + "requirement": ">=12", + "compatible": true, + "location": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/venv/lib/python3.12/site-packages" + } + }, + "permissions": { + "accessibility": "granted", + "screen_recording": "granted" + }, + "scope": "not_checked", + "journal": "not_checked", + "warnings": [], + "dependency_metadata": "installed distribution", + "limitation": "macOS only: one loopback HTTP browser fixture and one identified TextEdit document. Window identity is checked when binding.", + "provider_account_required": false, + "rust_toolchain_required": false, + "blockers": [], + "prerequisite_status": "met", + "qualification": "Prerequisites only. Window identity, current state, effects and job completion require separate checks." + }, + "inventory": [ + { + "name": "aiofiles", + "version": "25.1.0" + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1" + }, + { + "name": "aiohttp", + "version": "3.14.3" + }, + { + "name": "aiosignal", + "version": "1.4.0" + }, + { + "name": "annotated-types", + "version": "0.8.0" + }, + { + "name": "anthropic", + "version": "0.76.0" + }, + { + "name": "anyio", + "version": "4.12.1" + }, + { + "name": "attrs", + "version": "26.1.0" + }, + { + "name": "backoff", + "version": "2.2.1" + }, + { + "name": "beautifulsoup4", + "version": "4.15.0" + }, + { + "name": "browser-harness", + "version": "0.1.13" + }, + { + "name": "browser-use", + "version": "0.13.10" + }, + { + "name": "browser-use-sdk", + "version": "3.4.2" + }, + { + "name": "bubus", + "version": "1.5.6" + }, + { + "name": "cdp-use", + "version": "1.4.5" + }, + { + "name": "certifi", + "version": "2026.7.22" + }, + { + "name": "cffi", + "version": "2.1.1" + }, + { + "name": "charset-normalizer", + "version": "3.5.2" + }, + { + "name": "click", + "version": "8.3.3" + }, + { + "name": "cloudpickle", + "version": "3.1.2" + }, + { + "name": "cryptography", + "version": "50.0.2" + }, + { + "name": "cua-driver", + "version": "0.28.2" + }, + { + "name": "cython", + "version": "3.3.0" + }, + { + "name": "distro", + "version": "1.9.0" + }, + { + "name": "docstring-parser", + "version": "0.18.0" + }, + { + "name": "fetch-use", + "version": "0.4.0" + }, + { + "name": "frozenlist", + "version": "1.8.0" + }, + { + "name": "google-api-core", + "version": "2.29.0" + }, + { + "name": "google-api-python-client", + "version": "2.188.0" + }, + { + "name": "google-auth", + "version": "2.48.0" + }, + { + "name": "google-auth-httplib2", + "version": "0.4.4" + }, + { + "name": "google-auth-oauthlib", + "version": "1.2.4" + }, + { + "name": "google-genai", + "version": "1.65.0" + }, + { + "name": "googleapis-common-protos", + "version": "1.75.5" + }, + { + "name": "groq", + "version": "1.0.0" + }, + { + "name": "h11", + "version": "0.16.0" + }, + { + "name": "httpcore", + "version": "1.0.9" + }, + { + "name": "httpcore2", + "version": "2.13.1" + }, + { + "name": "httplib2", + "version": "0.32.0" + }, + { + "name": "httpx", + "version": "0.28.1" + }, + { + "name": "httpx2", + "version": "2.13.1" + }, + { + "name": "idna", + "version": "3.20" + }, + { + "name": "inquirerpy", + "version": "0.3.4" + }, + { + "name": "jevdual", + "version": "0.0.1" + }, + { + "name": "jiter", + "version": "0.17.0" + }, + { + "name": "jsonschema", + "version": "4.26.0" + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1" + }, + { + "name": "lxml", + "version": "6.1.3" + }, + { + "name": "markdown-it-py", + "version": "4.2.0" + }, + { + "name": "markdownify", + "version": "1.2.2" + }, + { + "name": "mcp", + "version": "2.1.1" + }, + { + "name": "mcp-types", + "version": "2.1.1" + }, + { + "name": "mdurl", + "version": "0.1.2" + }, + { + "name": "multidict", + "version": "6.9.1" + }, + { + "name": "oauthlib", + "version": "4.0.0" + }, + { + "name": "ollama", + "version": "0.6.1" + }, + { + "name": "openai", + "version": "2.26.0" + }, + { + "name": "opentelemetry-api", + "version": "1.45.1" + }, + { + "name": "orjson", + "version": "3.13.0" + }, + { + "name": "packaging", + "version": "26.3" + }, + { + "name": "pfzy", + "version": "0.3.4" + }, + { + "name": "pillow", + "version": "12.3.0" + }, + { + "name": "portalocker", + "version": "4.4.0" + }, + { + "name": "posthog", + "version": "7.7.0" + }, + { + "name": "prompt-toolkit", + "version": "3.0.53" + }, + { + "name": "propcache", + "version": "0.5.4" + }, + { + "name": "proto-plus", + "version": "1.29.0" + }, + { + "name": "protobuf", + "version": "6.33.6" + }, + { + "name": "psutil", + "version": "7.2.2" + }, + { + "name": "pyasn1", + "version": "0.6.4" + }, + { + "name": "pyasn1-modules", + "version": "0.4.2" + }, + { + "name": "pycparser", + "version": "3.11" + }, + { + "name": "pydantic", + "version": "2.13.5" + }, + { + "name": "pydantic-core", + "version": "2.46.5" + }, + { + "name": "pydantic-settings", + "version": "2.15.0" + }, + { + "name": "pygments", + "version": "2.21.0" + }, + { + "name": "pyjwt", + "version": "2.15.1" + }, + { + "name": "pyobjc-core", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-applicationservices", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-cocoa", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-coretext", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-quartz", + "version": "12.2.2" + }, + { + "name": "pyotp", + "version": "2.9.0" + }, + { + "name": "pyparsing", + "version": "3.3.3" + }, + { + "name": "pypdf", + "version": "6.16.2" + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0" + }, + { + "name": "python-docx", + "version": "1.2.0" + }, + { + "name": "python-dotenv", + "version": "1.2.2" + }, + { + "name": "python-multipart", + "version": "0.0.32" + }, + { + "name": "referencing", + "version": "0.37.0" + }, + { + "name": "reportlab", + "version": "4.4.9" + }, + { + "name": "requests", + "version": "2.33.0" + }, + { + "name": "requests-oauthlib", + "version": "2.0.0" + }, + { + "name": "rich", + "version": "14.3.3" + }, + { + "name": "rpds-py", + "version": "2026.9.1" + }, + { + "name": "rsa", + "version": "4.9.1" + }, + { + "name": "screeninfo", + "version": "0.8.1" + }, + { + "name": "six", + "version": "1.17.0" + }, + { + "name": "sniffio", + "version": "1.3.1" + }, + { + "name": "soupsieve", + "version": "3.0.3" + }, + { + "name": "sse-starlette", + "version": "3.5.0" + }, + { + "name": "starlette", + "version": "1.7.0" + }, + { + "name": "tenacity", + "version": "9.1.4" + }, + { + "name": "tqdm", + "version": "4.70.1" + }, + { + "name": "truststore", + "version": "0.10.4" + }, + { + "name": "typesafe-sdk", + "version": "0.7.4" + }, + { + "name": "typing-extensions", + "version": "4.15.0" + }, + { + "name": "typing-inspection", + "version": "0.4.4" + }, + { + "name": "uritemplate", + "version": "4.2.0" + }, + { + "name": "urllib3", + "version": "2.8.0" + }, + { + "name": "uuid7", + "version": "0.1.0" + }, + { + "name": "uvicorn", + "version": "0.54.0" + }, + { + "name": "wcwidth", + "version": "0.9.2" + }, + { + "name": "websockets", + "version": "15.0.1" + }, + { + "name": "yarl", + "version": "1.25.1" + } + ], + "wheel_tags": [ + { + "name": "anyio", + "version": "4.12.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "uvicorn", + "version": "0.54.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "groq", + "version": "1.0.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "oauthlib", + "version": "4.0.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "cloudpickle", + "version": "3.1.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-framework-CoreText", + "version": "12.2.2", + "tags": [ + "cp312-cp312-macosx_10_13_universal2" + ] + }, + { + "name": "portalocker", + "version": "4.4.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "lxml", + "version": "6.1.3", + "tags": [ + "cp312-cp312-macosx_10_13_universal2" + ] + }, + { + "name": "packaging", + "version": "26.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "Pygments", + "version": "2.21.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "inquirerpy", + "version": "0.3.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "browser-harness", + "version": "0.1.13", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "prompt_toolkit", + "version": "3.0.53", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "soupsieve", + "version": "3.0.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jiter", + "version": "0.17.0", + "tags": [ + "cp312-cp312-macosx_11_0_arm64" + ] + }, + { + "name": "typesafe-sdk", + "version": "0.7.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jevdual", + "version": "0.0.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-api-python-client", + "version": "2.188.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "yarl", + "version": "1.25.1", + "tags": [ + "cp312-cp312-macosx_11_0_arm64" + ] + }, + { + "name": "posthog", + "version": "7.7.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jsonschema", + "version": "4.26.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "orjson", + "version": "3.13.0", + "tags": [ + "cp312-cp312-macosx_15_0_arm64" + ] + }, + { + "name": "google-auth-oauthlib", + "version": "1.2.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "requests", + "version": "2.33.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "PyJWT", + "version": "2.15.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "opentelemetry-api", + "version": "1.45.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pydantic_core", + "version": "2.46.5", + "tags": [ + "cp312-cp312-macosx_11_0_arm64" + ] + }, + { + "name": "browser-use-sdk", + "version": "3.4.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyotp", + "version": "2.9.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "docstring_parser", + "version": "0.18.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "propcache", + "version": "0.5.4", + "tags": [ + "cp312-cp312-macosx_11_0_arm64" + ] + }, + { + "name": "mcp", + "version": "2.1.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "rich", + "version": "14.3.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "uritemplate", + "version": "4.2.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "reportlab", + "version": "4.4.9", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "tenacity", + "version": "9.1.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "aiofiles", + "version": "25.1.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-framework-Quartz", + "version": "12.2.2", + "tags": [ + "cp312-cp312-macosx_10_13_universal2" + ] + }, + { + "name": "httpcore", + "version": "1.0.9", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "cryptography", + "version": "50.0.2", + "tags": [ + "cp311-abi3-macosx_11_0_arm64" + ] + }, + { + "name": "pyobjc-framework-Cocoa", + "version": "12.2.2", + "tags": [ + "cp312-cp312-macosx_10_13_universal2" + ] + }, + { + "name": "wcwidth", + "version": "0.9.2", + "tags": [ + "cp310-abi3-macosx_11_0_arm64" + ] + }, + { + "name": "googleapis-common-protos", + "version": "1.75.5", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-genai", + "version": "1.65.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "tqdm", + "version": "4.70.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "urllib3", + "version": "2.8.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-dotenv", + "version": "1.2.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "h11", + "version": "0.16.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pydantic", + "version": "2.13.5", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "protobuf", + "version": "6.33.6", + "tags": [ + "cp39-abi3-macosx_10_9_universal2" + ] + }, + { + "name": "pydantic-settings", + "version": "2.15.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "sniffio", + "version": "1.3.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "rsa", + "version": "4.9.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyasn1", + "version": "0.6.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "screeninfo", + "version": "0.8.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "mdurl", + "version": "0.1.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-multipart", + "version": "0.0.32", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-framework-ApplicationServices", + "version": "12.2.2", + "tags": [ + "cp312-cp312-macosx_10_13_universal2" + ] + }, + { + "name": "fetch-use", + "version": "0.4.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "openai", + "version": "2.26.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-core", + "version": "12.2.2", + "tags": [ + "cp312-cp312-macosx_10_13_universal2" + ] + }, + { + "name": "click", + "version": "8.3.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "markdownify", + "version": "1.2.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "frozenlist", + "version": "1.8.0", + "tags": [ + "cp312-cp312-macosx_11_0_arm64" + ] + }, + { + "name": "cffi", + "version": "2.1.1", + "tags": [ + "cp312-cp312-macosx_11_0_arm64" + ] + }, + { + "name": "uuid7", + "version": "0.1.0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "typing_extensions", + "version": "4.15.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "aiosignal", + "version": "1.4.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "certifi", + "version": "2026.7.22", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "browser-use", + "version": "0.13.10", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "annotated-types", + "version": "0.8.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "referencing", + "version": "0.37.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-auth-httplib2", + "version": "0.4.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pycparser", + "version": "3.11", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "markdown-it-py", + "version": "4.2.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-auth", + "version": "2.48.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "bubus", + "version": "1.5.6", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "typing-inspection", + "version": "0.4.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "starlette", + "version": "1.7.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "distro", + "version": "1.9.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pfzy", + "version": "0.3.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "rpds-py", + "version": "2026.9.1", + "tags": [ + "cp312-cp312-macosx_11_0_arm64" + ] + }, + { + "name": "httplib2", + "version": "0.32.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-docx", + "version": "1.2.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "attrs", + "version": "26.1.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "ollama", + "version": "0.6.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "websockets", + "version": "15.0.1", + "tags": [ + "cp312-cp312-macosx_11_0_arm64" + ] + }, + { + "name": "pypdf", + "version": "6.16.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "httpx", + "version": "0.28.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "httpx2", + "version": "2.13.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "sse-starlette", + "version": "3.5.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "backoff", + "version": "2.2.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyasn1_modules", + "version": "0.4.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-api-core", + "version": "2.29.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "beautifulsoup4", + "version": "4.15.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "psutil", + "version": "7.2.2", + "tags": [ + "cp36-abi3-macosx_11_0_arm64" + ] + }, + { + "name": "aiohttp", + "version": "3.14.3", + "tags": [ + "cp312-cp312-macosx_11_0_arm64" + ] + }, + { + "name": "cdp-use", + "version": "1.4.5", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "truststore", + "version": "0.10.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "charset-normalizer", + "version": "3.5.2", + "tags": [ + "cp312-cp312-macosx_10_13_universal2" + ] + }, + { + "name": "pillow", + "version": "12.3.0", + "tags": [ + "cp312-cp312-macosx_11_0_arm64" + ] + }, + { + "name": "Cython", + "version": "3.3.0", + "tags": [ + "cp312-cp312-macosx_11_0_arm64" + ] + }, + { + "name": "proto-plus", + "version": "1.29.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "httpcore2", + "version": "2.13.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyparsing", + "version": "3.3.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "cua-driver", + "version": "0.28.2", + "tags": [ + "py3-none-macosx_13_0_universal2" + ] + }, + { + "name": "six", + "version": "1.17.0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "requests-oauthlib", + "version": "2.0.0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "multidict", + "version": "6.9.1", + "tags": [ + "cp312-cp312-macosx_11_0_arm64" + ] + }, + { + "name": "anthropic", + "version": "0.76.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "mcp-types", + "version": "2.1.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "idna", + "version": "3.20", + "tags": [ + "py3-none-any" + ] + } + ], + "cache_bytes_after_install": 391616193, + "private_state": { + "private-home": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/private-home", + "private-config": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/private-config", + "private-cache": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/private-cache", + "private-temp": "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/installed-agent-release-20261012-v2/footwork/output/install-v1/private-temp" + } +} diff --git a/jevdual/results/installed-agent-release-20261012-v2/missing-operation-rejection.json b/jevdual/results/installed-agent-release-20261012-v2/missing-operation-rejection.json new file mode 100644 index 0000000..0bff81f --- /dev/null +++ b/jevdual/results/installed-agent-release-20261012-v2/missing-operation-rejection.json @@ -0,0 +1,36 @@ +{ + "returncode": 1, + "report": { + "schema_version": "1", + "scope": "all_admitted_task_operations", + "validation": { + "accepted": false, + "reasons": [ + "operation_set_mismatch", + "disposition_mismatch" + ] + }, + "operation_completion": "completed", + "operations": [ + { + "operation": "op_741d45670d09", + "state": "final", + "disposition": "applied", + "updated": 1791835734.906624, + "created": 1791835732.11695 + } + ], + "persistence": "not_checked", + "current_state": "not_checked", + "task": "task_1ea760ed83ec4c7a88cdee3e2a62da40", + "snapshot_digest": "8afdbaa2a4f2cb3703dc1c4503c0dd5e38dcc200ce3b4b269d6c70802b54a6da", + "ledger_span": { + "first_created": 1791835732.11695, + "last_updated": 1791835734.906624, + "count": 1 + }, + "accepted_host_claim": null, + "as_of": 1791836148.593774, + "trusted_summary": "Host completion report rejected (operation_set_mismatch, disposition_mismatch). 1 recorded operations confirmed applied; 0 attempts confirmed no input. These are historical effects, not certification of the host job. Full task ledger: 1 records, 2026-10-12T20:08:52.116950+00:00 through 2026-10-12T20:08:54.906624+00:00. Current document state and disk persistence are not certified by this report." + } +} diff --git a/jevdual/results/installed-agent-release-20261012-v2/report.md b/jevdual/results/installed-agent-release-20261012-v2/report.md new file mode 100644 index 0000000..cd8b717 --- /dev/null +++ b/jevdual/results/installed-agent-release-20261012-v2/report.md @@ -0,0 +1,11 @@ +The fresh installed package completed one browser-to-TextEdit task through Claude Code, and automatic cleanup passed. Claude independently reviewed the raw evidence and approved the result with no blockers (message 1791836103010895000). + +The product came from packet 205577ee, frozen source 445a348 and wheel 9bb0440d. The separate driving harness was committed as d52d32d. Before the measured task, pristine verification checked all 14 packet members. Seven installation stages used an empty cache and private state to resolve 115 binary packages on macOS 26.5.1 arm64 with Python 3.12.6. Strict doctor passed from this responsible host with its existing Accessibility and Screen Recording grants. + +Claude Code 2.1.296 reported model claude-opus-5-5 and offered exactly five Footwork MCP tools plus its StructuredOutput finalizer. It used the generated installed server command, made one act call, and returned a structured completion claim with persistence not_checked. An independent AX readback matched Invoice report followed by 17.25 USD. One durable row, op_741d45670d09, remained final/applied/sent. After the host exited, the emitted installed report adapter accepted its claims; only the adapter's trusted summary was published. A separate readonly check rejected a report that omitted the admitted operation, with exit 1 and operation_set_mismatch/disposition_mismatch. + +Actual server and worker contexts came from the installed environment with private HOME, user site disabled and browser telemetry/cloud sync disabled. The registered worker and audited server were absent afterward. The reported model cost was $0.0990072, within the configured $1 soft cap and $2 scheduling reserve. The raw evidence manifest binds receipts, transcript, validated report, journal snapshot and cleanup observations. + +One SIGINT automatically closed the exact document, browser and listener with fixture exit 0. Independent fresh AXWindows enumeration succeeded and found no matching document. Exact owned fixture/browser processes were absent and the listener refused connections. File snapshots changed from 15 to 24 bytes; they do not certify saved-file persistence. No unrelated app/window close, permission change, native retry or extra model attempt occurred. + +This is one clean task on an already-granted host with existing authentication. It does not qualify real-host interruption/restart behavior in this lane, other hosts/models/OS versions, MDM, pixel capture, arbitrary apps, a cold OS or disk persistence. Original A1 failures remain unchanged. Operator preparation errors are retained separately: the v1 verifier option typo before any fixture/agent, and a v2 relative configuration path refusal before the successful absolute-path configuration. Only v2 contains the one measured host task. diff --git a/jevdual/results/installed-agent-release-20261012-v2/resource-absence.json b/jevdual/results/installed-agent-release-20261012-v2/resource-absence.json new file mode 100644 index 0000000..bc17be1 --- /dev/null +++ b/jevdual/results/installed-agent-release-20261012-v2/resource-absence.json @@ -0,0 +1,14 @@ +{ + "fixture": { + "pid": 89273, + "absent": true + }, + "browser": { + "pid": 89354, + "absent": true + }, + "listener": { + "port": 63108, + "connect_ex": 61 + } +} diff --git a/jevdual/results/installed-agent-release-20261012-v2/review.json b/jevdual/results/installed-agent-release-20261012-v2/review.json new file mode 100644 index 0000000..04bc38e --- /dev/null +++ b/jevdual/results/installed-agent-release-20261012-v2/review.json @@ -0,0 +1,13 @@ +{ + "reviewer": "Claude via authorized mailbox", + "message": "1791836103010895000", + "verdict": "approved, no blockers", + "artifact": "packet205577ee / product445a348 / wheel9bb0440d", + "harness": "d52d32d", + "limits": [ + "one clean real-host task on already-granted macOS26.5.1", + "no real-host interruption/restart claim in this lane", + "no persistence/pixel/other-host qualification" + ], + "source_notes_addressed": true +} diff --git a/jevdual/results/installed-agent-release-20261012-v2/summary.json b/jevdual/results/installed-agent-release-20261012-v2/summary.json new file mode 100644 index 0000000..fc0c257 --- /dev/null +++ b/jevdual/results/installed-agent-release-20261012-v2/summary.json @@ -0,0 +1,67 @@ +{ + "passed": true, + "runtime_gates_passed": true, + "independent_review": "approved", + "classification": "one bounded clean task through installed real agent host on existing host Mac", + "source_commit": "445a348ff31243c2f3d90baae9ce209d89bd7eac", + "harness_commit": "d52d32d0dd6f6fd0b308ea6b9c6a271597c2fc39", + "packet_sha256": "205577ee0848e9e68c8a15a9f25e1d308e820a054be449f631274ba7a9c7151d", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7", + "host": "Claude Code", + "host_version": "2.1.296", + "model": "claude-opus-5-5", + "os": "macOS26.5.1/25F80", + "architecture": "arm64", + "python": "3.12.6", + "installation_stages": 7, + "packages": 115, + "installed_server_worker_origins": true, + "tools_offered": [ + "StructuredOutput", + "mcp__footwork__act", + "mcp__footwork__bind", + "mcp__footwork__observe", + "mcp__footwork__report", + "mcp__footwork__status" + ], + "tool_calls": [ + "mcp__footwork__bind", + "mcp__footwork__bind", + "mcp__footwork__status", + "mcp__footwork__act", + "mcp__footwork__observe", + "mcp__footwork__report", + "StructuredOutput" + ], + "cost_usd": 0.09900719999999999, + "effect_retry": false, + "act_calls": 1, + "operation": "op_741d45670d09", + "journal_rows": 1, + "oracle": "Invoice report\n17.25 USD", + "adapter_accepted": true, + "automatic_cleanup": true, + "fixture_exit": 0, + "worker_absent": true, + "server_absent": true, + "independent_document_absent": true, + "independent_browser_listener_absent": true, + "existing_permissions": { + "accessibility": "granted", + "screen_recording": "granted" + }, + "disk_persistence": "not_checked", + "qualified_scope": "one Claude Code clean task on macOS26.5.1 arm64/Python3.12.6 with existing authenticated host and OS permissions", + "limits": [ + "not cold OS or permission grant journey", + "not other agent hosts/models/OS or MDM", + "not pixel capture or arbitrary apps", + "disk persistence not checked", + "original A1 failures and all U2/U3 records unchanged" + ], + "evidence_manifest_sha256": "24bd27629eda5ba6a0d20e1fab35b445f0be1b3cbe253b5cc5a60e851554776c", + "review_message": "1791836103010895000", + "adapter_missing_operation_refused": true, + "report_only_publication": true, + "journal_snapshot_sha256": "54948fafa129614b69ba57c7a8bb86ae1ea9c8a121507fd94f1694a56d5d6707" +} diff --git a/jevdual/results/installed-agent-release-20261012-v2/validated-report.json b/jevdual/results/installed-agent-release-20261012-v2/validated-report.json new file mode 100644 index 0000000..f57cdd5 --- /dev/null +++ b/jevdual/results/installed-agent-release-20261012-v2/validated-report.json @@ -0,0 +1,30 @@ +{ + "schema_version": "1", + "scope": "all_admitted_task_operations", + "validation": { + "accepted": true, + "reasons": [] + }, + "operation_completion": "completed", + "operations": [ + { + "operation": "op_741d45670d09", + "state": "final", + "disposition": "applied", + "updated": 1791835734.906624, + "created": 1791835732.11695 + } + ], + "persistence": "not_checked", + "current_state": "not_checked", + "task": "task_1ea760ed83ec4c7a88cdee3e2a62da40", + "snapshot_digest": "8afdbaa2a4f2cb3703dc1c4503c0dd5e38dcc200ce3b4b269d6c70802b54a6da", + "ledger_span": { + "first_created": 1791835732.11695, + "last_updated": 1791835734.906624, + "count": 1 + }, + "accepted_host_claim": "completed", + "as_of": 1791835751.512595, + "trusted_summary": "1 recorded operations confirmed applied; 0 attempts confirmed no input. These are historical effects, not certification of the host job. Full task ledger: 1 records, 2026-10-12T20:08:52.116950+00:00 through 2026-10-12T20:08:54.906624+00:00. Current document state and disk persistence are not certified by this report." +} diff --git a/jevdual/results/native-camera-20261010/conformance-v2.json b/jevdual/results/native-camera-20261010/conformance-v2.json new file mode 100644 index 0000000..396e7c0 --- /dev/null +++ b/jevdual/results/native-camera-20261010/conformance-v2.json @@ -0,0 +1,15 @@ +{ + "source_commit": "48b33a1", + "source_sha256": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/camera-capture-experiment/permission.swift": "71887f8fed0c51f4c539b2cce98c7c826a4b7c641c6ddbdc72f560f636161f56", + "tools/camera-capture-experiment/test_capture.py": "47b27ab7400fc264a88d1c3bd4f1794dbed8dfb802d85d42e5f96a9e4877eb1f", + "tools/camera-capture-experiment/README.md": "b1639705df033ddbeca92cf96ffac77e80f7a447a8f6369771e982abb481d2e7" + }, + "tests": 18, + "unit_command": "python3 -m unittest discover -s tools/camera-capture-experiment -p 'test_*.py' -v", + "unit_exit_code": 0, + "ruff_command": "uv run --no-sync ruff check ../tools/camera-capture-experiment", + "ruff_exit_code": 0, + "runtime_boundary": "synthetic camera/process fixtures; real ffprobe on generated image. Native camera attempts are separately reported." +} diff --git a/jevdual/results/native-camera-20261010/conformance.json b/jevdual/results/native-camera-20261010/conformance.json new file mode 100644 index 0000000..c7ecd25 --- /dev/null +++ b/jevdual/results/native-camera-20261010/conformance.json @@ -0,0 +1,26 @@ +{ + "source_sha256": { + "tools/camera-capture-experiment/capture.py": "5eb774018193a206e2fab836c801b5c10ec35d4d07170cb69967161d668c1c7b", + "tools/camera-capture-experiment/permission.swift": "71887f8fed0c51f4c539b2cce98c7c826a4b7c641c6ddbdc72f560f636161f56", + "tools/camera-capture-experiment/test_capture.py": "debef8d9115a87e2d0f93ac33c6c6b9f70f09adddb3e520cedfa3cc112171231", + "tools/camera-capture-experiment/README.md": "34108f3c8c5e378eaf314c6d9aec70e25bca5508098ef9d19e78c52b2ec95b5f" + }, + "preregistration_commit": "fb090bd", + "conformance": { + "command": "python3 -m unittest discover -s tools/camera-capture-experiment -p 'test_*.py' -v", + "exit_code": 0, + "tests": 18, + "runtime_boundary": "synthetic camera/process fixtures; real ffprobe on generated image, no live camera" + }, + "lint": { + "command": "uv run --no-sync ruff check ../tools/camera-capture-experiment", + "cwd": "jevdual", + "exit_code": 0 + }, + "notebook_check": { + "command": "uv run --no-sync python -m evals.notebook docs/notebook --check", + "cwd": "jevdual", + "exit_code": 0 + }, + "live_acquisition": "pending" +} diff --git a/jevdual/results/native-camera-20261010/live-v1.json b/jevdual/results/native-camera-20261010/live-v1.json new file mode 100644 index 0000000..c4946b1 --- /dev/null +++ b/jevdual/results/native-camera-20261010/live-v1.json @@ -0,0 +1,11 @@ +{ + "source_commit": "2bccd45", + "run": "tools/camera-capture-experiment/output/live-v1", + "token": "native-frame-001", + "state": "unknown", + "png_count": 0, + "diagnosis": "FFmpeg default 29.970030 fps rejected; device reports 15.000000 to 30.000000 fps modes", + "repeat_same_request": "retained unknown record, no new intent or image", + "ledger_sha256": "2fa6e53a27e1ba460546c2f59f9f83543090edad2591efb4583950aa861de916", + "permission": "not qualified by frame acquisition" +} diff --git a/jevdual/results/native-camera-20261010/live-v2.json b/jevdual/results/native-camera-20261010/live-v2.json new file mode 100644 index 0000000..78be557 --- /dev/null +++ b/jevdual/results/native-camera-20261010/live-v2.json @@ -0,0 +1,28 @@ +{ + "source_commit": "48b33a1", + "run": "tools/camera-capture-experiment/output/live-v2", + "token": "native-frame-002", + "state": "unknown", + "request": { + "allow_permission_prompt": true, + "camera": "MacBook Pro Camera", + "format": "png", + "framerate_fps": 30, + "timeout_seconds": 30.0 + }, + "png_count": 0, + "live_exit_code": 2, + "diagnosis": "30 second capture timeout; no PNG. Prior unsupported framerate message absent. Default yuv420p unsupported and supported pixel formats listed; FFmpeg documents automatic fallback, so format message alone does not establish cause. Camera authorization and first-frame delivery remain unverified.", + "supported_pixel_formats": [ + "uyvy422", + "yuyv422", + "nv12", + "0rgb", + "bgr0" + ], + "source_reference": "https://ffmpeg.org/ffmpeg-devices.html#avfoundation", + "status_after_restart": "unknown with no claims", + "cleanup": "no FFmpeg process observed after capture termination", + "ledger_sha256": "d5fad1d7096e345c36e5dbe2a9bfd483e3d931533267b59fbbd51d83859e6cc2", + "swift_status_after_trial": "not_determined; distinct privacy identity from FFmpeg" +} diff --git a/jevdual/results/native-camera-20261010/live-v3.json b/jevdual/results/native-camera-20261010/live-v3.json new file mode 100644 index 0000000..da6f203 --- /dev/null +++ b/jevdual/results/native-camera-20261010/live-v3.json @@ -0,0 +1,48 @@ +{ + "source_commit": "48b33a1", + "preregistration_commit": "c8f71e6", + "run": "tools/camera-capture-experiment/output/live-v3", + "token": "native-frame-003", + "state": "acquired", + "request": { + "allow_permission_prompt": true, + "camera": "MacBook Pro Camera", + "format": "png", + "framerate_fps": 30, + "timeout_seconds": 60.0 + }, + "artifact": { + "bytes": 2300920, + "codec": "png", + "height": 1920, + "sha256": "402a74b6443c6b5f3e9b04dc7f99bd24265e2e177ee6eddc8f85233a53c73d04", + "width": 1080 + }, + "private_artifact_mode": "0o600", + "live_exit_code": 0, + "ffprobe": { + "programs": [], + "stream_groups": [], + "streams": [ + { + "codec_name": "png", + "width": 1080, + "height": 1920, + "nb_read_frames": "1" + } + ] + }, + "ffmpeg_independent_decode_exit_code": 0, + "sha256_matches_receipt": true, + "fresh_process_repeat_capture": "same intent, receipt, artifact; unchanged ledger", + "fresh_process_status": "acquired; retained artifact verified", + "png_count": 1, + "ffmpeg_processes_after_checks": [], + "duration_intent_to_receipt_seconds": 2.582584857940674, + "limits": [ + "No authenticated hardware identity or exposure timestamp", + "No scene interpretation or desk workflow qualification", + "Observed success does not establish why earlier attempts timed out", + "Private image and diagnostics remain ignored" + ] +} diff --git a/jevdual/results/native-camera-20261010/preflight.json b/jevdual/results/native-camera-20261010/preflight.json new file mode 100644 index 0000000..c18a876 --- /dev/null +++ b/jevdual/results/native-camera-20261010/preflight.json @@ -0,0 +1,16 @@ +{ + "observed_at": "2026-10-10T14:22:07.709439-04:00", + "system_camera_names": [ + "MacBook Pro Camera", + "OBS Virtual Camera" + ], + "ffmpeg_exact_native_name_matches": [ + "MacBook Pro Camera" + ], + "ffmpeg_avfoundation_available": true, + "swift_video_authorization_raw": 0, + "swift_video_authorization": "not_determined", + "authorization_identity_limit": "Swift status is not a qualified FFmpeg permission result", + "camera_opened": false, + "frame_acquisition": "unverified" +} diff --git a/jevdual/results/native-camera-20261010/report.md b/jevdual/results/native-camera-20261010/report.md new file mode 100644 index 0000000..6f568b7 --- /dev/null +++ b/jevdual/results/native-camera-20261010/report.md @@ -0,0 +1,38 @@ +# Native MacBook camera experiment + +**Frame acquisition is verified on this Mac.** Attended live-v3 acquired one private 1080 × 1920 PNG on frozen source `48b33a1`. Independently run ffprobe and FFmpeg decoded it, and its SHA-256 and byte count match the persisted receipt. Fresh-process capture repeat and status retain the same original intent, receipt and artifact; the ledger bytes are unchanged. Exactly one PNG remains, and no FFmpeg process remained after verification. + +## Source and evidence + +- Initial preregistration: `fb090bd`; attended-trial preregistration: `c8f71e6`. +- Initial source: `2bccd45`; fixed-30-fps source used for the successful trial: `48b33a1`. +- Implementation: `tools/camera-capture-experiment/`. +- Conformance/source hashes: `conformance.json`, `conformance-v2.json`. +- Live results: `live-v1.json`, `live-v2.json`, `live-v3.json`. +- Private image/diagnostics: ignored `tools/camera-capture-experiment/output/live-v3/`; image has mode 0600. Image contents are not included in this report or Git. + +All 18 stdlib conformance tests and Ruff pass on `48b33a1`. Those tests use synthetic camera/process boundaries plus a real ffprobe check on a generated fixture. The separate live-v3 result supplies native-frame evidence. + +## Qualification + +| Criterion | Evidence | Result | +| --- | --- | --- | +| Select observed physical camera and exclude OBS/screen | Public doctor selects only MacBook Pro Camera | Discovery verified; names are not authenticated hardware identity | +| Acquire a saved native frame | Live-v3 receipt and independent decode/hash readback | Verified PNG, 1080 × 1920, 2,300,920 bytes | +| Preserve repeat operations across process restart | Public capture repeat and status preserve ledger and saved bytes | Verified retained lookup; no exactly-once physical-effect claim | +| Disable audio and reject substituted sources | Explicit camera:none and -an; source-selection conformance cases | Passed configured route and fixtures | +| Bound owned subprocess lifetime | Live termination/readback plus descendant timeout fixture | No FFmpeg process remained | +| Preserve uncertainty and reject corrupt/missing evidence | Earlier live unknown records and conformance fixtures | Passed; original failed trials preserved | + +Live-v1 rejected FFmpeg's default 29.970030 fps; the fixed source explicitly requests the observed supported 30 fps. Live-v2 timed out without saving an image. Its pixel-format message does not prove the cause because [FFmpeg documents fallback to the first supported format](https://ffmpeg.org/ffmpeg-devices.html#avfoundation). The user then requested a new attempt after being away. Live-v3 succeeded in about 2.58 seconds from persisted intent to receipt with a 60-second bound. This success does not establish prompt history or why live-v2 timed out. + +## Readback and limits + +```sh +python3 tools/camera-capture-experiment/capture.py status --run-dir tools/camera-capture-experiment/output/live-v3 --token native-frame-003 +python3 -m unittest discover -s tools/camera-capture-experiment -p 'test_*.py' -v +``` + +The successful capture command used exact camera MacBook Pro Camera, token native-frame-003, run live-v3, --allow-permission-prompt and --timeout 60. Repeating those exact arguments retrieves the retained result. Earlier unresolved records are never replayed automatically. + +This qualifies frame acquisition and retained artifact integrity here. It does not qualify exposure freshness, semantic interpretation, useful desk workflows, robot control, power-loss recovery or authenticated hardware identity. Retain the isolated tool until a separately scoped useful workflow is observed. Core Footwork authority, TextEdit recovery and model routing are unchanged. No provider calls or downloads were needed. diff --git a/jevdual/results/network-body-adapter-20261011/attempt-ledger.json b/jevdual/results/network-body-adapter-20261011/attempt-ledger.json new file mode 100644 index 0000000..6eca62b --- /dev/null +++ b/jevdual/results/network-body-adapter-20261011/attempt-ledger.json @@ -0,0 +1,44 @@ +{ + "actual_model_attempts": [ + { + "attempt": 1, + "completed_cycles": 2, + "faults": [ + { + "call": "calls/job-1-call-001", + "command_id": "command-9b6a7f12d66e4839a33440f6a4dd657e", + "mode": "drop_after", + "read_only": true, + "reconciled_state": "completed" + }, + { + "call": "calls/job-1-call-002", + "mode": "duplicate" + }, + { + "call": "calls/job-1-call-003", + "mode": "delay_after" + } + ], + "jobs": [ + { + "calls": 14, + "completed": true, + "job_id": "network-ai-1" + }, + { + "calls": 13, + "completed": true, + "job_id": "network-ai-2" + } + ], + "outcome": "passed", + "planner": "actual_codex_cli_images_over_TCP", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-network-ai-v1", + "service_exit_observed": true, + "source_commit": "e8c7e9b502f852639a89932aa30884a40a432ca9", + "version": 1 + } + ], + "development_attempts": "retained-development-attempts.json" +} diff --git a/jevdual/results/network-body-adapter-20261011/conformance-summary.json b/jevdual/results/network-body-adapter-20261011/conformance-summary.json new file mode 100644 index 0000000..62edad2 --- /dev/null +++ b/jevdual/results/network-body-adapter-20261011/conformance-summary.json @@ -0,0 +1,37 @@ +{ + "AI_evidence": false, + "checks": 65, + "core_checks": 29, + "network_checks": 36, + "passed": true, + "suites": [ + { + "checks": 13, + "log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-terminal-pointer-after-fix.log", + "log_sha256": "bf9c2b85544b0d531bec433d33b02dead4441da7cfea3fd08b786d2a85c06cc3", + "passed": true, + "suite": "server" + }, + { + "checks": 11, + "log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-tests-v4/unittest.log", + "log_sha256": "cb6fe9cb99ae76fe875e0b8568dc47c6f5b9fb4bed22b51e4899085174fd7413", + "passed": true, + "suite": "client" + }, + { + "checks": 12, + "log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9-matrix-v3/unittest.log", + "log_sha256": "9bfd71157bfe218e82a275ecebaf46f30bc5b2037ae5c9de7085d973addafbd1", + "passed": true, + "suite": "fault-matrix" + }, + { + "checks": 29, + "log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-core-regression.log", + "log_sha256": "9ff1fd78da76bc2ff9ce1e146ced78641d99714536e3ee2ad0932e7922629ecb", + "passed": true, + "suite": "unchanged-core" + } + ] +} diff --git a/jevdual/results/network-body-adapter-20261011/fault-matrix.json b/jevdual/results/network-body-adapter-20261011/fault-matrix.json new file mode 100644 index 0000000..6ca4333 --- /dev/null +++ b/jevdual/results/network-body-adapter-20261011/fault-matrix.json @@ -0,0 +1,143 @@ +{ + "AI_evidence": false, + "all_owned_processes_reaped": true, + "all_sources_unchanged": true, + "cases": [ + { + "conformance": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-8085197a1c/conformance.json", + "conformance_sha256": "e1983f08fc608f0266d106c8e9395dddec5c1012f99c0906c33331078a50cf5b", + "passed": true, + "processes_reaped": true, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-8085197a1c", + "source_unchanged": true, + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_01_successive_network_cycles_with_RGB_only_policy" + }, + { + "conformance": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-74599fd771/conformance.json", + "conformance_sha256": "dc76b28c1227183b220c41a1abddf85764ffed8fd5d842a847a9e1c1fc029477", + "passed": true, + "processes_reaped": true, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-74599fd771", + "source_unchanged": true, + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_02_bounded_delays_preserve_exact_effect" + }, + { + "conformance": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-4d57771bfc/conformance.json", + "conformance_sha256": "5e3c487379bbb4fbf9639a3ea6d94c077862fea9ecfbccb2175892ab32d4438e", + "passed": true, + "processes_reaped": true, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-4d57771bfc", + "source_unchanged": true, + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_03_drop_before_acceptance_stays_pending_until_fenced" + }, + { + "conformance": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-359640b707/conformance.json", + "conformance_sha256": "8c01fb36b3a72b2b642c8a230aadce9d96709a06ca864bfce210752258777d35", + "passed": true, + "processes_reaped": true, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-359640b707", + "source_unchanged": true, + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_04_drop_after_effect_reconciles_without_resending" + }, + { + "conformance": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-eb23a92d4a/conformance.json", + "conformance_sha256": "4e79bf94fa256365818b4e481ab2716f593101ca6b953b40fa588789e087cf88", + "passed": true, + "processes_reaped": true, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-eb23a92d4a", + "source_unchanged": true, + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_05_exact_concurrent_duplicate_has_one_result_one_motion" + }, + { + "conformance": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-e195011a06/conformance.json", + "conformance_sha256": "d34ae8e672789de462d9200f6f6d5fbca0b99dd7f7980423aa4845deb40e5ed1", + "passed": true, + "processes_reaped": true, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-e195011a06", + "source_unchanged": true, + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_06_conflicting_duplicate_refuses_without_second_effect" + }, + { + "conformance": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-19ab17bcec/conformance.json", + "conformance_sha256": "a8be9d07527d0a4469927bd23a6c0736dd82c8348dd596746533461273f42ba8", + "passed": true, + "processes_reaped": true, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-19ab17bcec", + "source_unchanged": true, + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_07_stale_image_proposal_cannot_dispatch_effect" + }, + { + "conformance": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-2b1064309f/conformance.json", + "conformance_sha256": "0b162c735e17b80c608c05d05083403637e3bbf223ecb5aca2bfca4e50b36626", + "passed": true, + "processes_reaped": true, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-2b1064309f", + "source_unchanged": true, + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_08_delay_past_lease_expiry_never_admits_late_effect" + }, + { + "conformance": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-c0f0200063/conformance.json", + "conformance_sha256": "3e748e9144b41adead369e2b1c72caa53b1c502d009e89e9714e3d6277f6ce21", + "passed": true, + "processes_reaped": true, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-c0f0200063", + "source_unchanged": true, + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_09_identity_source_epoch_and_exclusive_lease_fence" + }, + { + "conformance": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-aa5b68fca2/conformance.json", + "conformance_sha256": "8541f98ffdc413848464fb444e1b70fbe0f68cc029022da48b618aadf36f473c", + "passed": true, + "processes_reaped": true, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-aa5b68fca2", + "source_unchanged": true, + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_10_process_restart_preserves_terminal_history_and_body" + }, + { + "conformance": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-177873e378/conformance.json", + "conformance_sha256": "22000f17e6100d905f2c967827ceda3cfd52415f8533af1ccaf430dc7d076bef", + "passed": true, + "processes_reaped": true, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-177873e378", + "source_unchanged": true, + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_11_restart_recovers_exact_lower_receipt_without_dispatch" + }, + { + "conformance": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-7d10544d21/conformance.json", + "conformance_sha256": "d633b367b02d67cee8f20f0e61478a1503eaf47e86676524ebbb9f98ce7befc9", + "passed": true, + "processes_reaped": true, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-7d10544d21", + "source_unchanged": true, + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_12_accepted_without_lower_proof_and_client_restart_stays_unknown" + } + ], + "elapsed_s": 53.269, + "log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9-matrix-v3/unittest.log", + "log_sha256": "9bfd71157bfe218e82a275ecebaf46f30bc5b2037ae5c9de7085d973addafbd1", + "passed": true, + "preregistration": "c976508", + "prior_logs": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9-matrix-v1/unittest.log", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9-matrix-v2/unittest.log" + ], + "source_sha256": { + "../camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "body_client.py": "40388c238192d923888c9852b363314801dcf65c7266aadc8c377d3810aeb33d", + "body_network.py": "95fd21597bcdd9e982a698859cd1219ae4522062e332c50fea233629dbfb343c", + "body_protocol.py": "1d24066768105c28b5cf6f4d46458e4e1b9d86cbde45ed6ce466b30da7ff974f", + "episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "network_fault_proxy.py": "44b0b52f7c213c0f5f93b8997ba72be4e750bc9f1a4f7f6d7b2a8c4f1233b595", + "order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + }, + "suite": "SIM9 mechanical public TCP boundary", + "test_count": 12, + "test_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/test_network_body_faults.py", + "test_sha256": "1e83420ed13825d84d03bd3e10da94d6e65f92ab022917629f4ba687ed30fd5e" +} diff --git a/jevdual/results/network-body-adapter-20261011/final-checks.json b/jevdual/results/network-body-adapter-20261011/final-checks.json new file mode 100644 index 0000000..5034085 --- /dev/null +++ b/jevdual/results/network-body-adapter-20261011/final-checks.json @@ -0,0 +1,24 @@ +{ + "actual_proxy_faults_bound": { + "delay_after": 1, + "drop_after": 1, + "duplicate": 1, + "duplicate_exact_payload_and_reply": true, + "lost_dispatch_single_POST_single_read_only_GET": true + }, + "all_owned_model_and_service_pids_absent": true, + "max_axis_actuator_force_n": 1.45171519700936, + "model_processes": 27, + "motion_receipts": 21, + "original_browser_product": { + "active_job_id": null, + "completed_jobs": 2, + "model_request_pending": false, + "runner_status": "running", + "url": "http://127.0.0.1:64370" + }, + "original_browser_server_preserved": true, + "passed": true, + "proxy_closed": true, + "source_unchanged": true +} diff --git a/jevdual/results/network-body-adapter-20261011/qualification-score.json b/jevdual/results/network-body-adapter-20261011/qualification-score.json new file mode 100644 index 0000000..316e1e3 --- /dev/null +++ b/jevdual/results/network-body-adapter-20261011/qualification-score.json @@ -0,0 +1,754 @@ +{ + "body_id": "ab384557b47e442891620c90252a52ca", + "calls": [ + { + "action": "move", + "call": 1, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": "command-9b6a7f12d66e4839a33440f6a4dd657e", + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 12940, + "output_tokens": 187 + } + }, + { + "action": "move", + "call": 2, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12599, + "output_tokens": 130 + } + }, + { + "action": "move", + "call": 3, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12669, + "output_tokens": 171 + } + }, + { + "action": "measure", + "call": 4, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12741, + "output_tokens": 119 + } + }, + { + "action": "begin_reset", + "call": 5, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12808, + "output_tokens": 38 + } + }, + { + "action": "move", + "call": 6, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12869, + "output_tokens": 284 + } + }, + { + "action": "move", + "call": 7, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12941, + "output_tokens": 138 + } + }, + { + "action": "move", + "call": 8, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12940, + "output_tokens": 215 + } + }, + { + "action": "move", + "call": 9, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12947, + "output_tokens": 186 + } + }, + { + "action": "move", + "call": 10, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12948, + "output_tokens": 154 + } + }, + { + "action": "move", + "call": 11, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12952, + "output_tokens": 110 + } + }, + { + "action": "move", + "call": 12, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12963, + "output_tokens": 151 + } + }, + { + "action": "move", + "call": 13, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 11976, + "output_tokens": 109 + } + }, + { + "action": "finish_reset", + "call": 14, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12966, + "output_tokens": 103 + } + }, + { + "action": "move", + "call": 1, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12524, + "output_tokens": 191 + } + }, + { + "action": "move", + "call": 2, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12595, + "output_tokens": 188 + } + }, + { + "action": "measure", + "call": 3, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13080, + "output_tokens": 132 + } + }, + { + "action": "begin_reset", + "call": 4, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12734, + "output_tokens": 39 + } + }, + { + "action": "move", + "call": 5, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12796, + "output_tokens": 180 + } + }, + { + "action": "move", + "call": 6, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12867, + "output_tokens": 168 + } + }, + { + "action": "move", + "call": 7, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 11954, + "output_tokens": 239 + } + }, + { + "action": "move", + "call": 8, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12950, + "output_tokens": 158 + } + }, + { + "action": "move", + "call": 9, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12954, + "output_tokens": 212 + } + }, + { + "action": "move", + "call": 10, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13358, + "output_tokens": 123 + } + }, + { + "action": "move", + "call": 11, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13368, + "output_tokens": 133 + } + }, + { + "action": "move", + "call": 12, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12965, + "output_tokens": 114 + } + }, + { + "action": "finish_reset", + "call": 13, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "network-ai-2", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "outcome": "executed", + "reconciled_command_id": null, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12963, + "output_tokens": 110 + } + } + ], + "completed_cycles": 2, + "faults": [ + { + "call": "calls/job-1-call-001", + "command_id": "command-9b6a7f12d66e4839a33440f6a4dd657e", + "mode": "drop_after", + "read_only": true, + "reconciled_state": "completed" + }, + { + "call": "calls/job-1-call-002", + "mode": "duplicate" + }, + { + "call": "calls/job-1-call-003", + "mode": "delay_after" + } + ], + "lost_reply_reconciled": true, + "model_boundaries_passed": true, + "passed": true, + "scope": "loopback TCP simulation, actual Codex image policy, independent post-trial native/SQLite checks", + "scores": [ + { + "job_id": "network-ai-1", + "measurement_receipt": { + "bench_id": "ab384557b47e442891620c90252a52ca", + "generation": 3, + "image_sha256": "abd56127579f6a8c8dd685d44e7d633a4b7f230837d5892d1b0d10b71bbddd4b", + "job_id": "network-ai-1", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 6.401999999999517, + "operation_id": "agent-cbce78c4e9b94db6939d8cc4ab710ac3", + "source_kind": "mujoco_simulation", + "state_sha256": "f6e8a7529bd2326775b9043e7a716b126906ab7003fafbc6f229c159b904c7d0", + "version": 1, + "worker_pid": 9467 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06580390404818154, + -1.5285398651025924e-10, + 0.034995730455369904 + ], + "external_force_cleared": true, + "generation": 3, + "native_time_s": 6.401999999999517, + "pusher_home": false, + "pusher_xyz": [ + -0.029999970057503877, + 1.0537612379601558e-25, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "f6e8a7529bd2326775b9043e7a716b126906ab7003fafbc6f229c159b904c7d0" + }, + "model_calls": 14, + "motion_count": 11, + "passed": true, + "reset_receipt": { + "bench_id": "ab384557b47e442891620c90252a52ca", + "block_home_verified": true, + "generation": 11, + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "network-ai-1", + "kind": "finish_reset", + "native_time_s": 22.94599999999816, + "operation_id": "agent-6a5cfaff4f71405b97a18c03642c8b46", + "pusher_home_verified": true, + "reset_contact_samples": 3132, + "reset_native_steps": 8272, + "state_sha256": "c6f5f6647e2a7540d10ee3258a909c0479b719c7814f657a56bfeccea18b9208" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.1157663460691671, + -2.426921464787385e-09, + 0.03499573045536983 + ], + "external_force_cleared": true, + "generation": 11, + "native_time_s": 22.94599999999816, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "c6f5f6647e2a7540d10ee3258a909c0479b719c7814f657a56bfeccea18b9208" + } + }, + { + "job_id": "network-ai-2", + "measurement_receipt": { + "bench_id": "ab384557b47e442891620c90252a52ca", + "generation": 13, + "image_sha256": "7a7e8a845f816ca42d8c548c21596a282e33b047f4f70ae81f9889f8f3582824", + "job_id": "network-ai-2", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 27.481999999995647, + "operation_id": "agent-28a9ec23a725478c919618fc57d36227", + "source_kind": "mujoco_simulation", + "state_sha256": "f6b8e40878229f2a91c4e1702896ed01284d848ea4d1f18e79b51619e1842371", + "version": 1, + "worker_pid": 16109 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.07578006126019292, + -2.4269150856619973e-09, + 0.034995730455369765 + ], + "external_force_cleared": true, + "generation": 13, + "native_time_s": 27.481999999995647, + "pusher_home": false, + "pusher_xyz": [ + -0.029999969671697213, + 3.3521388079464474e-27, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "f6b8e40878229f2a91c4e1702896ed01284d848ea4d1f18e79b51619e1842371" + }, + "model_calls": 13, + "motion_count": 10, + "passed": true, + "reset_receipt": { + "bench_id": "ab384557b47e442891620c90252a52ca", + "block_home_verified": true, + "generation": 21, + "image_sha256": "e2cd823b33e1b80d69fd7f951fa4fab98c72d0231718bb4e9c4f0b7f0a0ebee1", + "job_id": "network-ai-2", + "kind": "finish_reset", + "native_time_s": 43.760000000007516, + "operation_id": "agent-7aa94511d53a4afb9085e18eebc053d9", + "pusher_home_verified": true, + "reset_contact_samples": 3188, + "reset_native_steps": 8139, + "state_sha256": "0b3b7cf03214abcc701e0f72bc11ed5d5b862c11dd31297673bce7bd54938fb0" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11577407045506503, + -4.568246329025581e-09, + 0.03499573045536982 + ], + "external_force_cleared": true, + "generation": 21, + "native_time_s": 43.760000000007516, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "0b3b7cf03214abcc701e0f72bc11ed5d5b862c11dd31297673bce7bd54938fb0" + } + } + ], + "source_commit": "e8c7e9b502f852639a89932aa30884a40a432ca9", + "source_unchanged": true, + "sqlite": { + "count": 2, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "ab384557b47e442891620c90252a52ca", + "generation": 3, + "image_sha256": "abd56127579f6a8c8dd685d44e7d633a4b7f230837d5892d1b0d10b71bbddd4b", + "job_id": "network-ai-1", + "operation_id": "agent-cbce78c4e9b94db6939d8cc4ab710ac3", + "receipt": { + "bench_id": "ab384557b47e442891620c90252a52ca", + "generation": 3, + "image_sha256": "abd56127579f6a8c8dd685d44e7d633a4b7f230837d5892d1b0d10b71bbddd4b", + "job_id": "network-ai-1", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 6.401999999999517, + "operation_id": "agent-cbce78c4e9b94db6939d8cc4ab710ac3", + "source_kind": "mujoco_simulation", + "state_sha256": "f6e8a7529bd2326775b9043e7a716b126906ab7003fafbc6f229c159b904c7d0", + "version": 1, + "worker_pid": 9467 + }, + "state_sha256": "f6e8a7529bd2326775b9043e7a716b126906ab7003fafbc6f229c159b904c7d0" + }, + { + "bench_id": "ab384557b47e442891620c90252a52ca", + "generation": 13, + "image_sha256": "7a7e8a845f816ca42d8c548c21596a282e33b047f4f70ae81f9889f8f3582824", + "job_id": "network-ai-2", + "operation_id": "agent-28a9ec23a725478c919618fc57d36227", + "receipt": { + "bench_id": "ab384557b47e442891620c90252a52ca", + "generation": 13, + "image_sha256": "7a7e8a845f816ca42d8c548c21596a282e33b047f4f70ae81f9889f8f3582824", + "job_id": "network-ai-2", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 27.481999999995647, + "operation_id": "agent-28a9ec23a725478c919618fc57d36227", + "source_kind": "mujoco_simulation", + "state_sha256": "f6b8e40878229f2a91c4e1702896ed01284d848ea4d1f18e79b51619e1842371", + "version": 1, + "worker_pid": 16109 + }, + "state_sha256": "f6b8e40878229f2a91c4e1702896ed01284d848ea4d1f18e79b51619e1842371" + } + ] + }, + "transport_command_count": 83, + "transport_journal_bound": true +} diff --git a/jevdual/results/network-body-adapter-20261011/report.md b/jevdual/results/network-body-adapter-20261011/report.md new file mode 100644 index 0000000..8600242 --- /dev/null +++ b/jevdual/results/network-body-adapter-20261011/report.md @@ -0,0 +1,30 @@ +# Persistent network body: qualified in simulation + +The adapter passed its twelve-case TCP fault matrix and the first frozen actual-model attempt: two successive camera-guided jobs on one retained MuJoCo body. Each job placed the block, saved a separately computed measurement in SQLite and reset through physical contact. + +Measured source: `e8c7e9b502f852639a89932aa30884a40a432ca9`. Preregistration: `c976508`. The exact measured files remain unchanged. Existing native guards and the local browser product were not edited. + +| Evidence | Result | +| --- | --- | +| Public TCP fault matrix | 12/12 cases passed on final source | +| Focused server/client checks | 13 + 11 passed | +| Existing bench/agent guard checks | 29 passed | +| Actual Codex image decisions | 27; all dispatched outcomes executed | +| Native movements | 21; maximum recorded axis actuator force 1.452 N, below the existing 4 N limit | +| Independently scored cycles | 2/2; measurement placement, SQLite receipt identity and contact resets passed | +| Transport commands | 83; bound to immutable requests and lower receipts | +| Process cleanup | All 27 model processes and the trial service exited; proxy closed | + +Body `ab384557b47e442891620c90252a52ca` retained generation 0 → 11 → 21, with completed native times 22.946 → 43.760 seconds. Separate native-state reconstruction verified each measured placement and home reset; read-only SQLite scoring verified exactly one measurement per job. Model input contained the exact SHA-verified PNG fetched through TCP, task geometry and bounded history. No native pose or detector locations were supplied to the model. + +The actual-model trial discarded the first movement's reply after the body had acted. The client retained uncertainty and issued one GET for that exact command's receipt, with one original POST and no resubmission. Another movement was forwarded twice concurrently with identical payloads and identical historical replies. A third reply was delayed by 0.2 seconds. All three fault events bind to retained command evidence. + +The mechanical matrix separately covered delays before and after forwarding, dropped requests, dropped replies, concurrent duplicates, conflicting duplicates, stale image proposals, lease expiry during delay, wrong identities/source/epochs, exclusive leases, process restart, crash after lower receipt before transport receipt, and accepted intent without lower proof. Proven receipt recovery reads lower records without dispatch. Missing proof remains unknown and blocks new commands, including after a separate client process restarts. + +The first server suite failed seven cases because an extra envelope field violated its closed schema. Two targeted regressions also reproduced a premature cache update and an undersized local journal reader. Repairs preserve the failures; the complete matrix was rerun on final source. The 256-record capacity fixture tests local journal reload and refusal before send 257; it is not evidence of 256 native effects. All development attempts remain in the ledger and private run directories. + +This qualifies same-host loopback TCP simulation and these two model jobs. Authentication, operation across machines, real hardware, power-loss recovery and broad model repeatability remain unqualified. Leases fence new admissions; an accepted bounded movement can finish after a disconnect. The browser product at port 64370 remains local, open and idle with its two earlier completed jobs. The new adapter is available through its service/client API and qualification CLI. + +Evidence: [independent score](qualification-score.json), [fault matrix](fault-matrix.json), [conformance](conformance-summary.json), [cleanup and actual fault binding](final-checks.json), [source freeze](source-freeze.json), [actual-model attempts](attempt-ledger.json), [development attempts](retained-development-attempts.json). Private raw trial: `tools/physical-workcell/output/sim9-network-ai-v1/`. + +Usage and verification: [network body runbook](../../../tools/physical-workcell/NETWORK_BODY.md). The service owns the persistent body; the client owns image transport and command uncertainty; the existing Bench and AgentBench own local effect guards. No new dependency or language runtime was added. diff --git a/jevdual/results/network-body-adapter-20261011/retained-development-attempts.json b/jevdual/results/network-body-adapter-20261011/retained-development-attempts.json new file mode 100644 index 0000000..7bce3d3 --- /dev/null +++ b/jevdual/results/network-body-adapter-20261011/retained-development-attempts.json @@ -0,0 +1,42 @@ +{ + "attempts": [ + { + "evidence": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-body-server-tests-first.log", + "failed_checks": 7, + "kind": "server-conformance", + "outcome": "failed", + "passed_checks": 5, + "reason": "stray response field violated closed retained schema" + }, + { + "authority_remained_fenced": true, + "evidence": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-terminal-pointer-before-fix.log", + "kind": "terminal-pointer-regression", + "outcome": "expected_failure" + }, + { + "evidence": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-capacity-before/unittest.log", + "kind": "client-capacity-regression", + "outcome": "expected_failure", + "reason": "256-entry local journal exceeded 64 KiB reader" + }, + { + "checks": 12, + "evidence": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9-matrix-v1/unittest.log", + "kind": "matrix-v1", + "outcome": "passed" + }, + { + "checks": 12, + "evidence": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9-matrix-v2/unittest.log", + "kind": "matrix-v2", + "outcome": "passed" + }, + { + "checks": 12, + "evidence": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9-matrix-v3/aggregate.json", + "kind": "matrix-v3", + "outcome": "passed" + } + ] +} diff --git a/jevdual/results/network-body-adapter-20261011/source-freeze.json b/jevdual/results/network-body-adapter-20261011/source-freeze.json new file mode 100644 index 0000000..bf6cccd --- /dev/null +++ b/jevdual/results/network-body-adapter-20261011/source-freeze.json @@ -0,0 +1,24 @@ +{ + "commit": "e8c7e9b502f852639a89932aa30884a40a432ca9", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/body_client.py": "40388c238192d923888c9852b363314801dcf65c7266aadc8c377d3810aeb33d", + "tools/physical-workcell/body_network.py": "95fd21597bcdd9e982a698859cd1219ae4522062e332c50fea233629dbfb343c", + "tools/physical-workcell/body_protocol.py": "1d24066768105c28b5cf6f4d46458e4e1b9d86cbde45ed6ce466b30da7ff974f", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "40fe73e01d97ee1e5cfdb813650b11edadbea29534784ab10cc1ef4e7f4d5418", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/network_fault_proxy.py": "44b0b52f7c213c0f5f93b8997ba72be4e750bc9f1a4f7f6d7b2a8c4f1233b595", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/qualify_network_body.py": "2800cf38afd21b86f8b035c46e994b6f96d966db4ccba1bba703e9e1a22f7c2b", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_network_body.py": "d1f7150a5ff23775664b5656fd21379502ba71175487fd7aadb2b51bffcdaf8c", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/network-workbench-20261011/attempt-ledger.json b/jevdual/results/network-workbench-20261011/attempt-ledger.json new file mode 100644 index 0000000..9423b82 --- /dev/null +++ b/jevdual/results/network-workbench-20261011/attempt-ledger.json @@ -0,0 +1,18 @@ +{ + "actual_model_attempts": [ + { + "attempt": 1, + "completed_cycles": 2, + "faults": "fault-observations.json", + "model_calls": 26, + "outcome": "passed", + "owned_trial_processes_reaped": true, + "planner": "actual_codex_cli_images_over_TCP", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim10-browser-ai-v1", + "source_commit": "d731ac8ea03f326edef18ea9c1b9cb2027421871", + "transport_commands": 80 + } + ], + "development_attempts": "retained-development-attempts.json", + "handoff": "handoff.json" +} diff --git a/jevdual/results/network-workbench-20261011/conformance-summary.json b/jevdual/results/network-workbench-20261011/conformance-summary.json new file mode 100644 index 0000000..578a2b9 --- /dev/null +++ b/jevdual/results/network-workbench-20261011/conformance-summary.json @@ -0,0 +1,22 @@ +{ + "additional_checks": [ + "Ruff new Python", + "JavaScript syntax", + "notebook schema", + "Codex CLI authenticated preflight" + ], + "failed_final": 0, + "passed": 94, + "suites": [ + { + "cases": 14, + "log": "tools/physical-workcell/output/sim10-network-runtime-final.log", + "scope": "real-socket queue, unknown stages, restart, health and control races" + }, + { + "cases": 80, + "log": "tools/physical-workcell/output/sim10-core-link-http-regression-v2.log", + "scope": "unchanged core, adapter, network fault matrix, retained link and browser HTTP boundaries" + } + ] +} diff --git a/jevdual/results/network-workbench-20261011/fault-observations.json b/jevdual/results/network-workbench-20261011/fault-observations.json new file mode 100644 index 0000000..6fa586d --- /dev/null +++ b/jevdual/results/network-workbench-20261011/fault-observations.json @@ -0,0 +1,36 @@ +{ + "actual_browser_faults": [ + { + "client_POSTs": 1, + "client_send_count": 1, + "command_id": "command-85f915f500a54459b605d7bb546a8cba", + "delay_s": null, + "forwarded_count": 1, + "mode": "dropped_after", + "retained_command_state": "completed", + "same_reply": null + }, + { + "client_POSTs": 1, + "client_send_count": 1, + "command_id": "command-0b59b5fe6d674a12b1ba2d56b7c83833", + "delay_s": null, + "forwarded_count": 2, + "mode": "duplicate_replies", + "retained_command_state": "completed", + "same_reply": true + }, + { + "client_POSTs": 1, + "client_send_count": 1, + "command_id": "command-9e3ed3f1ec7c45acb5ed2ad4e17b640f", + "delay_s": 0.8, + "forwarded_count": 1, + "mode": "delay_after", + "retained_command_state": "completed", + "same_reply": null + } + ], + "native_receipt_binding": "qualification-score.json", + "recovery_ledger": "ui-ledger.json" +} diff --git a/jevdual/results/network-workbench-20261011/handoff.json b/jevdual/results/network-workbench-20261011/handoff.json new file mode 100644 index 0000000..5b06a76 --- /dev/null +++ b/jevdual/results/network-workbench-20261011/handoff.json @@ -0,0 +1,37 @@ +{ + "processes": [ + { + "role": "body", + "pid": 65719, + "arguments": [ + "body_network.py", + "--root", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim10-browser-ai-v1/body", + "--diagnostic", + "--port", + "51373" + ] + }, + { + "role": "workbench", + "pid": 65723, + "arguments": [ + "network_bench_operator.py", + "serve", + "--root", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim10-browser-ai-v1/operator", + "--port", + "51377", + "--backend", + "codex" + ] + } + ], + "browser_origin": "http://127.0.0.1:51377", + "body_id": "a649def6a17f4401b475acd602c151f5", + "app_id": "7560f614128c40c7b9d82a7a20f35435", + "runner_status": "paused", + "completed_jobs": 2, + "client_commands": 80, + "model_request_pending": false +} \ No newline at end of file diff --git a/jevdual/results/network-workbench-20261011/process-ledger.json b/jevdual/results/network-workbench-20261011/process-ledger.json new file mode 100644 index 0000000..15c4df7 --- /dev/null +++ b/jevdual/results/network-workbench-20261011/process-ledger.json @@ -0,0 +1,25 @@ +{ + "app_id": "7560f614128c40c7b9d82a7a20f35435", + "body_id": "a649def6a17f4401b475acd602c151f5", + "body_origin": "http://127.0.0.1:51373", + "browser_origin": "http://127.0.0.1:51377", + "owned_processes_exited": true, + "planner": "actual_codex_cli_images_over_TCP", + "processes": [ + { + "exit_observed": true, + "pid": 51543, + "returncode": 0, + "role": "body" + }, + { + "exit_observed": true, + "pid": 51546, + "returncode": 0, + "role": "workbench" + } + ], + "ready": true, + "simulation": true, + "source_commit": "d731ac8ea03f326edef18ea9c1b9cb2027421871" +} diff --git a/jevdual/results/network-workbench-20261011/qualification-score.json b/jevdual/results/network-workbench-20261011/qualification-score.json new file mode 100644 index 0000000..aa83971 --- /dev/null +++ b/jevdual/results/network-workbench-20261011/qualification-score.json @@ -0,0 +1,707 @@ +{ + "body_id": "a649def6a17f4401b475acd602c151f5", + "browser_transitions_passed": true, + "calls": [ + { + "action": "move", + "call": 1, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12606, + "output_tokens": 197 + }, + "withheld": false + }, + { + "action": "move", + "call": 2, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12682, + "output_tokens": 155 + }, + "withheld": false + }, + { + "action": "measure", + "call": 3, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10752, + "input_tokens": 13168, + "output_tokens": 123 + }, + "withheld": false + }, + { + "action": "begin_reset", + "call": 4, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12823, + "output_tokens": 38 + }, + "withheld": false + }, + { + "action": "move", + "call": 5, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12884, + "output_tokens": 221 + }, + "withheld": false + }, + { + "action": "move", + "call": 6, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12959, + "output_tokens": 166 + }, + "withheld": false + }, + { + "action": "move", + "call": 7, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13034, + "output_tokens": 167 + }, + "withheld": false + }, + { + "action": "move", + "call": 8, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13035, + "output_tokens": 178 + }, + "withheld": false + }, + { + "action": "move", + "call": 9, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13039, + "output_tokens": 172 + }, + "withheld": false + }, + { + "action": "move", + "call": 10, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13042, + "output_tokens": 179 + }, + "withheld": false + }, + { + "action": "move", + "call": 11, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13053, + "output_tokens": 178 + }, + "withheld": false + }, + { + "action": "move", + "call": 12, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13467, + "output_tokens": 107 + }, + "withheld": false + }, + { + "action": "finish_reset", + "call": 13, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13049, + "output_tokens": 97 + }, + "withheld": false + }, + { + "action": "move", + "call": 1, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12610, + "output_tokens": 170 + }, + "withheld": false + }, + { + "action": "move", + "call": 2, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10752, + "input_tokens": 13097, + "output_tokens": 177 + }, + "withheld": false + }, + { + "action": "measure", + "call": 3, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12750, + "output_tokens": 111 + }, + "withheld": false + }, + { + "action": "begin_reset", + "call": 4, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12817, + "output_tokens": 42 + }, + "withheld": false + }, + { + "action": "move", + "call": 5, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12882, + "output_tokens": 198 + }, + "withheld": false + }, + { + "action": "move", + "call": 6, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12957, + "output_tokens": 265 + }, + "withheld": false + }, + { + "action": "move", + "call": 7, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13028, + "output_tokens": 146 + }, + "withheld": false + }, + { + "action": "move", + "call": 8, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13032, + "output_tokens": 161 + }, + "withheld": false + }, + { + "action": "move", + "call": 9, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13036, + "output_tokens": 181 + }, + "withheld": false + }, + { + "action": "move", + "call": 10, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10752, + "input_tokens": 13459, + "output_tokens": 154 + }, + "withheld": false + }, + { + "action": "move", + "call": 11, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13053, + "output_tokens": 158 + }, + "withheld": false + }, + { + "action": "move", + "call": 12, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13055, + "output_tokens": 117 + }, + "withheld": false + }, + { + "action": "finish_reset", + "call": 13, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13060, + "output_tokens": 98 + }, + "withheld": false + } + ], + "completed_cycles": 2, + "lost_reply_reconciled_without_resend": true, + "model_boundaries_passed": true, + "owned_processes_exited": true, + "passed": true, + "scope": "browser UI and actual image policy over loopback TCP simulation", + "scores": [ + { + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "measurement_receipt": { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.533999999999613, + "operation_id": "agent-995e1490e4144fee886050b9ac44e6b5", + "source_kind": "mujoco_simulation", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3", + "version": 1, + "worker_pid": 53676 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06582868246696554, + -8.393868978677625e-11, + 0.034995730455369786 + ], + "external_force_cleared": true, + "generation": 2, + "native_time_s": 5.533999999999613, + "pusher_home": false, + "pusher_xyz": [ + -0.049999969408768885, + 8.057882667829012e-26, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3" + }, + "model_calls": 13, + "motion_count": 10, + "passed": true, + "reset_receipt": { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "kind": "finish_reset", + "native_time_s": 21.81399999999879, + "operation_id": "agent-2d5b35e1c4ad4dc8a7756bb73b70b0fa", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8140, + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11582763630912812, + -2.3633644648884637e-09, + 0.03499573045536985 + ], + "external_force_cleared": true, + "generation": 10, + "native_time_s": 21.81399999999879, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + } + }, + { + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "measurement_receipt": { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "generation": 12, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 26.349999999996275, + "operation_id": "agent-a6f487c1961a478384394c551e62dfa6", + "source_kind": "mujoco_simulation", + "state_sha256": "b404922ffb5f889a67d58db1cd318adf36480402268a5324245f7b5aaf8dc4fa", + "version": 1, + "worker_pid": 60485 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.07578824921321287, + -2.363334835175414e-09, + 0.034995730455369856 + ], + "external_force_cleared": true, + "generation": 12, + "native_time_s": 26.349999999996275, + "pusher_home": false, + "pusher_xyz": [ + -0.029999969671445637, + -3.033819548242173e-26, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "b404922ffb5f889a67d58db1cd318adf36480402268a5324245f7b5aaf8dc4fa" + }, + "model_calls": 13, + "motion_count": 10, + "passed": true, + "reset_receipt": { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "block_home_verified": true, + "generation": 20, + "image_sha256": "2d2c4fcec313d713e1865aba74ba4fb11a792380d7ce2b6b694b4ff968aac95b", + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "kind": "finish_reset", + "native_time_s": 42.56200000000605, + "operation_id": "agent-9bc164e1269d4eb3861b243de5629fff", + "pusher_home_verified": true, + "reset_contact_samples": 3254, + "reset_native_steps": 8106, + "state_sha256": "a50d85406807ec8e44cc67fbd34d34802fc7d6dda176fdceda92c4cae5af162d" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11584066223827265, + -6.772213335361033e-09, + 0.034995730455369876 + ], + "external_force_cleared": true, + "generation": 20, + "native_time_s": 42.56200000000605, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "a50d85406807ec8e44cc67fbd34d34802fc7d6dda176fdceda92c4cae5af162d" + } + } + ], + "source_commit": "d731ac8ea03f326edef18ea9c1b9cb2027421871", + "source_unchanged": true, + "sqlite": { + "count": 2, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "generation": 12, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "operation_id": "agent-a6f487c1961a478384394c551e62dfa6", + "receipt": { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "generation": 12, + "image_sha256": "abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35", + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 26.349999999996275, + "operation_id": "agent-a6f487c1961a478384394c551e62dfa6", + "source_kind": "mujoco_simulation", + "state_sha256": "b404922ffb5f889a67d58db1cd318adf36480402268a5324245f7b5aaf8dc4fa", + "version": 1, + "worker_pid": 60485 + }, + "state_sha256": "b404922ffb5f889a67d58db1cd318adf36480402268a5324245f7b5aaf8dc4fa" + }, + { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "operation_id": "agent-995e1490e4144fee886050b9ac44e6b5", + "receipt": { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.533999999999613, + "operation_id": "agent-995e1490e4144fee886050b9ac44e6b5", + "source_kind": "mujoco_simulation", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3", + "version": 1, + "worker_pid": 53676 + }, + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3" + } + ] + }, + "transport_command_count": 80, + "transport_journal_bound": true, + "ui_steps": [ + "completed", + "delay_armed", + "disconnected", + "offline_receipt", + "reconnected", + "recovered", + "reloaded", + "resumed", + "uncertain" + ] +} diff --git a/jevdual/results/network-workbench-20261011/report.md b/jevdual/results/network-workbench-20261011/report.md new file mode 100644 index 0000000..dffff9f --- /dev/null +++ b/jevdual/results/network-workbench-20261011/report.md @@ -0,0 +1,22 @@ +# Browser workbench on a persistent body + +The first frozen browser/model trial passed. Two successive actual Codex jobs used camera images downloaded over TCP, recorded one measurement each and reset by contact on the same simulated body. A lost motion reply visibly blocked the queue. Disconnect and reconnect sent no commands; receipt lookup recovered the original result without resending its action and left the agent paused until explicit Resume. + +Measured source: `d731ac8ea03f326edef18ea9c1b9cb2027421871`, unchanged throughout qualification. The preregistered experiment is [SIM10](../../docs/experiments/SIM10-network-workbench.md); the full independent result is [qualification-score.json](qualification-score.json). + +| Job | Model calls | Measurement generation | Reset generation | Native time at reset | Reset contact samples | +| --- | ---: | ---: | ---: | ---: | ---: | +| Network inspection 01 | 13 | 2 | 10 | 21.814 s | 3,174 | +| Network inspection 02 | 13 | 12 | 20 | 42.562 s | 3,254 | + +The independent scorer ran after both owned trial processes exited. It checked native placement and home snapshots, continuous native time and generation, exact SQLite measurement receipts, image digests, model process exit, typed proposals and core receipts, single client sends and the browser transition ledger. All passed. The planner and browser host had no native state access. Reload retained both completed jobs and all 80 client command records without another command. + +The browser also exercised an exact duplicate and a 0.8-second delayed dispatch reply. The duplicate produced two upstream forwards, equal retained replies and one client send; the body/core receipt bound the result to the original command. Each diagnostic fault was consumed once. See [fault observations](fault-observations.json). The recovery controls blocked receipt lookup while disconnected. The visible Resume snapshot was captured immediately after the click; its public-state ledger was recorded later while the successive jobs were still running. + +Conformance passed 94 checks: fourteen real-socket runtime checks and eighty core, adapter, fault-matrix, link and HTTP checks. Ruff, JavaScript syntax, notebook schema and authenticated CLI preflight passed. Earlier failures and fixture corrections remain in [retained development attempts](retained-development-attempts.json); the one actual-model attempt is in [attempt-ledger.json](attempt-ledger.json). + +After scoring, the same roots and ports were restarted for handoff. Body and application identities, completed history and the 80-command journal persisted. The host is paused with no active job or pending model request. The browser is at `http://127.0.0.1:51377/`; the body is at `http://127.0.0.1:51373`. Handoff process ownership is recorded in [handoff.json](handoff.json). Use New job or Repeat job, then Resume. Connection diagnostics remain visible. + +This qualifies same-Mac loopback simulation. Physical hardware and authenticated control across machines remain untested. Pause blocks later admissions; an already admitted bounded action may finish. Queue, model and command budgets are finite. Run and restart instructions are in [NETWORK_WORKBENCH.md](../../../tools/physical-workcell/NETWORK_WORKBENCH.md). + +![Completed jobs and connection controls](ui/handoff.jpg) diff --git a/jevdual/results/network-workbench-20261011/retained-development-attempts.json b/jevdual/results/network-workbench-20261011/retained-development-attempts.json new file mode 100644 index 0000000..95e9a6e --- /dev/null +++ b/jevdual/results/network-workbench-20261011/retained-development-attempts.json @@ -0,0 +1,32 @@ +{ + "attempts": [ + { + "artifact": "tools/physical-workcell/output/sim10-link-tests-v1/unittest.log", + "outcome": "Eight of nine passed; wrong lexical command selector corrected, retained link behavior unchanged" + }, + { + "artifact": "tools/physical-workcell/output/sim10-idle-health-before-fix.log", + "outcome": "Original idle upstream outage not detected; bounded health added" + }, + { + "artifact": "tools/physical-workcell/output/sim10-resume-before-fix.log", + "outcome": "Original stale Resume overrode Pause; control epoch bound" + }, + { + "artifact": "tools/physical-workcell/output/sim10-stale-health-before-fix.log", + "outcome": "Original old failed health response paused a known completed native dispatch; transport epoch bound" + }, + { + "artifact": "tools/physical-workcell/output/sim10-network-runtime-tests-first.log", + "outcome": "Eleven of thirteen passed; local manifest reader and duplicate fixture initialization errors corrected" + }, + { + "artifact": "tools/physical-workcell/output/sim10-network-runtime-tests-second.log", + "outcome": "Thirteen passed before the final fourteen-case suite" + }, + { + "artifact": "tools/physical-workcell/output/sim10-core-link-http-regression.log", + "outcome": "Wrong relative interpreter path exited before tests; corrected launch retained separately" + } + ] +} diff --git a/jevdual/results/network-workbench-20261011/source-freeze.json b/jevdual/results/network-workbench-20261011/source-freeze.json new file mode 100644 index 0000000..8a622f7 --- /dev/null +++ b/jevdual/results/network-workbench-20261011/source-freeze.json @@ -0,0 +1,31 @@ +{ + "commit": "d731ac8ea03f326edef18ea9c1b9cb2027421871", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/body_client.py": "40388c238192d923888c9852b363314801dcf65c7266aadc8c377d3810aeb33d", + "tools/physical-workcell/body_network.py": "95fd21597bcdd9e982a698859cd1219ae4522062e332c50fea233629dbfb343c", + "tools/physical-workcell/body_protocol.py": "1d24066768105c28b5cf6f4d46458e4e1b9d86cbde45ed6ce466b30da7ff974f", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "40fe73e01d97ee1e5cfdb813650b11edadbea29534784ab10cc1ef4e7f4d5418", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/network_bench_operator.html": "2f9ffefc17fe25136cc3cd9582aef0add2d6f52361a2cc78d8261c231539eef5", + "tools/physical-workcell/network_bench_operator.py": "9079729ea4e8b56ccdf2a6d187b0c5d79a23a33688748fe55d92d10922500b40", + "tools/physical-workcell/network_fault_proxy.py": "44b0b52f7c213c0f5f93b8997ba72be4e750bc9f1a4f7f6d7b2a8c4f1233b595", + "tools/physical-workcell/network_operator_runtime.py": "65f7e939a0a09be788c3f618aa1570eb19740d04640caccbb80e1ae57860bf34", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/qualify_network_body.py": "2800cf38afd21b86f8b035c46e994b6f96d966db4ccba1bba703e9e1a22f7c2b", + "tools/physical-workcell/qualify_network_workbench.py": "e5c72acda2ea8fae3658faba907448765df2f1e34e05749fbe85faed28ef003b", + "tools/physical-workcell/record_network_workbench.py": "7604865caa309c7002c01a2384da84eb62d8b22dcd63ab256618c536bc5fedc4", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_network_body.py": "d1f7150a5ff23775664b5656fd21379502ba71175487fd7aadb2b51bffcdaf8c", + "tools/physical-workcell/score_network_workbench.py": "67a53586081fe425bd14b0a0fbbf3d7f0fc1e66daccda06765bb88f3d9d4d84c", + "tools/physical-workcell/workbench_link.py": "578d94684463bcb3d7f2d45d9ba68abcd27d8a27f7f39538323a269e251c330e", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/network-workbench-20261011/ui-ledger.json b/jevdual/results/network-workbench-20261011/ui-ledger.json new file mode 100644 index 0000000..e47f9da --- /dev/null +++ b/jevdual/results/network-workbench-20261011/ui-ledger.json @@ -0,0 +1,680 @@ +{ + "steps": [ + { + "browser_action": "Queued two jobs, armed Lose next reply, resumed, and observed Outcome uncertain in the browser", + "client_commands": 4, + "name": "uncertain", + "pending_command_ids": [ + "command-85f915f500a54459b605d7bb546a8cba" + ], + "screenshot": "ui/uncertain.jpg", + "state": { + "active_job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "app_id": "7560f614128c40c7b9d82a7a20f35435", + "connection": { + "adapter_id": "9e322142a34a41bf87ea1a23f6c4adb2", + "body_id": "a649def6a17f4401b475acd602c151f5", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "label": "Network inspection 01", + "last_intent": "Push the block right until its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "label": "Network inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-85f915f500a54459b605d7bb546a8cba", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791754738.6793149, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t74 checkbox Network inspection 01 Needs attention, Value: 0\n\t\t\t75 checkbox Network inspection 02 Queued, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t76 text Network inspection 02\n\t\t77 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t78 text reply is uncertain; reconcile exact command without resending\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t79 text Body connected\n\t\t\t80 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button Check receipt, ID: reconcile\n\t\t\t58 container\n\t\t\t\t59 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t60 text Test the simulated connection\n\t\t\t\t61 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t62 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t63 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t64 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t81 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t82 text Last observation \u00b7 Network inspection 01\n\t\t\t73 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t83 text Captured \u00b7 generation 0\n\t\t84 text OUTCOME UNCERTAIN\n\t\t85 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t86 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t87 text 14 events\n\t\t\t43 content list activity\n\t\t\t\t88 container\n\t\t\t\t\t89 text 05:38 PM Agent stopped for review reply is uncertain; reconcile exact command without resending\n\t\t\t\t90 container\n\t\t\t\t\t91 text 05:38 PM Sending a guarded action\n\t\t\t\t92 container\n\t\t\t\t\t93 text 05:38 PM Action proposal accepted Network inspection 01\n\t\t\t\t94 container\n\t\t\t\t\t95 text 05:38 PM Checking the planned action\n\t\t\t\t96 container\n\t\t\t\t\t97 text 05:38 PM AI planning requested Network inspection 01\n\t\t\t\t98 container\n\t\t\t\t\t99 text 05:38 PM Camera observation received Network inspection 01\n\t\t\t\t100 container\n\t\t\t\t\t101 text 05:38 PM Requesting camera observation\n\t\t\t\t102 container\n\t\t\t\t\t103 text 05:38 PM Inspection started Network inspection 01\n\t\t\t\t104 container\n\t\t\t\t\t105 text 05:38 PM Preparing inspection\n\t\t\t\t106 container\n\t\t\t\t\t107 text 05:38 PM Agent resumed\n\t\t\t\t108 container\n\t\t\t\t\t109 text 05:38 PM Reply fault armed The next command reply will be lost.\n\t\t\t\t110 container\n\t\t\t\t\t111 text 05:37 PM Job queued Network inspection 02\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t112 text 1 model requests \u00b7 Reported tokens: 12,610 input / 170 output\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Clicked Disconnect and observed Body disconnected with retained camera", + "client_commands": 4, + "name": "disconnected", + "pending_command_ids": [ + "command-85f915f500a54459b605d7bb546a8cba" + ], + "screenshot": "ui/disconnected.jpg", + "state": { + "active_job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "app_id": "7560f614128c40c7b9d82a7a20f35435", + "connection": { + "adapter_id": "9e322142a34a41bf87ea1a23f6c4adb2", + "body_id": "a649def6a17f4401b475acd602c151f5", + "epoch": 1, + "fault": null, + "reason": "Diagnostic link disconnected; cached observations are retained", + "state": "disconnected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "label": "Network inspection 01", + "last_intent": "Push the block right until its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "label": "Network inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-85f915f500a54459b605d7bb546a8cba", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791754751.266366, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t74 checkbox Network inspection 01 Needs attention, Value: 0\n\t\t\t75 checkbox Network inspection 02 Queued, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t76 text Network inspection 02\n\t\t77 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t78 text reply is uncertain; reconcile exact command without resending\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t79 text Body disconnected\n\t\t\t80 text Diagnostic link disconnected; cached observations are retained\n\t\t\t20 button Reconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t58 container\n\t\t\t\t59 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t60 text Test the simulated connection\n\t\t\t\t61 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t62 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t63 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t64 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t81 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t82 text Retained observation \u00b7 body offline\n\t\t\t73 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t83 text Retained \u00b7 offline \u00b7 generation 0\n\t\t84 text DISCONNECTED\n\t\t85 text The body connection is unavailable. The last camera observation is retained.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t86 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t87 text 15 events\n\t\t\t43 content list activity\n\t\t\t\t88 container\n\t\t\t\t\t89 text 05:39 PM Body disconnected Transport only; no body reset or replay\n\t\t\t\t90 container\n\t\t\t\t\t91 text 05:38 PM Agent stopped for review reply is uncertain; reconcile exact command without resending\n\t\t\t\t92 container\n\t\t\t\t\t93 text 05:38 PM Sending a guarded action\n\t\t\t\t94 container\n\t\t\t\t\t95 text 05:38 PM Action proposal accepted Network inspection 01\n\t\t\t\t96 container\n\t\t\t\t\t97 text 05:38 PM Checking the planned action\n\t\t\t\t98 container\n\t\t\t\t\t99 text 05:38 PM AI planning requested Network inspection 01\n\t\t\t\t100 container\n\t\t\t\t\t101 text 05:38 PM Camera observation received Network inspection 01\n\t\t\t\t102 container\n\t\t\t\t\t103 text 05:38 PM Requesting camera observation\n\t\t\t\t104 container\n\t\t\t\t\t105 text 05:38 PM Inspection started Network inspection 01\n\t\t\t\t106 container\n\t\t\t\t\t107 text 05:38 PM Preparing inspection\n\t\t\t\t108 container\n\t\t\t\t\t109 text 05:38 PM Agent resumed\n\t\t\t\t110 container\n\t\t\t\t\t111 text 05:38 PM Reply fault armed The next command reply will be lost.\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t112 text 1 model requests \u00b7 Reported tokens: 12,610 input / 170 output\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Observed and checked that Check receipt is disabled while offline; did not force the disabled control", + "client_commands": 4, + "name": "offline_receipt", + "pending_command_ids": [ + "command-85f915f500a54459b605d7bb546a8cba" + ], + "screenshot": null, + "state": { + "active_job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "app_id": "7560f614128c40c7b9d82a7a20f35435", + "connection": { + "adapter_id": "9e322142a34a41bf87ea1a23f6c4adb2", + "body_id": "a649def6a17f4401b475acd602c151f5", + "epoch": 1, + "fault": null, + "reason": "Diagnostic link disconnected; cached observations are retained", + "state": "disconnected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "label": "Network inspection 01", + "last_intent": "Push the block right until its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "label": "Network inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-85f915f500a54459b605d7bb546a8cba", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791754751.363982, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t74 checkbox Network inspection 01 Needs attention, Value: 0\n\t\t\t75 checkbox Network inspection 02 Queued, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t76 text Network inspection 02\n\t\t77 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t78 text reply is uncertain; reconcile exact command without resending\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t79 text Body disconnected\n\t\t\t80 text Diagnostic link disconnected; cached observations are retained\n\t\t\t20 button Reconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t58 container\n\t\t\t\t59 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t60 text Test the simulated connection\n\t\t\t\t61 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t62 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t63 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t64 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t81 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t82 text Retained observation \u00b7 body offline\n\t\t\t73 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t83 text Retained \u00b7 offline \u00b7 generation 0\n\t\t84 text DISCONNECTED\n\t\t85 text The body connection is unavailable. The last camera observation is retained.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t86 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t87 text 15 events\n\t\t\t43 content list activity\n\t\t\t\t88 container\n\t\t\t\t\t89 text 05:39 PM Body disconnected Transport only; no body reset or replay\n\t\t\t\t90 container\n\t\t\t\t\t91 text 05:38 PM Agent stopped for review reply is uncertain; reconcile exact command without resending\n\t\t\t\t92 container\n\t\t\t\t\t93 text 05:38 PM Sending a guarded action\n\t\t\t\t94 container\n\t\t\t\t\t95 text 05:38 PM Action proposal accepted Network inspection 01\n\t\t\t\t96 container\n\t\t\t\t\t97 text 05:38 PM Checking the planned action\n\t\t\t\t98 container\n\t\t\t\t\t99 text 05:38 PM AI planning requested Network inspection 01\n\t\t\t\t100 container\n\t\t\t\t\t101 text 05:38 PM Camera observation received Network inspection 01\n\t\t\t\t102 container\n\t\t\t\t\t103 text 05:38 PM Requesting camera observation\n\t\t\t\t104 container\n\t\t\t\t\t105 text 05:38 PM Inspection started Network inspection 01\n\t\t\t\t106 container\n\t\t\t\t\t107 text 05:38 PM Preparing inspection\n\t\t\t\t108 container\n\t\t\t\t\t109 text 05:38 PM Agent resumed\n\t\t\t\t110 container\n\t\t\t\t\t111 text 05:38 PM Reply fault armed The next command reply will be lost.\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t112 text 1 model requests \u00b7 Reported tokens: 12,610 input / 170 output\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Clicked Reconnect; observed Body connected with Outcome uncertain and effects still disabled", + "client_commands": 4, + "name": "reconnected", + "pending_command_ids": [ + "command-85f915f500a54459b605d7bb546a8cba" + ], + "screenshot": null, + "state": { + "active_job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "app_id": "7560f614128c40c7b9d82a7a20f35435", + "connection": { + "adapter_id": "9e322142a34a41bf87ea1a23f6c4adb2", + "body_id": "a649def6a17f4401b475acd602c151f5", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "label": "Network inspection 01", + "last_intent": "Push the block right until its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "label": "Network inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-85f915f500a54459b605d7bb546a8cba", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791754759.127721, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t74 checkbox Network inspection 01 Needs attention, Value: 0\n\t\t\t75 checkbox Network inspection 02 Queued, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t76 text Network inspection 02\n\t\t77 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t78 text reply is uncertain; reconcile exact command without resending\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t79 text Body connected\n\t\t\t80 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button Check receipt, ID: reconcile\n\t\t\t58 container\n\t\t\t\t59 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t60 text Test the simulated connection\n\t\t\t\t61 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t62 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t63 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t64 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t81 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t82 text Last observation \u00b7 Network inspection 01\n\t\t\t73 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t83 text Captured \u00b7 generation 0\n\t\t84 text OUTCOME UNCERTAIN\n\t\t85 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t86 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t87 text 16 events\n\t\t\t43 content list activity\n\t\t\t\t88 container\n\t\t\t\t\t89 text 05:39 PM Body reconnecting No command sent or replayed\n\t\t\t\t90 container\n\t\t\t\t\t91 text 05:39 PM Body disconnected Transport only; no body reset or replay\n\t\t\t\t92 container\n\t\t\t\t\t93 text 05:38 PM Agent stopped for review reply is uncertain; reconcile exact command without resending\n\t\t\t\t94 container\n\t\t\t\t\t95 text 05:38 PM Sending a guarded action\n\t\t\t\t96 container\n\t\t\t\t\t97 text 05:38 PM Action proposal accepted Network inspection 01\n\t\t\t\t98 container\n\t\t\t\t\t99 text 05:38 PM Checking the planned action\n\t\t\t\t100 container\n\t\t\t\t\t101 text 05:38 PM AI planning requested Network inspection 01\n\t\t\t\t102 container\n\t\t\t\t\t103 text 05:38 PM Camera observation received Network inspection 01\n\t\t\t\t104 container\n\t\t\t\t\t105 text 05:38 PM Requesting camera observation\n\t\t\t\t106 container\n\t\t\t\t\t107 text 05:38 PM Inspection started Network inspection 01\n\t\t\t\t108 container\n\t\t\t\t\t109 text 05:38 PM Preparing inspection\n\t\t\t\t110 container\n\t\t\t\t\t111 text 05:38 PM Agent resumed\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t112 text 1 model requests \u00b7 Reported tokens: 12,610 input / 170 output\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Clicked Check receipt; observed Receipt recovered, disabled receipt control, and explicit Resume", + "client_commands": 4, + "name": "recovered", + "pending_command_ids": [], + "screenshot": "ui/recovered.jpg", + "state": { + "active_job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "app_id": "7560f614128c40c7b9d82a7a20f35435", + "connection": { + "adapter_id": "9e322142a34a41bf87ea1a23f6c4adb2", + "body_id": "a649def6a17f4401b475acd602c151f5", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "label": "Network inspection 01", + "last_intent": "Push the block right until its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "label": "Network inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-85f915f500a54459b605d7bb546a8cba", + "operation": "dispatch", + "reason": "Exact historical stage recovered; explicit Resume observes fresh evidence", + "state": "recovered" + }, + "runner_status": "paused" + }, + "time": 1791754767.347348, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t74 checkbox Network inspection 01 Needs attention, Value: 0\n\t\t\t75 checkbox Network inspection 02 Queued, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t76 text Network inspection 02\n\t\t77 text Paused\n\t\t16 button Resume, ID: pause\n\t\t78 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue.\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t79 text Body connected\n\t\t\t80 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t58 container\n\t\t\t\t59 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t60 text Test the simulated connection\n\t\t\t\t61 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t62 button Lose next reply, ID: fault-drop\n\t\t\t\t63 button Delay next reply, ID: fault-delay\n\t\t\t\t64 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t81 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t82 text Last observation \u00b7 Network inspection 01\n\t\t\t73 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t83 text Captured \u00b7 generation 0\n\t\t84 text RECEIPT RECOVERED\n\t\t85 text The exact command receipt was recovered. The agent is paused until you resume.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t86 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t87 text 18 events\n\t\t\t43 content list activity\n\t\t\t\t88 container\n\t\t\t\t\t89 text 05:39 PM Command receipt recovered The agent remains paused until you resume.\n\t\t\t\t90 container\n\t\t\t\t\t91 text 05:39 PM Action receipt checked Network inspection 01\n\t\t\t\t92 container\n\t\t\t\t\t93 text 05:39 PM Body reconnecting No command sent or replayed\n\t\t\t\t94 container\n\t\t\t\t\t95 text 05:39 PM Body disconnected Transport only; no body reset or replay\n\t\t\t\t96 container\n\t\t\t\t\t97 text 05:38 PM Agent stopped for review reply is uncertain; reconcile exact command without resending\n\t\t\t\t98 container\n\t\t\t\t\t99 text 05:38 PM Sending a guarded action\n\t\t\t\t100 container\n\t\t\t\t\t101 text 05:38 PM Action proposal accepted Network inspection 01\n\t\t\t\t102 container\n\t\t\t\t\t103 text 05:38 PM Checking the planned action\n\t\t\t\t104 container\n\t\t\t\t\t105 text 05:38 PM AI planning requested Network inspection 01\n\t\t\t\t106 container\n\t\t\t\t\t107 text 05:38 PM Camera observation received Network inspection 01\n\t\t\t\t108 container\n\t\t\t\t\t109 text 05:38 PM Requesting camera observation\n\t\t\t\t110 container\n\t\t\t\t\t111 text 05:38 PM Inspection started Network inspection 01\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t112 text 1 model requests \u00b7 Reported tokens: 12,610 input / 170 output\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Armed one exact duplicate, clicked Resume, and observed Running; next decision takes a fresh TCP image", + "client_commands": 52, + "name": "resumed", + "pending_command_ids": [], + "screenshot": null, + "state": { + "active_job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "app_id": "7560f614128c40c7b9d82a7a20f35435", + "connection": { + "adapter_id": "9e322142a34a41bf87ea1a23f6c4adb2", + "body_id": "a649def6a17f4401b475acd602c151f5", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 12, + "image_url": "/api/frame?generation=12&sha256=abfdb72469ad6d232f3198aec14980db23bba6ddf167d8cebd3a06b297486b35" + }, + "jobs": [ + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "label": "Network inspection 01", + "last_intent": "Finish reset now that the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "kind": "finish_reset", + "native_time_s": 21.81399999999879, + "operation_id": "agent-2d5b35e1c4ad4dc8a7756bb73b70b0fa", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8140, + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 4, + "disturbance_test": false, + "error": null, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "label": "Network inspection 02", + "last_intent": "Begin the reset sequence after the verified measurement.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "measured", + "reset": null, + "status": "running", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791754896.69149, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t74 checkbox Network inspection 01 Needs attention, Value: 0\n\t\t\t75 checkbox Network inspection 02 Queued, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t76 text Network inspection 02\n\t\t77 text Running\n\t\t16 button Pause, ID: pause\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t78 text Body connected\n\t\t\t79 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t58 container\n\t\t\t\t59 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t60 text Test the simulated connection\n\t\t\t\t61 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t62 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t63 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t64 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t80 text The next command will be duplicated with the same identity.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t81 text Last observation \u00b7 Network inspection 01\n\t\t\t73 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t82 text Captured \u00b7 generation 0\n\t\t83 text QUEUED\n\t\t84 text This job is queued. The agent will start when the bench is ready.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t85 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t86 text 20 events\n\t\t\t43 content list activity\n\t\t\t\t87 container\n\t\t\t\t\t88 text 05:39 PM Agent resumed\n\t\t\t\t89 container\n\t\t\t\t\t90 text 05:39 PM Reply fault armed The next command will be duplicated with the same identity.\n\t\t\t\t91 container\n\t\t\t\t\t92 text 05:39 PM Command receipt recovered The agent remains paused until you resume.\n\t\t\t\t93 container\n\t\t\t\t\t94 text 05:39 PM Action receipt checked Network inspection 01\n\t\t\t\t95 container\n\t\t\t\t\t96 text 05:39 PM Body reconnecting No command sent or replayed\n\t\t\t\t97 container\n\t\t\t\t\t98 text 05:39 PM Body disconnected Transport only; no body reset or replay\n\t\t\t\t99 container\n\t\t\t\t\t100 text 05:38 PM Agent stopped for review reply is uncertain; reconcile exact command without resending\n\t\t\t\t101 container\n\t\t\t\t\t102 text 05:38 PM Sending a guarded action\n\t\t\t\t103 container\n\t\t\t\t\t104 text 05:38 PM Action proposal accepted Network inspection 01\n\t\t\t\t105 container\n\t\t\t\t\t106 text 05:38 PM Checking the planned action\n\t\t\t\t107 container\n\t\t\t\t\t108 text 05:38 PM AI planning requested Network inspection 01\n\t\t\t\t109 container\n\t\t\t\t\t110 text 05:38 PM Camera observation received Network inspection 01\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t111 text 1 model requests \u00b7 Reported tokens: 12,610 input / 170 output\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Clicked Delay next reply while the second job was running; the UI confirmed the one-shot diagnostic fault", + "client_commands": 66, + "name": "delay_armed", + "pending_command_ids": [], + "screenshot": null, + "state": { + "active_job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "app_id": "7560f614128c40c7b9d82a7a20f35435", + "connection": { + "adapter_id": "9e322142a34a41bf87ea1a23f6c4adb2", + "body_id": "a649def6a17f4401b475acd602c151f5", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 16, + "image_url": "/api/frame?generation=16&sha256=3fbc15f387e7d6bdfcc7acc65497d1904707776869995ca60f37dde177934c2d" + }, + "jobs": [ + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "label": "Network inspection 01", + "last_intent": "Finish reset now that the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "kind": "finish_reset", + "native_time_s": 21.81399999999879, + "operation_id": "agent-2d5b35e1c4ad4dc8a7756bb73b70b0fa", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8140, + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 9, + "disturbance_test": false, + "error": null, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "label": "Network inspection 02", + "last_intent": "Push the block leftward from its right side until its center reaches the home zone.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "resetting", + "reset": null, + "status": "running", + "target": "B" + } + ], + "model_request_pending": true, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791754929.899638, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t94 checkbox Network inspection 01 Completed, Value: 0\n\t\t\t95 checkbox Network inspection 02 Working, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t96 text Network inspection 02\n\t\t97 text Running\n\t\t16 button Pause, ID: pause\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t98 text Body connected\n\t\t\t99 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t58 container\n\t\t\t\t59 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t60 text Test the simulated connection\n\t\t\t\t61 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t62 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t63 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t64 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t100 text The next command reply will be delayed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t101 text Last observation \u00b7 Network inspection 02\n\t\t\t73 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t102 text Captured \u00b7 generation 12\n\t\t103 text AI IS WORKING\n\t\t104 text Move the pusher into the upper bypass lane before crossing to the block's right side.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t105 text Recorded\n\t\t\t86 container measurement-result\n\t\t\t\t106 text 1,729\n\t\t\t\t88 text Orange pixels\n\t\t\t\t89 container\n\t\t\t\t\t107 text 4,802.8\n\t\t\t\t\t91 text mm\u00b2\n\t\t\t\t92 text Apparent footprint\n\t\t\t\t93 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t108 text 24 events\n\t\t\t43 content list activity\n\t\t\t\t109 container\n\t\t\t\t\t110 text 05:41 PM Reply fault armed The next command reply will be delayed.\n\t\t\t\t111 container\n\t\t\t\t\t112 text 05:41 PM Sending a guarded action\n\t\t\t\t113 container\n\t\t\t\t\t114 text 05:41 PM Action proposal accepted Network inspection 02\n\t\t\t\t115 container\n\t\t\t\t\t116 text 05:41 PM Checking the planned action\n\t\t\t\t117 container\n\t\t\t\t\t118 text 05:41 PM AI planning requested Network inspection 02\n\t\t\t\t119 container\n\t\t\t\t\t120 text 05:41 PM Camera observation received Network inspection 02\n\t\t\t\t121 container\n\t\t\t\t\t122 text 05:41 PM Requesting camera observation\n\t\t\t\t123 container\n\t\t\t\t\t124 text 05:41 PM Action receipt checked Network inspection 02\n\t\t\t\t125 container\n\t\t\t\t\t126 text 05:41 PM Sending a guarded action\n\t\t\t\t127 container\n\t\t\t\t\t128 text 05:41 PM Action proposal accepted Network inspection 02\n\t\t\t\t129 container\n\t\t\t\t\t130 text 05:41 PM Checking the planned action\n\t\t\t\t131 container\n\t\t\t\t\t132 text 05:41 PM AI planning requested Network inspection 02\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t133 text 18 model requests \u00b7 Reported tokens: 232,999 input / 2,712 output\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Observed both successive AI jobs Completed with recorded measurements and verified contact resets", + "client_commands": 80, + "name": "completed", + "pending_command_ids": [], + "screenshot": "tools/physical-workcell/output/sim10-browser-ai-v1/ui/completed.jpg", + "state": { + "active_job_id": null, + "app_id": "7560f614128c40c7b9d82a7a20f35435", + "connection": { + "adapter_id": "9e322142a34a41bf87ea1a23f6c4adb2", + "body_id": "a649def6a17f4401b475acd602c151f5", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 20, + "image_url": "/api/frame?generation=20&sha256=2d2c4fcec313d713e1865aba74ba4fb11a792380d7ce2b6b694b4ff968aac95b" + }, + "jobs": [ + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "label": "Network inspection 01", + "last_intent": "Finish reset now that the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "kind": "finish_reset", + "native_time_s": 21.81399999999879, + "operation_id": "agent-2d5b35e1c4ad4dc8a7756bb73b70b0fa", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8140, + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "label": "Network inspection 02", + "last_intent": "Finish reset now that the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "block_home_verified": true, + "generation": 20, + "image_sha256": "2d2c4fcec313d713e1865aba74ba4fb11a792380d7ce2b6b694b4ff968aac95b", + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "kind": "finish_reset", + "native_time_s": 42.56200000000605, + "operation_id": "agent-9bc164e1269d4eb3861b243de5629fff", + "pusher_home_verified": true, + "reset_contact_samples": 3254, + "reset_native_steps": 8106, + "state_sha256": "a50d85406807ec8e44cc67fbd34d34802fc7d6dda176fdceda92c4cae5af162d" + }, + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791754971.431434, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t109 checkbox Network inspection 01 Completed, Value: 0\n\t\t\t110 checkbox Network inspection 02 Completed, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t111 text Network inspection 02\n\t\t112 text Running\n\t\t16 button Pause, ID: pause\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t113 text Body connected\n\t\t\t114 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t58 container\n\t\t\t\t59 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t60 text Test the simulated connection\n\t\t\t\t61 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t62 button Lose next reply, ID: fault-drop\n\t\t\t\t63 button Delay next reply, ID: fault-delay\n\t\t\t\t64 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t115 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t116 text Last camera observation\n\t\t\t73 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t117 text Captured \u00b7 generation 20\n\t\t118 text COMPLETED\n\t\t119 text Measurement recorded and reset verified. The bench is ready for another job.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t120 text Historical result\n\t\t\t86 container measurement-result\n\t\t\t\t121 text 1,729\n\t\t\t\t88 text Orange pixels\n\t\t\t\t89 container\n\t\t\t\t\t122 text 4,802.8\n\t\t\t\t\t91 text mm\u00b2\n\t\t\t\t92 text Apparent footprint\n\t\t\t\t93 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment.\n\t\t\t108 button Repeat job, ID: repeat\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t123 text 24 events\n\t\t\t43 content list activity\n\t\t\t\t124 container\n\t\t\t\t\t125 text 05:42 PM Action receipt checked Network inspection 02\n\t\t\t\t126 container\n\t\t\t\t\t127 text 05:42 PM Sending a guarded action\n\t\t\t\t128 container\n\t\t\t\t\t129 text 05:42 PM Action proposal accepted Network inspection 02\n\t\t\t\t130 container\n\t\t\t\t\t131 text 05:42 PM Checking the planned action\n\t\t\t\t132 container\n\t\t\t\t\t133 text 05:42 PM AI planning requested Network inspection 02\n\t\t\t\t134 container\n\t\t\t\t\t135 text 05:42 PM Camera observation received Network inspection 02\n\t\t\t\t136 container\n\t\t\t\t\t137 text 05:42 PM Requesting camera observation\n\t\t\t\t138 container\n\t\t\t\t\t139 text 05:42 PM Action receipt checked Network inspection 02\n\t\t\t\t140 container\n\t\t\t\t\t141 text 05:42 PM Sending a guarded action\n\t\t\t\t142 container\n\t\t\t\t\t143 text 05:42 PM Action proposal accepted Network inspection 02\n\t\t\t\t144 container\n\t\t\t\t\t145 text 05:42 PM Checking the planned action\n\t\t\t\t146 container\n\t\t\t\t\t147 text 05:42 PM AI planning requested Network inspection 02\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t148 text 26 model requests \u00b7 Reported tokens: 337,677 input / 3,956 output\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Reloaded the browser and observed both completed jobs, measurements, and the same body identity without command replay", + "client_commands": 80, + "name": "reloaded", + "pending_command_ids": [], + "screenshot": "tools/physical-workcell/output/sim10-browser-ai-v1/ui/reloaded.jpg", + "state": { + "active_job_id": null, + "app_id": "7560f614128c40c7b9d82a7a20f35435", + "connection": { + "adapter_id": "9e322142a34a41bf87ea1a23f6c4adb2", + "body_id": "a649def6a17f4401b475acd602c151f5", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 20, + "image_url": "/api/frame?generation=20&sha256=2d2c4fcec313d713e1865aba74ba4fb11a792380d7ce2b6b694b4ff968aac95b" + }, + "jobs": [ + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "label": "Network inspection 01", + "last_intent": "Finish reset now that the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "60cf7b32-5b88-40d7-8534-14d1e0497557", + "kind": "finish_reset", + "native_time_s": 21.81399999999879, + "operation_id": "agent-2d5b35e1c4ad4dc8a7756bb73b70b0fa", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8140, + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "label": "Network inspection 02", + "last_intent": "Finish reset now that the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "a649def6a17f4401b475acd602c151f5", + "block_home_verified": true, + "generation": 20, + "image_sha256": "2d2c4fcec313d713e1865aba74ba4fb11a792380d7ce2b6b694b4ff968aac95b", + "job_id": "134b0e3d-6428-4614-8c67-7a6912852790", + "kind": "finish_reset", + "native_time_s": 42.56200000000605, + "operation_id": "agent-9bc164e1269d4eb3861b243de5629fff", + "pusher_home_verified": true, + "reset_contact_samples": 3254, + "reset_native_steps": 8106, + "state_sha256": "a50d85406807ec8e44cc67fbd34d34802fc7d6dda176fdceda92c4cae5af162d" + }, + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791754981.420227, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t9 checkbox Network inspection 01 Completed, Value: 0\n\t\t\t10 checkbox Network inspection 02 Completed, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t14 text Network inspection 02\n\t\t15 text Running\n\t\t16 button Pause, ID: pause\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t18 text Body connected\n\t\t\t19 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t22 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t23 text Test the simulated connection\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t26 text Last camera observation\n\t\t\t27 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t29 text Captured \u00b7 generation 20\n\t\t30 text COMPLETED\n\t\t31 text Measurement recorded and reset verified. The bench is ready for another job.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t37 text Historical result\n\t\t\t38 container measurement-result\n\t\t\t\t39 text 1,729\n\t\t\t\t40 text Orange pixels\n\t\t\t\t41 container\n\t\t\t\t\t42 text 4,802.8\n\t\t\t\t\t43 text mm\u00b2\n\t\t\t\t44 text Apparent footprint\n\t\t\t\t45 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment.\n\t\t\t46 button Repeat job, ID: repeat\n\t\t47 container Activity\n\t\t\t48 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t49 text Activity\n\t\t\t50 text 24 events\n\t\t\t51 content list activity\n\t\t\t\t52 container\n\t\t\t\t\t53 text 05:42 PM Action receipt checked Network inspection 02\n\t\t\t\t54 container\n\t\t\t\t\t55 text 05:42 PM Sending a guarded action\n\t\t\t\t56 container\n\t\t\t\t\t57 text 05:42 PM Action proposal accepted Network inspection 02\n\t\t\t\t58 container\n\t\t\t\t\t59 text 05:42 PM Checking the planned action\n\t\t\t\t60 container\n\t\t\t\t\t61 text 05:42 PM AI planning requested Network inspection 02\n\t\t\t\t62 container\n\t\t\t\t\t63 text 05:42 PM Camera observation received Network inspection 02\n\t\t\t\t64 container\n\t\t\t\t\t65 text 05:42 PM Requesting camera observation\n\t\t\t\t66 container\n\t\t\t\t\t67 text 05:42 PM Action receipt checked Network inspection 02\n\t\t\t\t68 container\n\t\t\t\t\t69 text 05:42 PM Sending a guarded action\n\t\t\t\t70 container\n\t\t\t\t\t71 text 05:42 PM Action proposal accepted Network inspection 02\n\t\t\t\t72 container\n\t\t\t\t\t73 text 05:42 PM Checking the planned action\n\t\t\t\t74 container\n\t\t\t\t\t75 text 05:42 PM AI planning requested Network inspection 02\n\t\t76 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t77 text Session details\n\t\t78 text 26 model requests \u00b7 Reported tokens: 337,677 input / 3,956 output\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + } + ] +} diff --git a/jevdual/results/network-workbench-20261011/ui/disconnected.jpg b/jevdual/results/network-workbench-20261011/ui/disconnected.jpg new file mode 100644 index 0000000..8a1fc3c Binary files /dev/null and b/jevdual/results/network-workbench-20261011/ui/disconnected.jpg differ diff --git a/jevdual/results/network-workbench-20261011/ui/handoff.jpg b/jevdual/results/network-workbench-20261011/ui/handoff.jpg new file mode 100644 index 0000000..cfbfd51 Binary files /dev/null and b/jevdual/results/network-workbench-20261011/ui/handoff.jpg differ diff --git a/jevdual/results/network-workbench-20261011/ui/uncertain.jpg b/jevdual/results/network-workbench-20261011/ui/uncertain.jpg new file mode 100644 index 0000000..6be6c5a Binary files /dev/null and b/jevdual/results/network-workbench-20261011/ui/uncertain.jpg differ diff --git a/jevdual/results/onboarding-fixture-v4-preparation-20261012/final-smoke.json b/jevdual/results/onboarding-fixture-v4-preparation-20261012/final-smoke.json new file mode 100644 index 0000000..2c57da6 --- /dev/null +++ b/jevdual/results/onboarding-fixture-v4-preparation-20261012/final-smoke.json @@ -0,0 +1,44 @@ +{ + "artifact_sha256": "03dc007954a246f6f038a771e3c63afa7df8d44f5b7616b1409f8c1d0eff2fb7", + "profiles": { + "312": { + "member_verification": { + "verified": true, + "members": 14, + "source_commit": "3d0984dda6e4c1b67a2c4ca79d5adde4a275ae2a", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7" + }, + "help_exits": { + "capability_clean_install.py": 0, + "capability_fixture.py": 0, + "capability_fixture_config.py": 0, + "capability_installed_trial.py": 0 + }, + "helper_imports": 7, + "git_invoked": false, + "receipt_source_commit": "3d0984dda6e4c1b67a2c4ca79d5adde4a275ae2a", + "intentional_uv_boundary_exit": 17 + }, + "314": { + "member_verification": { + "verified": true, + "members": 14, + "source_commit": "3d0984dda6e4c1b67a2c4ca79d5adde4a275ae2a", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7" + }, + "help_exits": { + "capability_clean_install.py": 0, + "capability_fixture.py": 0, + "capability_fixture_config.py": 0, + "capability_installed_trial.py": 0 + }, + "helper_imports": 7, + "git_invoked": false, + "receipt_source_commit": "3d0984dda6e4c1b67a2c4ca79d5adde4a275ae2a", + "intentional_uv_boundary_exit": 17 + } + }, + "native_actions": "not_performed", + "installation": "stopped_at_external_uv_boundary", + "passed": true +} diff --git a/jevdual/results/onboarding-fixture-v4-preparation-20261012/qualified-smoke.json b/jevdual/results/onboarding-fixture-v4-preparation-20261012/qualified-smoke.json new file mode 100644 index 0000000..6cca057 --- /dev/null +++ b/jevdual/results/onboarding-fixture-v4-preparation-20261012/qualified-smoke.json @@ -0,0 +1,44 @@ +{ + "artifact_sha256": "eef3632506680d74b21b511cec905d73de2324f951e924b0716f2309490f590d", + "profiles": { + "312": { + "member_verification": { + "verified": true, + "members": 14, + "source_commit": "21599c641e4c54cad137b3c49e11142c5c14e596", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7" + }, + "help_exits": { + "capability_clean_install.py": 0, + "capability_fixture.py": 0, + "capability_fixture_config.py": 0, + "capability_installed_trial.py": 0 + }, + "helper_imports": 7, + "git_invoked": false, + "receipt_source_commit": "21599c641e4c54cad137b3c49e11142c5c14e596", + "intentional_uv_boundary_exit": 17 + }, + "314": { + "member_verification": { + "verified": true, + "members": 14, + "source_commit": "21599c641e4c54cad137b3c49e11142c5c14e596", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7" + }, + "help_exits": { + "capability_clean_install.py": 0, + "capability_fixture.py": 0, + "capability_fixture_config.py": 0, + "capability_installed_trial.py": 0 + }, + "helper_imports": 7, + "git_invoked": false, + "receipt_source_commit": "21599c641e4c54cad137b3c49e11142c5c14e596", + "intentional_uv_boundary_exit": 17 + } + }, + "native_actions": "not_performed", + "installation": "stopped_at_external_uv_boundary", + "passed": true +} diff --git a/jevdual/results/onboarding-fixture-v4-preparation-20261012/report.md b/jevdual/results/onboarding-fixture-v4-preparation-20261012/report.md new file mode 100644 index 0000000..0e121f8 --- /dev/null +++ b/jevdual/results/onboarding-fixture-v4-preparation-20261012/report.md @@ -0,0 +1,17 @@ +# Packet v4 preparation + +Source1d72e51 adds exact-document Accessibility cleanup and separate browser startup/window-identity phases to the fixture helper. The reviewed product wheel is unchanged. The packet repeats byte-for-byte at14members; see summary.json for source, archive and wheel hashes. + +The old main-boundary scenario leaves its owned AX window open when Automation is denied. The new helper closes the unique exact-path window and preserves the same-name other-path window. Tests challenge duplicate paths, changed window/close/sheet identity, unrelated or unrecognized dialogs, failed and unresponsive AX calls, and disappearance readback. Browser/setup/identity failure and hung SDK scenarios retain typed receipts and independently stop owned browser/listener resources. Runtime AX calls, SDK enumeration and shutdown have bounded phases; only identified English save sheets are supported. This is mocked OS-boundary evidence, not a live result. + +Root reviewed the worker diff and reran61focused fixture/onboarding/packaging tests; Ruff and notebook checks pass. An initial developer probe used a stale editable environment, then a relative source path failed a subprocess check. The corrected absolute worktree PYTHONPATH passes the full selection; no product or tests were changed to hide those probe failures. + +Both installed3.12/3.14 preparation profiles verify all frozen members, import seven helpers and pass four help entry points outside source with Git unavailable. The cold-installer provenance smoke stops at its deliberately stubbed uv boundary; it does not qualify dependency installation or native effects. See smoke.json. + +Independent Claude artifact review is requested in1791828568355220000. No v4 guest effect or permission reset has run. U3 will record fresh permission state on the previously provisioned SIP-enabled guest, with new packet root and dependencies. It will not regrade the original U2 cold-start failure or imply a new cold OS. + +Reference seams: Arc CUA6b353039 benchmarks/workflows.py:533 for AX close (Footwork retains stronger exact identity); browser-usec75e8476 browser_use/browser/watchdogs/local_browser_watchdog.py:407 for process-aware loopback CDP readiness; Chrome DevTools MCPf08dbe15 src/BrowserManager.ts:132 for abandoned launch lifetime and owned cleanup. Discovery feature changes remain outside v4. + +Claude approved the initial exact artifact23e5e16f at1d72e51 (1791828646123834000), with no blockers and review notes. Root incorporated N2/N3/N4 into frozen3d0984d: bounded synchronous identity AX reads with late-result refusal, SIGINT protection only during bounded cleanup with handler restoration, and bounded actual document file size/SHA256 snapshots before/after close. All69focused checks and Ruff pass; the final14member packet03dc0079 repeats and both installed preparation profiles pass again. Prior artifacts remain retained. Final exact-artifact confirmation is requested in1791829124898322000; no v4 guest execution or TCC reset yet. + +Final qualification candidate is21599c6,14members,312685bytes, SHA256eef3632506680d74b21b511cec905d73de2324f951e924b0716f2309490f590d, stored at output/u2-packet-v4-final-r2/footwork-onboarding-u2.zip. Repeat bytes match and both installed preparation profiles pass. The73focused checks and Ruff pass. N3 is corrected to the process-local system-wide AX default, with reset0 in nested teardown; four new boundary cases fail the prior setter scope and pass the correction. Apple documentation confirms these semantics. Claude independently verifies the exact artifact/helper Git blob and approves without blockers in1791829509202848000. Prior revisions are retained; only this candidate enters U3. No product wheel change. diff --git a/jevdual/results/onboarding-fixture-v4-preparation-20261012/smoke.json b/jevdual/results/onboarding-fixture-v4-preparation-20261012/smoke.json new file mode 100644 index 0000000..5d69d7a --- /dev/null +++ b/jevdual/results/onboarding-fixture-v4-preparation-20261012/smoke.json @@ -0,0 +1,44 @@ +{ + "artifact_sha256": "23e5e16fe5923c392b9d11e4601c390ff174234efd01af9a27653ed59b6a3d1e", + "profiles": { + "312": { + "member_verification": { + "verified": true, + "members": 14, + "source_commit": "1d72e51ef7c4c5a49dac6cfac44c95427fbeff01", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7" + }, + "help_exits": { + "capability_clean_install.py": 0, + "capability_fixture.py": 0, + "capability_fixture_config.py": 0, + "capability_installed_trial.py": 0 + }, + "helper_imports": 7, + "git_invoked": false, + "receipt_source_commit": "1d72e51ef7c4c5a49dac6cfac44c95427fbeff01", + "intentional_uv_boundary_exit": 17 + }, + "314": { + "member_verification": { + "verified": true, + "members": 14, + "source_commit": "1d72e51ef7c4c5a49dac6cfac44c95427fbeff01", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7" + }, + "help_exits": { + "capability_clean_install.py": 0, + "capability_fixture.py": 0, + "capability_fixture_config.py": 0, + "capability_installed_trial.py": 0 + }, + "helper_imports": 7, + "git_invoked": false, + "receipt_source_commit": "1d72e51ef7c4c5a49dac6cfac44c95427fbeff01", + "intentional_uv_boundary_exit": 17 + } + }, + "native_actions": "not_performed", + "installation": "stopped_at_external_uv_boundary", + "passed": true +} diff --git a/jevdual/results/onboarding-fixture-v4-preparation-20261012/summary.json b/jevdual/results/onboarding-fixture-v4-preparation-20261012/summary.json new file mode 100644 index 0000000..d7596ef --- /dev/null +++ b/jevdual/results/onboarding-fixture-v4-preparation-20261012/summary.json @@ -0,0 +1,45 @@ +{ + "source_commit": "21599c641e4c54cad137b3c49e11142c5c14e596", + "packet_sha256": "eef3632506680d74b21b511cec905d73de2324f951e924b0716f2309490f590d", + "packet_bytes": 312685, + "packet_members": 14, + "repeat_build_matches": true, + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7", + "checks": { + "focused_tests_passed": 73, + "fixture_boundary_tests": 47, + "ruff": "passed", + "notebook": "passed", + "profiles": [ + "3.12", + "3.14" + ], + "helper_imports_per_profile": 7, + "help_entries_per_profile": 4 + }, + "baseline_automation_denial": "old exact AX window remains open; new helper automatically closes it", + "claude_review": { + "approved": true, + "message": "1791829509202848000", + "blockers": 0 + }, + "guest_qualification": "not_performed", + "initial_build": { + "source_commit": "1d72e51ef7c4c5a49dac6cfac44c95427fbeff01", + "packet_sha256": "23e5e16fe5923c392b9d11e4601c390ff174234efd01af9a27653ed59b6a3d1e", + "claude_review": "approved1791828646123834000 with notesN2/N3/N4" + }, + "retained_revisions": [ + { + "source_commit": "1d72e51ef7c4c5a49dac6cfac44c95427fbeff01", + "packet_sha256": "23e5e16fe5923c392b9d11e4601c390ff174234efd01af9a27653ed59b6a3d1e", + "claude_review": "approved1791828646123834000 with notesN2/N3/N4" + }, + { + "source_commit": "3d0984dda6e4c1b67a2c4ca79d5adde4a275ae2a", + "packet_sha256": "03dc007954a246f6f038a771e3c63afa7df8d44f5b7616b1409f8c1d0eff2fb7", + "claude_review": "approved1791829172719781000 with N3 APIcorrection note" + } + ], + "packet_path": "output/u2-packet-v4-final-r2/footwork-onboarding-u2.zip" +} diff --git a/jevdual/results/onboarding-fixture-v4-repair-preparation-20261012/report.md b/jevdual/results/onboarding-fixture-v4-repair-preparation-20261012/report.md new file mode 100644 index 0000000..ab4187f --- /dev/null +++ b/jevdual/results/onboarding-fixture-v4-repair-preparation-20261012/report.md @@ -0,0 +1,7 @@ +Frozen repair packet205577ee/source445a348 corrects the observed stale AX element read after an owned close. Only post-close AX attribute errors with code-25202 cause bounded fresh observation; no close is repeated, AXPress errors remain terminal, and confirmation requires readable exact-window absence. Persistent invalidity, unreadable enumeration and renamed/reused identity refuse. Product wheel9bb0440d is unchanged. + +The frozen old source21599c6 fails five of six reproduction cases; corrected55fixture cases and81combined helper/onboarding/packaging checks pass. Ruff check/format pass. Two builds match14members/312916bytes. Claude verifies exact packet/helper blob and approves the full new permission-state run in1791832201147781000. + +The packet is pristine-verified and cold-installed into a new guest root footwork-u4-repair (seven stages/115packages). Supported Terminal-only reset and a denied doctor/refusal are observed; corrected same-command capture records exits1/2 and no resources. Baseline hashes are retained before the actual grant. Native/cleanup qualification remains pending. This existing SIP-enabled macOS26.6.2 guest is not a new cold OS. U3v1 remains failed in results/u3-packet-v4-permission-20261012-v1. + +Subsequent runtime result: final U3v3 passed its scoped permission-state and automatic-cleanup gates, with independent Claude review. See `../u3-packet-v4-permission-20261012-v3/report.md`; the preparation state above remains its historical record. diff --git a/jevdual/results/onboarding-fixture-v4-repair-preparation-20261012/summary.json b/jevdual/results/onboarding-fixture-v4-repair-preparation-20261012/summary.json new file mode 100644 index 0000000..1dcfd97 --- /dev/null +++ b/jevdual/results/onboarding-fixture-v4-repair-preparation-20261012/summary.json @@ -0,0 +1,15 @@ +{ + "source_commit": "445a348ff31243c2f3d90baae9ce209d89bd7eac", + "packet_sha256": "205577ee0848e9e68c8a15a9f25e1d308e820a054be449f631274ba7a9c7151d", + "packet_bytes": 312916, + "packet_members": 14, + "repeat_build_matches": true, + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7", + "focused_checks_passed": 81, + "fixture_checks_passed": 55, + "ruff": "passed", + "claude_approval": "1791832031158825000", + "full_journey_approval": "1791832201147781000", + "guest_runtime_qualification": "pending", + "packet_path": "output/u3-packet-v4-repair-v1/footwork-onboarding-u2.zip" +} diff --git a/jevdual/results/onboarding-remediation-20261011/report.md b/jevdual/results/onboarding-remediation-20261011/report.md new file mode 100644 index 0000000..2fbffa0 --- /dev/null +++ b/jevdual/results/onboarding-remediation-20261011/report.md @@ -0,0 +1,16 @@ +# Standalone packet v3 review remediation + +Frozen helper/doc commit: `33e1afb06710e2d2a57a0f7d1aea7ea0e19a878d`. +Packet: `output/u2-packet-v3/footwork-onboarding-u2.zip`. +SHA256: `769c08c89061731368db33d78b180e7c8c96d531190f43bee44263642a76fd0f`. +Two builds match at14members. The product wheel remains the PR2-reviewed9bb0440d artifact, byte for byte. + +Claude B1/N1/N2 are addressed: both receipt paths obtain manifest provenance before resource creation; checkout fallback runs Git only at an owned .git marker; the runbook uses one explicit Python executable and records paths/versions; a stdlib guest command verifies every extracted payload and rejects extra members. + +23 focused tests pass, plus Ruff and notebook validation. `output/u2-remediation-smoke-v3/receipt.json` records actual unpacked installer invocations on existing installed3.12.6 and3.14.7 interpreters. Git is trapped and uv intentionally exits17 at the external environment-creation boundary: v2 invokes Git and loses its receipt; v3 does not invoke Git and preserves its frozen source commit in a failed-stage receipt. This is a receipt-path smoke, not a fresh dependency installation. Both profiles run the guest verifier successfully and native entry refuses an invalid manifest before output creation or scope access. Native effects and the final successful native receipt have not been rerun here; U2 will exercise them in the independent guest. Tamper/symlink/escape/extra-member cases are covered by CLI tests. + +The checkout development .venv disappeared between checks and was recreated empty by uv; no cause was established. The repeated23tests and Ruff used the main checkout's existing development environment with the isolated checkout's source/tests. Packet tests used the retained separate full-dependency environments. No new host dependencies were installed for these checks. + +PR2 merged exact7669d3d at268f0fc; both post-merge workflows passed (38160734302,38160734323). The new packet changes remain on the local independent-onboarding branch. Claude's v3 review is requested. The user approved SSD VM launcher/image provisioning; actual clean-Mac permission qualification remains unexecuted. + +Claude approved the exact v3 archive in mailbox1791740993609795000 after independent outside-repository extraction, trapped-Git provenance checks and guest-verifier modification/extra/symlink negatives. B1/N1/N2 are closed; U2runtime success remains unproven. diff --git a/jevdual/results/onboarding-remediation-20261011/summary.json b/jevdual/results/onboarding-remediation-20261011/summary.json new file mode 100644 index 0000000..1bcdcff --- /dev/null +++ b/jevdual/results/onboarding-remediation-20261011/summary.json @@ -0,0 +1,100 @@ +{ + "source_commit": "33e1afb06710e2d2a57a0f7d1aea7ea0e19a878d", + "packet_sha256": "769c08c89061731368db33d78b180e7c8c96d531190f43bee44263642a76fd0f", + "members": 14, + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7", + "reproducible": true, + "focused_tests_passed": 23, + "ruff": "passed", + "profiles": { + "312": { + "v2": { + "returncode": 1, + "git_invoked": true, + "receipt_source_commit": null, + "external_stage": null + }, + "member_verification": { + "verified": true, + "members": 14, + "source_commit": "33e1afb06710e2d2a57a0f7d1aea7ea0e19a878d", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7" + }, + "v3": { + "returncode": 1, + "git_invoked": false, + "receipt_source_commit": "33e1afb06710e2d2a57a0f7d1aea7ea0e19a878d", + "external_stage": [ + { + "stage": "venv", + "argv": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-remediation-smoke-v3/312-v3/stub-bin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-remediation-smoke-v3/312-v3/footwork/output/install-provenance/empty-cache", + "--no-python-downloads", + "venv", + "--python", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/delivery-dependencies-py312-v1/venv/bin/python", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-remediation-smoke-v3/312-v3/footwork/output/install-provenance/venv" + ], + "returncode": 17 + } + ] + }, + "native_bad_provenance_refuses_before_resources": true + }, + "314": { + "v2": { + "returncode": 1, + "git_invoked": true, + "receipt_source_commit": null, + "external_stage": null + }, + "member_verification": { + "verified": true, + "members": 14, + "source_commit": "33e1afb06710e2d2a57a0f7d1aea7ea0e19a878d", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7" + }, + "v3": { + "returncode": 1, + "git_invoked": false, + "receipt_source_commit": "33e1afb06710e2d2a57a0f7d1aea7ea0e19a878d", + "external_stage": [ + { + "stage": "venv", + "argv": [ + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-remediation-smoke-v3/314-v3/stub-bin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-remediation-smoke-v3/314-v3/footwork/output/install-provenance/empty-cache", + "--no-python-downloads", + "venv", + "--python", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/delivery-dependencies-py314-v1/venv/bin/python", + "/Users/thomasmain/.codex/worktrees/restart-agent-acceptance/footwork/output/u2-remediation-smoke-v3/314-v3/footwork/output/install-provenance/venv" + ], + "returncode": 17 + } + ] + }, + "native_bad_provenance_refuses_before_resources": true + } + }, + "first_use_qualification": "not_performed", + "claude_review": { + "verdict": "approved_for_preparation", + "message": "1791740993609795000", + "artifact_sha256": "769c08c89061731368db33d78b180e7c8c96d531190f43bee44263642a76fd0f", + "runtime_success": false + }, + "pr2": { + "state": "merged", + "merge_commit": "268f0fc940a76e1fb071faeeb94ef12799176df2", + "post_merge_ci": "passed" + }, + "limits": "stopped_at_external_uv_boundary; native actions not performed; source unit checks use existing dev environment, packet checks use separately installed dependency environments." +} diff --git a/jevdual/results/physical-workcell-20261010/environment.json b/jevdual/results/physical-workcell-20261010/environment.json new file mode 100644 index 0000000..8dcb9e7 --- /dev/null +++ b/jevdual/results/physical-workcell-20261010/environment.json @@ -0,0 +1,19 @@ +{ + "mujoco": "3.15.0", + "python": "3.12.6", + "dependencies": [ + "absl-py==2.5.1", + "etils==1.14.0", + "fsspec==2026.9.0", + "glfw==2.10.2", + "mujoco==3.15.0", + "numpy==2.5.3", + "pyopengl==3.1.10", + "typing-extensions==4.16.0", + "websockets==17.2", + "zipp==4.1.1" + ], + "source_kind": "mujoco_simulation", + "install_scope": "tools/physical-workcell/.venv", + "work_order_sha256": "c4a5b752bff6bf7638d48c7376f1a1d950c8ccd8597c58997eee1831b57e2877" +} diff --git a/jevdual/results/physical-workcell-20261010/independent-demo.json b/jevdual/results/physical-workcell-20261010/independent-demo.json new file mode 100644 index 0000000..d7ef274 --- /dev/null +++ b/jevdual/results/physical-workcell-20261010/independent-demo.json @@ -0,0 +1,591 @@ +{ + "source_kind": "mujoco_simulation", + "source_commit": "ece54ae2b55506f52bc234d05e8dfaf1ec00c94a", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "implementation_sha256": "810d343cf500526ac884badc31bae095f819a3c3e469a3d4a3155c7a20960d3b", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1", + "checks": [ + { + "label": "initial", + "command_args": [ + "init", + "--work-order", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/inputs/work-order.json", + "--work-order-sha256", + "c4a5b752bff6bf7638d48c7376f1a1d950c8ccd8597c58997eee1831b57e2877" + ], + "exit_code": 0, + "result": { + "block_center_in_zone": false, + "block_xyz": [ + -0.11999998822184511, + 1.630059551357049e-11, + 0.03499573045537004 + ], + "claims": [ + "native_simulator_state_observation" + ], + "frame": "sim_world", + "generation": 0, + "order_id": "footwork-stage-block-001", + "phase": "ready", + "physical_condition_verified": false, + "pusher_xyz": [ + -0.3, + 0.0, + 0.06 + ], + "simulation_time_s": 1.0000000000000007, + "source_kind": "mujoco_simulation", + "state_authority": "current_persisted_simulator_state", + "state_sha256": "3a8c7d1b2c047630a067a0590f939c066b6c621349b6021d26609c85b2e4644b", + "target_zone": { + "x_max": 0.26, + "x_min": 0.18, + "y_max": 0.04, + "y_min": -0.04 + } + } + }, + { + "label": "before-render", + "command_args": [ + "render" + ], + "exit_code": 0, + "result": { + "generation": 0, + "height": 360, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/render-000000.png", + "image_sha256": "5238c61efa5d4b1ec098c6c6f87c0b864b427376a44874b033b0c8a1484ec597", + "simulator_state_sha256": "3a8c7d1b2c047630a067a0590f939c066b6c621349b6021d26609c85b2e4644b", + "source_kind": "mujoco_simulation", + "width": 640 + } + }, + { + "label": "premature-completion", + "command_args": [ + "complete", + "--token", + "early-001" + ], + "exit_code": 2, + "result": { + "source_kind": "mujoco_simulation", + "phase": "refused", + "reason": "observed block center is not in the work-order zone; no software result was created", + "claims": [] + } + }, + { + "label": "contact-motion", + "command_args": [ + "act", + "--token", + "move-001", + "--expected-generation", + "0", + "--target-x", + "0.15", + "--target-y", + "0" + ], + "exit_code": 0, + "result": { + "observation": { + "block_center_in_zone": true, + "block_xyz": [ + 0.20075701935390755, + -8.394987444074871e-11, + 0.03499834119111764 + ], + "claims": [ + "native_simulator_state_observation" + ], + "frame": "sim_world", + "generation": 1, + "order_id": "footwork-stage-block-001", + "phase": "ready", + "physical_condition_verified": true, + "pusher_xyz": [ + 0.14575969206037903, + -2.3249397224564703e-19, + 0.06 + ], + "simulation_time_s": 4.999999999999671, + "source_kind": "mujoco_simulation", + "state_authority": "current_persisted_simulator_state", + "state_sha256": "95629caca4dca0408800ca3cb6ec6a48b8db2af5a19b0828d866e210b4e33afc", + "target_zone": { + "x_max": 0.26, + "x_min": 0.18, + "y_max": 0.04, + "y_min": -0.04 + } + }, + "operation": { + "after_generation": 1, + "after_state_sha256": "95629caca4dca0408800ca3cb6ec6a48b8db2af5a19b0828d866e210b4e33afc", + "before_state_sha256": "3a8c7d1b2c047630a067a0590f939c066b6c621349b6021d26609c85b2e4644b", + "controller_evidence": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 1.278071451355089, + "native_steps": 2000, + "pusher_block_contact_samples": 4192 + }, + "historical_motion_outcome": "executed", + "intent_time_unix": 1791661017.950069, + "phase": "executed", + "physical_readback": { + "block_center_in_zone": true, + "block_xyz": [ + 0.20075701935390755, + -8.394987444074871e-11, + 0.03499834119111764 + ], + "pusher_xyz": [ + 0.14575969206037903, + -2.3249397224564703e-19, + 0.06 + ] + }, + "request": { + "expected_generation": 0, + "kind": "act", + "target_x": 0.15, + "target_y": 0.0 + }, + "source_kind": "mujoco_simulation", + "token": "move-001" + }, + "replayed": false, + "source_kind": "mujoco_simulation" + } + }, + { + "label": "fresh-process-observation", + "command_args": [ + "observe" + ], + "exit_code": 0, + "result": { + "block_center_in_zone": true, + "block_xyz": [ + 0.20075701935390755, + -8.394987444074871e-11, + 0.03499834119111764 + ], + "claims": [ + "native_simulator_state_observation" + ], + "frame": "sim_world", + "generation": 1, + "order_id": "footwork-stage-block-001", + "phase": "ready", + "physical_condition_verified": true, + "pusher_xyz": [ + 0.14575969206037903, + -2.3249397224564703e-19, + 0.06 + ], + "simulation_time_s": 4.999999999999671, + "source_kind": "mujoco_simulation", + "state_authority": "current_persisted_simulator_state", + "state_sha256": "95629caca4dca0408800ca3cb6ec6a48b8db2af5a19b0828d866e210b4e33afc", + "target_zone": { + "x_max": 0.26, + "x_min": 0.18, + "y_max": 0.04, + "y_min": -0.04 + } + } + }, + { + "label": "stale-generation", + "command_args": [ + "act", + "--token", + "stale-001", + "--expected-generation", + "0", + "--target-x", + "0.1", + "--target-y", + "0" + ], + "exit_code": 2, + "result": { + "source_kind": "mujoco_simulation", + "phase": "refused", + "reason": "observation generation is stale; observe again", + "claims": [] + } + }, + { + "label": "out-of-bounds", + "command_args": [ + "act", + "--token", + "bounds-001", + "--expected-generation", + "1", + "--target-x", + "1", + "--target-y", + "0" + ], + "exit_code": 2, + "result": { + "source_kind": "mujoco_simulation", + "phase": "refused", + "reason": "pusher target is outside the admitted workspace", + "claims": [] + } + }, + { + "label": "after-render", + "command_args": [ + "render" + ], + "exit_code": 0, + "result": { + "generation": 1, + "height": 360, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/render-000001.png", + "image_sha256": "76649d3f99a8046a03bde722a3049891b1b766d1ede37040c517bb67bb75e848", + "simulator_state_sha256": "95629caca4dca0408800ca3cb6ec6a48b8db2af5a19b0828d866e210b4e33afc", + "source_kind": "mujoco_simulation", + "width": 640 + } + }, + { + "label": "conditional-completion", + "command_args": [ + "complete", + "--token", + "finish-001" + ], + "exit_code": 0, + "result": { + "observation": { + "block_center_in_zone": true, + "block_xyz": [ + 0.20075701935390755, + -8.394987444074871e-11, + 0.03499834119111764 + ], + "claims": [ + "native_simulator_state_observation" + ], + "current_software_artifact_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1193, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/expected-order.json", + "expected_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "limit_bytes": 1048576, + "observed_bytes": 1193, + "observed_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/order.json", + "source_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f" + }, + "frame": "sim_world", + "generation": 1, + "historical_software_outcome": "staged", + "order_id": "footwork-stage-block-001", + "phase": "completed", + "physical_condition_verified": true, + "pusher_xyz": [ + 0.14575969206037903, + -2.3249397224564703e-19, + 0.06 + ], + "simulation_time_s": 4.999999999999671, + "source_kind": "mujoco_simulation", + "state_authority": "current_persisted_simulator_state", + "state_sha256": "95629caca4dca0408800ca3cb6ec6a48b8db2af5a19b0828d866e210b4e33afc", + "target_zone": { + "x_max": 0.26, + "x_min": 0.18, + "y_max": 0.04, + "y_min": -0.04 + } + }, + "operation": { + "artifact_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1193, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/expected-order.json", + "expected_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "limit_bytes": 1048576, + "observed_bytes": 1193, + "observed_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/order.json", + "source_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f" + }, + "historical_software_outcome": "staged", + "intent_time_unix": 1791661018.626794, + "phase": "staged", + "physical_readback": { + "block_center_in_zone": true, + "block_xyz": [ + 0.20075701935390755, + -8.394987444074871e-11, + 0.03499834119111764 + ], + "pusher_xyz": [ + 0.14575969206037903, + -2.3249397224564703e-19, + 0.06 + ] + }, + "request": { + "kind": "complete", + "work_order_sha256": "c4a5b752bff6bf7638d48c7376f1a1d950c8ccd8597c58997eee1831b57e2877" + }, + "result_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "source_kind": "mujoco_simulation", + "state_sha256": "95629caca4dca0408800ca3cb6ec6a48b8db2af5a19b0828d866e210b4e33afc", + "token": "finish-001" + }, + "replayed": false, + "source_kind": "mujoco_simulation" + } + }, + { + "label": "repeated-motion-token", + "command_args": [ + "act", + "--token", + "move-001", + "--expected-generation", + "0", + "--target-x", + "0.15", + "--target-y", + "0" + ], + "exit_code": 0, + "result": { + "observation": { + "block_center_in_zone": true, + "block_xyz": [ + 0.20075701935390755, + -8.394987444074871e-11, + 0.03499834119111764 + ], + "claims": [ + "native_simulator_state_observation" + ], + "current_software_artifact_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1193, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/expected-order.json", + "expected_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "limit_bytes": 1048576, + "observed_bytes": 1193, + "observed_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/order.json", + "source_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f" + }, + "frame": "sim_world", + "generation": 1, + "historical_software_outcome": "staged", + "order_id": "footwork-stage-block-001", + "phase": "completed", + "physical_condition_verified": true, + "pusher_xyz": [ + 0.14575969206037903, + -2.3249397224564703e-19, + 0.06 + ], + "simulation_time_s": 4.999999999999671, + "source_kind": "mujoco_simulation", + "state_authority": "current_persisted_simulator_state", + "state_sha256": "95629caca4dca0408800ca3cb6ec6a48b8db2af5a19b0828d866e210b4e33afc", + "target_zone": { + "x_max": 0.26, + "x_min": 0.18, + "y_max": 0.04, + "y_min": -0.04 + } + }, + "operation": { + "after_generation": 1, + "after_state_sha256": "95629caca4dca0408800ca3cb6ec6a48b8db2af5a19b0828d866e210b4e33afc", + "before_state_sha256": "3a8c7d1b2c047630a067a0590f939c066b6c621349b6021d26609c85b2e4644b", + "controller_evidence": { + "command_speed_limit_m_s": 0.15, + "max_axis_actuator_force_n": 1.278071451355089, + "native_steps": 2000, + "pusher_block_contact_samples": 4192 + }, + "historical_motion_outcome": "executed", + "intent_time_unix": 1791661017.950069, + "phase": "executed", + "physical_readback": { + "block_center_in_zone": true, + "block_xyz": [ + 0.20075701935390755, + -8.394987444074871e-11, + 0.03499834119111764 + ], + "pusher_xyz": [ + 0.14575969206037903, + -2.3249397224564703e-19, + 0.06 + ] + }, + "request": { + "expected_generation": 0, + "kind": "act", + "target_x": 0.15, + "target_y": 0.0 + }, + "source_kind": "mujoco_simulation", + "token": "move-001" + }, + "replayed": false, + "source_kind": "mujoco_simulation" + } + }, + { + "label": "repeated-completion-token", + "command_args": [ + "complete", + "--token", + "finish-001" + ], + "exit_code": 0, + "result": { + "observation": { + "block_center_in_zone": true, + "block_xyz": [ + 0.20075701935390755, + -8.394987444074871e-11, + 0.03499834119111764 + ], + "claims": [ + "native_simulator_state_observation" + ], + "current_software_artifact_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1193, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/expected-order.json", + "expected_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "limit_bytes": 1048576, + "observed_bytes": 1193, + "observed_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/order.json", + "source_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f" + }, + "frame": "sim_world", + "generation": 1, + "historical_software_outcome": "staged", + "order_id": "footwork-stage-block-001", + "phase": "completed", + "physical_condition_verified": true, + "pusher_xyz": [ + 0.14575969206037903, + -2.3249397224564703e-19, + 0.06 + ], + "simulation_time_s": 4.999999999999671, + "source_kind": "mujoco_simulation", + "state_authority": "current_persisted_simulator_state", + "state_sha256": "95629caca4dca0408800ca3cb6ec6a48b8db2af5a19b0828d866e210b4e33afc", + "target_zone": { + "x_max": 0.26, + "x_min": 0.18, + "y_max": 0.04, + "y_min": -0.04 + } + }, + "operation": { + "artifact_check": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1193, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/expected-order.json", + "expected_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "limit_bytes": 1048576, + "observed_bytes": 1193, + "observed_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/order.json", + "source_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f" + }, + "historical_software_outcome": "staged", + "intent_time_unix": 1791661018.626794, + "phase": "staged", + "physical_readback": { + "block_center_in_zone": true, + "block_xyz": [ + 0.20075701935390755, + -8.394987444074871e-11, + 0.03499834119111764 + ], + "pusher_xyz": [ + 0.14575969206037903, + -2.3249397224564703e-19, + 0.06 + ] + }, + "request": { + "kind": "complete", + "work_order_sha256": "c4a5b752bff6bf7638d48c7376f1a1d950c8ccd8597c58997eee1831b57e2877" + }, + "result_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "source_kind": "mujoco_simulation", + "state_sha256": "95629caca4dca0408800ca3cb6ec6a48b8db2af5a19b0828d866e210b4e33afc", + "token": "finish-001" + }, + "replayed": false, + "source_kind": "mujoco_simulation" + } + }, + { + "label": "independent-public-footwork-check", + "exit_code": 0, + "result": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 1193, + "expected_file": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/expected-order.json", + "expected_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "limit_bytes": 1048576, + "observed_bytes": 1193, + "observed_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/demo-v1/order.json", + "source_sha256": "978bd2888c4677bdaf7e97c826fbb86cfd052a086932b203208e64e4397e650f" + } + } + ], + "all_assertions_passed": true, + "limits": [ + "simulator ground truth; no learned vision", + "one XY pusher; no robot hardware", + "local work-order file; no external project update" + ] +} diff --git a/jevdual/results/physical-workcell-20261010/report.md b/jevdual/results/physical-workcell-20261010/report.md new file mode 100644 index 0000000..2c9b652 --- /dev/null +++ b/jevdual/results/physical-workcell-20261010/report.md @@ -0,0 +1,33 @@ +# SIM1: Physical and computer workcell + +The independent public CLI run passed on frozen source/model commit `ece54ae`, after prediction commit `49904e3`. Native MuJoCo contact moved the block into the bound work-order zone; only then did the conditional computer action create a local staged order. A separate public Footwork check verified the saved bytes. + +## Evidence + +`independent-demo.json` retains public command arguments, exit codes, source/model hashes, observations, controller evidence, artifact checks and assertions. `environment.json` records the isolated pinned runtime. The private run is `tools/physical-workcell/output/demo-v1`; its two inspected native renders are `render-000000.png` and `render-000001.png`. + +| Check | Observed | +|---|---| +| Before physical condition | Block x = -0.119999988 m; center outside zone | +| Premature completion | Refused; no software order file created | +| Native contact motion | Block x = 0.200757019 m; center inside x=[0.18,0.26], y=[-0.04,0.04] m | +| Controller | 2,000 native steps; 4,192 pusher/block contact samples; peak axis actuator force 1.278071451 N against a 4 N cap | +| Fresh process | Same generation, state hash and block pose; no implicit reset | +| Stale/out-of-envelope requests | Refused before dispatch | +| Conditional computer result | One local `order.json` with state `staged`; separate public Footwork byte check passed | +| Repeated motion/completion tokens | Retained physics, result and manifest bytes unchanged; no redispatch | +| Native rendering | Actual MuJoCo RGB before/after images inspected; source identified as simulation | + +The scoped conformance suite passed 13 tests on the actual pinned native engine. It also covers noncontact motion failing fulfillment, lost physical/software receipts, interrupted state publication, tampered state/order/model/runtime, missing/tampered results without recreation, invalid/nonfinite inputs and cross-process ownership. Fault injection tests address local bookkeeping failures; they do not qualify hardware failure recovery. + +## Decision + +Keep this prototype under `tools/physical-workcell`, inside Footwork. The agent-facing interface is `init`, `observe`, `act`, `complete`, with separate `render`. It delegates motion to a bounded native physics controller; it checks physical state before the local computer effect. No new core motion API or external project write is introduced. + +The next experiment should replace pose ground truth with camera-based observation and compare it against held-out simulator truth. After that, a mobile simulated body can navigate to an observation point before initiating a computer task. These are proposed experiments, not implemented capabilities. Notes remain supporting records of task intent, observations and verified outcomes. + +## Limits + +This is one XY pusher, one free block, a block-center zone predicate and one local software artifact. There is no autonomous learned planner, vision grounding, gripper, navigation, robot hardware or real-time operating-system guarantee. The target uses simulator coordinates. The observations are simulator state, not camera recognition. Footwork's file check proves expected byte persistence; the native state check supplies the simulated physical evidence. + +Python handles task/state/receipt orchestration. MuJoCo supplies native C/C++ physics/rendering. Existing camera capture uses FFmpeg and Swift/Apple Vision. No new Rust route was justified: earlier workbench profiling found small-file hash and in-process artifact checks cheap relative to process/helper startup. Sustained video/sensor streams and bounded action dispatch are future profiling candidates; native physics and low-level robot controllers should remain outside the model loop. diff --git a/jevdual/results/physical-workcell-20261010/verification.json b/jevdual/results/physical-workcell-20261010/verification.json new file mode 100644 index 0000000..bbd3831 --- /dev/null +++ b/jevdual/results/physical-workcell-20261010/verification.json @@ -0,0 +1,35 @@ +{ + "checks": [ + { + "label": "native-conformance", + "argv": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/.venv/bin/python", + "-m", + "unittest", + "discover", + "-s", + "tools/physical-workcell", + "-p", + "test_*.py", + "-v" + ], + "exit_code": 0, + "output": "test_before_condition_false_and_completion_creates_nothing (test_workcell.WorkcellTests.test_before_condition_false_and_completion_creates_nothing) ... ok\ntest_cross_process_lock_prevents_second_owner (test_workcell.WorkcellTests.test_cross_process_lock_prevents_second_owner) ... ok\ntest_fresh_process_continuity_repeat_and_one_digital_result (test_workcell.WorkcellTests.test_fresh_process_continuity_repeat_and_one_digital_result) ... ok\ntest_init_lookup_is_repeat_safe_and_changed_order_refused (test_workcell.WorkcellTests.test_init_lookup_is_repeat_safe_and_changed_order_refused) ... ok\ntest_interrupted_state_publication_does_not_implicitly_reset_or_reexecute (test_workcell.WorkcellTests.test_interrupted_state_publication_does_not_implicitly_reset_or_reexecute) ... ok\ntest_lost_motion_receipt_blocks_replay_and_completion (test_workcell.WorkcellTests.test_lost_motion_receipt_blocks_replay_and_completion) ... ok\ntest_lost_software_receipt_never_promotes_valid_bytes_or_replays (test_workcell.WorkcellTests.test_lost_software_receipt_never_promotes_valid_bytes_or_replays) ... ok\ntest_malformed_and_nonfinite_inputs_fail_without_traceback (test_workcell.WorkcellTests.test_malformed_and_nonfinite_inputs_fail_without_traceback) ... ok\ntest_model_state_order_and_runtime_tamper_refuse_implicit_reset (test_workcell.WorkcellTests.test_model_state_order_and_runtime_tamper_refuse_implicit_reset) ... ok\ntest_noncontact_command_does_not_fulfill_order (test_workcell.WorkcellTests.test_noncontact_command_does_not_fulfill_order) ... ok\ntest_real_contact_changes_block_and_enables_independent_computer_result (test_workcell.WorkcellTests.test_real_contact_changes_block_and_enables_independent_computer_result) ... ok\ntest_tampered_or_missing_result_preserves_history_without_recreation (test_workcell.WorkcellTests.test_tampered_or_missing_result_preserves_history_without_recreation) ... ok\ntest_token_arguments_stale_generation_and_workspace_refuse_before_step (test_workcell.WorkcellTests.test_token_arguments_stale_generation_and_workspace_refuse_before_step) ... ok\n\n----------------------------------------------------------------------\nRan 13 tests in 3.263s\n\nOK\n" + }, + { + "label": "ruff", + "argv": [ + "/Users/thomasmain/projects/footwork/jevdual/.venv/bin/ruff", + "check", + "tools/physical-workcell" + ], + "exit_code": 0, + "output": "All checks passed!\n" + }, + { + "label": "measured-source-unchanged", + "implementation_sha256": "810d343cf500526ac884badc31bae095f819a3c3e469a3d4a3155c7a20960d3b", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f" + } + ] +} diff --git a/jevdual/results/release-candidate-20261012/extracted-verification.json b/jevdual/results/release-candidate-20261012/extracted-verification.json new file mode 100644 index 0000000..b4675bc --- /dev/null +++ b/jevdual/results/release-candidate-20261012/extracted-verification.json @@ -0,0 +1 @@ +{"verified": true, "members": 30, "source_commit": "d9315593886c17196db477db969f21481fc10e8d", "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7"} diff --git a/jevdual/results/release-candidate-20261012/report-v1.md b/jevdual/results/release-candidate-20261012/report-v1.md new file mode 100644 index 0000000..5e5e01b --- /dev/null +++ b/jevdual/results/release-candidate-20261012/report-v1.md @@ -0,0 +1,7 @@ +Local candidate v1 passed artifact checks and independent Claude review (1791836643188530000). A derived candidate will remove identifying paths from legacy evidence before sharing; original evidence and v1 remain unchanged. + +Frozen release source 5031b50 assembles the qualified wheel and helpers from product source 445a348. Two builds produce identical ZIP bytes, SHA256 47d87922b79d66207e7057b47e01d13013e844cbd075636c18e67605bd06abb6, with 30 members and 341107 bytes. All payload manifest digests and wheel RECORD/runtime bytes verify against committed source. The wheel, eight helpers and MIT license remain byte-identical to the approved qualification packet. The extracted consumer verifier passes and all 18 relative Markdown links resolve. + +The bundle contains setup and agent-host instructions, per-member hashes, exact product/release/harness provenance, reviewed guest and real-host evidence summaries, and separately labeled historical installation context. It excludes dependencies, caches, virtual environments, browser profiles, credentials, raw transcripts and operational databases. Install dependencies from PyPI in an owned environment with existing Python/uv/Chrome prerequisites. + +The guest evidence covers the supported Terminal permission reset and captured grant on macOS 26.6.2; the real-host evidence covers one Claude Code 2.1.296 Opus 5.5 clean task on already-granted macOS 26.5.1. Disk persistence, pixel routes, other agent hosts and cold OS are not qualified. The original failed and interrupted runs remain preserved. This package is a local candidate: no registry publication, tag, push or merge occurred. diff --git a/jevdual/results/release-candidate-20261012/report.md b/jevdual/results/release-candidate-20261012/report.md new file mode 100644 index 0000000..914cc09 --- /dev/null +++ b/jevdual/results/release-candidate-20261012/report.md @@ -0,0 +1,11 @@ +The final local release package passed artifact checks and independent Claude review. Message 1791837075792802000 approves the exact derived bundle and closes the path-disclosure note. + +Frozen release source d931559 preserves the qualified product from source 445a348. Two builds produce identical ZIP bytes, SHA256 8dc81c724bf617a9f3aa4833f250d5124b9e8c5e9a1c504c67d634080f44fd1f, with 30 members and 342599 bytes. Every manifest digest and wheel RECORD/runtime byte verifies against committed source. The wheel, eight helpers and MIT license remain byte-identical to the approved qualification packet. The extracted consumer verifier passes and all 18 relative Markdown links resolve. + +The bundle contains setup and agent-host instructions, exact product/release/harness provenance, reviewed guest and real-host evidence summaries, and separately labeled historical installation context. Ten legacy evidence files are derived display copies with stable path placeholders and original/derived SHA256 records. The scan finds zero identifying home paths across text members and inside the wheel. Original committed evidence and the first approved local bundle remain unchanged. Before correction, the leaked-path boundary test failed; afterward, 19 release checks and Ruff pass. The independent installed-host harness has 19 passing checks. + +The guest evidence covers the supported Terminal permission reset and captured grant on macOS 26.6.2. The real-host evidence covers one Claude Code 2.1.296 Opus 5.5 clean task on already-granted macOS 26.5.1. It used one native effect, one durable journal operation and the enforced completion adapter, followed by automatic cleanup. Disk persistence, pixel routes, other agent hosts and a cold OS are not qualified; earlier failures and interruptions remain preserved. + +The wheel excludes dependencies, which install from PyPI using existing Python/uv/Chrome prerequisites. The bundle excludes caches, virtual environments, browser profiles, credentials, raw transcripts and operational databases. This is a local release candidate; no registry publication, tag, push or merge occurred. + +Artifact: output/release-candidate-20261012-v2/build-one/footwork-capability-0.0.1-macos-arm64-candidate.zip. SHA256SUMS and the extracted manifest accompany it. Both previous local packaging and original runtime evidence remain preserved. diff --git a/jevdual/results/release-candidate-20261012/review-v1.json b/jevdual/results/release-candidate-20261012/review-v1.json new file mode 100644 index 0000000..a040b7b --- /dev/null +++ b/jevdual/results/release-candidate-20261012/review-v1.json @@ -0,0 +1,6 @@ +{ + "message": "1791836643188530000", + "verdict": "approved local candidate", + "note": "189 identifying home/worktree paths in legacy derived attachments; remediate before external sharing", + "sha256": "47d87922b79d66207e7057b47e01d13013e844cbd075636c18e67605bd06abb6" +} diff --git a/jevdual/results/release-candidate-20261012/review.json b/jevdual/results/release-candidate-20261012/review.json new file mode 100644 index 0000000..2a50893 --- /dev/null +++ b/jevdual/results/release-candidate-20261012/review.json @@ -0,0 +1,17 @@ +{ + "reviewer": "Claude via authorized mailbox", + "runtime_approval": "1791836103010895000", + "first_local_bundle_approval": "1791836643188530000", + "final_bundle_approval": "1791837075792802000", + "verdict": "final local release packaging approved; N1 closed", + "sha256": "8dc81c724bf617a9f3aa4833f250d5124b9e8c5e9a1c504c67d634080f44fd1f", + "checks_independently_reproduced": [ + "repeatable ZIP", + "30-member extracted verifier", + "10 unchanged qualified payloads", + "outer/nested-wheel path scan", + "all evidence JSON parse", + "10 original/derived evidence hash pairs" + ], + "limits": "same scoped host and guest permissions; no registry, tag, push or merge" +} diff --git a/jevdual/results/release-candidate-20261012/summary-v1.json b/jevdual/results/release-candidate-20261012/summary-v1.json new file mode 100644 index 0000000..1118297 --- /dev/null +++ b/jevdual/results/release-candidate-20261012/summary-v1.json @@ -0,0 +1,28 @@ +{ + "repeated_bytes_equal": true, + "sha256": "47d87922b79d66207e7057b47e01d13013e844cbd075636c18e67605bd06abb6", + "members": 30, + "size_bytes": 341107, + "relative_links_verified": 18, + "unchanged_qualified_payloads": 10, + "release_source_commit": "5031b5034c4015104f8b0da54df1a720889bc0a7", + "qualified_product_source_commit": "445a348ff31243c2f3d90baae9ce209d89bd7eac", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7", + "local_candidate_only": true, + "passed": true, + "independent_review": "approved", + "qualification": "local candidate packaging only", + "host_qualification": "../installed-agent-release-20261012-v2", + "permission_qualification": "../u3-packet-v4-permission-20261012-v3", + "focused_checks": { + "installed_host": 19, + "release_builder": 16 + }, + "registry_publication": false, + "tag_created": false, + "push_or_merge": false, + "review_message": "1791836643188530000", + "scope": "local candidate v1", + "external_distribution": "not_ready_due_to_identifying_legacy_paths", + "sanitization_followup": "IA3" +} diff --git a/jevdual/results/release-candidate-20261012/summary.json b/jevdual/results/release-candidate-20261012/summary.json new file mode 100644 index 0000000..4bbdc50 --- /dev/null +++ b/jevdual/results/release-candidate-20261012/summary.json @@ -0,0 +1,32 @@ +{ + "repeated_bytes_equal": true, + "sha256": "8dc81c724bf617a9f3aa4833f250d5124b9e8c5e9a1c504c67d634080f44fd1f", + "members": 30, + "size_bytes": 342599, + "relative_links_verified": 18, + "unchanged_qualified_payloads": 10, + "release_source_commit": "d9315593886c17196db477db969f21481fc10e8d", + "qualified_product_source_commit": "445a348ff31243c2f3d90baae9ce209d89bd7eac", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7", + "identifying_home_paths": 0, + "scan_includes_nested_wheel": true, + "derived_legacy_attachments": 10, + "local_candidate_only": true, + "passed": true, + "independent_review": "approved", + "qualification": "local candidate packaging only", + "host_qualification": "../installed-agent-release-20261012-v2", + "permission_qualification": "../u3-packet-v4-permission-20261012-v3", + "focused_checks": { + "installed_host": 19, + "release_builder": 19 + }, + "registry_publication": false, + "tag_created": false, + "push_or_merge": false, + "previous_local_candidate": "summary-v1.json", + "original_evidence_preserved": true, + "review_message": "1791837075792802000", + "sanitization_note": "N1 closed; original/derived hashes independently verified", + "candidate_path": "output/release-candidate-20261012-v2/build-one/footwork-capability-0.0.1-macos-arm64-candidate.zip" +} diff --git a/jevdual/results/repeatable-work-orders-20261010/baseline-v1.json b/jevdual/results/repeatable-work-orders-20261010/baseline-v1.json new file mode 100644 index 0000000..5e0b8c4 --- /dev/null +++ b/jevdual/results/repeatable-work-orders-20261010/baseline-v1.json @@ -0,0 +1,482 @@ +{ + "source_commit": "b9941ef", + "doctor": { + "exit_code": 0, + "result": { + "checks": { + "footwork_checker": true, + "native_runtime_pinned": true, + "numpy_available": true, + "renderer": true + }, + "mujoco_version": "3.15.0", + "phase": "ready", + "scope": "dependency readiness only; no episode, persisted state, action or camera acquisition", + "source_kind": "mujoco_simulation" + } + }, + "episodes": [ + { + "case_id": "ordinary-a", + "group": "ordinary", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/ordinary-a", + "exit_code": 0, + "wall_call_seconds": 1.2456378936767578, + "public_result": { + "case_sha256": "4a60068d9e562246812c02d57e978259e24697274f3ea5055bda8175c85a5751", + "declaration": { + "explanation": "Fresh RGB completion proposal received a staged software receipt.\n", + "explanation_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/ordinary-a/baseline-explanation.txt", + "explanation_sha256": "5b03217d327bb49b459fca24b1e2a108f01a427d2f590b40a1a2c09cd7ab58c4", + "finished_time_unix": 1791665370.330595, + "interventions_declared": 0, + "outcome": "completed" + }, + "episode_id": "e822fa4d00d345a8bc89acc09029b49f", + "history": [ + { + "decision": "move", + "generation": 0, + "phase": "executed", + "token": "rgb-000" + }, + { + "decision": "complete", + "generation": 3, + "phase": "staged", + "token": "rgb-003" + }, + { + "decision": "move", + "generation": 2, + "phase": "executed", + "token": "rgb-002" + }, + { + "decision": "move", + "generation": 1, + "phase": "executed", + "token": "rgb-001" + } + ], + "participant": "deterministic_rgb", + "phase": "finished", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/ordinary-a", + "source_kind": "mujoco_simulation", + "started_time_unix": 1791665369.1890612 + }, + "stderr": "", + "evaluation_exit_code": 0, + "evaluation": { + "action_count": 4, + "attempted": 1, + "case_sha256": "4a60068d9e562246812c02d57e978259e24697274f3ea5055bda8175c85a5751", + "completed": 1, + "contact_motion_count": 3, + "current_software_ok": true, + "declared_outcome": "completed", + "driver_failed": false, + "elapsed_s": 1.1415338516235352, + "episode_id": "e822fa4d00d345a8bc89acc09029b49f", + "evidence_errors": [], + "failed": 0, + "finished": true, + "historical_software_staged": true, + "incorrect_completion_claim": false, + "interventions_declared": 0, + "motion_executed_count": 3, + "participant": "deterministic_rgb", + "physical_condition": true, + "qualified_completion": true, + "refused_completion_count": 0, + "role": "parent_only_evaluator", + "setup_failed": false, + "source_kind": "mujoco_simulation", + "unresolved": 0, + "visual_completion_attempts": 1, + "visual_false_positive_count": 0, + "visual_positive_count": 1, + "visual_verdicts": [ + { + "bound_native_condition": true, + "generation": 3, + "reason": "fresh RGB center satisfies the conservative zone", + "token": "rgb-003", + "visual_false_positive": false, + "visual_positive": true + } + ] + } + }, + { + "case_id": "ordinary-b", + "group": "ordinary", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/ordinary-b", + "exit_code": 0, + "wall_call_seconds": 1.458353042602539, + "public_result": { + "case_sha256": "b6a36fffbf83a68af315944ff1f92cdfd9a0dfa48c921dd11a6f64f4b564b926", + "declaration": { + "explanation": "Fresh RGB completion proposal received a staged software receipt.\n", + "explanation_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/ordinary-b/baseline-explanation.txt", + "explanation_sha256": "5b03217d327bb49b459fca24b1e2a108f01a427d2f590b40a1a2c09cd7ab58c4", + "finished_time_unix": 1791665372.149882, + "interventions_declared": 0, + "outcome": "completed" + }, + "episode_id": "2b5357cd5a0d4e06bede3bc57d5ff285", + "history": [ + { + "decision": "move", + "generation": 0, + "phase": "executed", + "token": "rgb-000" + }, + { + "decision": "move", + "generation": 3, + "phase": "executed", + "token": "rgb-003" + }, + { + "decision": "move", + "generation": 2, + "phase": "executed", + "token": "rgb-002" + }, + { + "decision": "complete", + "generation": 4, + "phase": "staged", + "token": "rgb-004" + }, + { + "decision": "move", + "generation": 1, + "phase": "executed", + "token": "rgb-001" + } + ], + "participant": "deterministic_rgb", + "phase": "finished", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/ordinary-b", + "source_kind": "mujoco_simulation", + "started_time_unix": 1791665370.801292 + }, + "stderr": "", + "evaluation_exit_code": 0, + "evaluation": { + "action_count": 5, + "attempted": 1, + "case_sha256": "b6a36fffbf83a68af315944ff1f92cdfd9a0dfa48c921dd11a6f64f4b564b926", + "completed": 1, + "contact_motion_count": 3, + "current_software_ok": true, + "declared_outcome": "completed", + "driver_failed": false, + "elapsed_s": 1.3485901355743408, + "episode_id": "2b5357cd5a0d4e06bede3bc57d5ff285", + "evidence_errors": [], + "failed": 0, + "finished": true, + "historical_software_staged": true, + "incorrect_completion_claim": false, + "interventions_declared": 0, + "motion_executed_count": 4, + "participant": "deterministic_rgb", + "physical_condition": true, + "qualified_completion": true, + "refused_completion_count": 0, + "role": "parent_only_evaluator", + "setup_failed": false, + "source_kind": "mujoco_simulation", + "unresolved": 0, + "visual_completion_attempts": 1, + "visual_false_positive_count": 0, + "visual_positive_count": 1, + "visual_verdicts": [ + { + "bound_native_condition": true, + "generation": 4, + "reason": "fresh RGB center satisfies the conservative zone", + "token": "rgb-004", + "visual_false_positive": false, + "visual_positive": true + } + ] + } + }, + { + "case_id": "ordinary-c", + "group": "ordinary", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/ordinary-c", + "exit_code": 0, + "wall_call_seconds": 1.4272665977478027, + "public_result": { + "case_sha256": "fa34684c4d313a2fb765c2ad1bed38f7cfc37d447436486ff87d88380c25fbb7", + "declaration": { + "explanation": "Fresh RGB completion proposal received a staged software receipt.\n", + "explanation_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/ordinary-c/baseline-explanation.txt", + "explanation_sha256": "5b03217d327bb49b459fca24b1e2a108f01a427d2f590b40a1a2c09cd7ab58c4", + "finished_time_unix": 1791665373.945101, + "interventions_declared": 0, + "outcome": "completed" + }, + "episode_id": "ea08bacdeee54db596cb90e687abe88c", + "history": [ + { + "decision": "move", + "generation": 0, + "phase": "executed", + "token": "rgb-000" + }, + { + "decision": "move", + "generation": 3, + "phase": "executed", + "token": "rgb-003" + }, + { + "decision": "move", + "generation": 2, + "phase": "executed", + "token": "rgb-002" + }, + { + "decision": "complete", + "generation": 4, + "phase": "staged", + "token": "rgb-004" + }, + { + "decision": "move", + "generation": 1, + "phase": "executed", + "token": "rgb-001" + } + ], + "participant": "deterministic_rgb", + "phase": "finished", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/ordinary-c", + "source_kind": "mujoco_simulation", + "started_time_unix": 1791665372.623067 + }, + "stderr": "", + "evaluation_exit_code": 0, + "evaluation": { + "action_count": 5, + "attempted": 1, + "case_sha256": "fa34684c4d313a2fb765c2ad1bed38f7cfc37d447436486ff87d88380c25fbb7", + "completed": 1, + "contact_motion_count": 3, + "current_software_ok": true, + "declared_outcome": "completed", + "driver_failed": false, + "elapsed_s": 1.3220341205596924, + "episode_id": "ea08bacdeee54db596cb90e687abe88c", + "evidence_errors": [], + "failed": 0, + "finished": true, + "historical_software_staged": true, + "incorrect_completion_claim": false, + "interventions_declared": 0, + "motion_executed_count": 4, + "participant": "deterministic_rgb", + "physical_condition": true, + "qualified_completion": true, + "refused_completion_count": 0, + "role": "parent_only_evaluator", + "setup_failed": false, + "source_kind": "mujoco_simulation", + "unresolved": 0, + "visual_completion_attempts": 1, + "visual_false_positive_count": 0, + "visual_positive_count": 1, + "visual_verdicts": [ + { + "bound_native_condition": true, + "generation": 4, + "reason": "fresh RGB center satisfies the conservative zone", + "token": "rgb-004", + "visual_false_positive": false, + "visual_positive": true + } + ] + } + }, + { + "case_id": "low-light", + "group": "challenge", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/low-light", + "exit_code": 0, + "wall_call_seconds": 0.21388792991638184, + "public_result": { + "case_sha256": "30321dd8243b1305de5dad17e6f4f9d1230187e6f8dd6845d82c56508e871d98", + "declaration": { + "explanation": "Acquired RGB image has unavailable or ambiguous part recognition.\n", + "explanation_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/low-light/baseline-explanation.txt", + "explanation_sha256": "2054ff7634f4c4fb93a47a0848f353b3b7789b8ebcebc9e9eabe937e1939b634", + "finished_time_unix": 1791665374.5302908, + "interventions_declared": 0, + "outcome": "unresolved" + }, + "episode_id": "fdd98251a28347e5a36febc35f0e237e", + "history": [], + "participant": "deterministic_rgb", + "phase": "finished", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/low-light", + "source_kind": "mujoco_simulation", + "started_time_unix": 1791665374.4217238 + }, + "stderr": "", + "evaluation_exit_code": 0, + "evaluation": { + "action_count": 0, + "attempted": 1, + "case_sha256": "30321dd8243b1305de5dad17e6f4f9d1230187e6f8dd6845d82c56508e871d98", + "completed": 0, + "contact_motion_count": 0, + "current_software_ok": false, + "declared_outcome": "unresolved", + "driver_failed": false, + "elapsed_s": 0.1085669994354248, + "episode_id": "fdd98251a28347e5a36febc35f0e237e", + "evidence_errors": [], + "failed": 0, + "finished": true, + "historical_software_staged": false, + "incorrect_completion_claim": false, + "interventions_declared": 0, + "motion_executed_count": 0, + "participant": "deterministic_rgb", + "physical_condition": false, + "qualified_completion": false, + "refused_completion_count": 0, + "role": "parent_only_evaluator", + "setup_failed": false, + "source_kind": "mujoco_simulation", + "unresolved": 1, + "visual_completion_attempts": 0, + "visual_false_positive_count": 0, + "visual_positive_count": 0, + "visual_verdicts": [] + } + }, + { + "case_id": "occlusion", + "group": "challenge", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/occlusion", + "exit_code": 0, + "wall_call_seconds": 0.21574687957763672, + "public_result": { + "case_sha256": "934880ad3736e73cb761b64d3cfda109a7e40d08320e770fb3edcb50c90769da", + "declaration": { + "explanation": "Acquired RGB image has unavailable or ambiguous part recognition.\n", + "explanation_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/occlusion/baseline-explanation.txt", + "explanation_sha256": "2054ff7634f4c4fb93a47a0848f353b3b7789b8ebcebc9e9eabe937e1939b634", + "finished_time_unix": 1791665374.8538518, + "interventions_declared": 0, + "outcome": "unresolved" + }, + "episode_id": "d97255015d6641089984f4b3f61c63e8", + "history": [], + "participant": "deterministic_rgb", + "phase": "finished", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/occlusion", + "source_kind": "mujoco_simulation", + "started_time_unix": 1791665374.7464461 + }, + "stderr": "", + "evaluation_exit_code": 0, + "evaluation": { + "action_count": 0, + "attempted": 1, + "case_sha256": "934880ad3736e73cb761b64d3cfda109a7e40d08320e770fb3edcb50c90769da", + "completed": 0, + "contact_motion_count": 0, + "current_software_ok": false, + "declared_outcome": "unresolved", + "driver_failed": false, + "elapsed_s": 0.1074056625366211, + "episode_id": "d97255015d6641089984f4b3f61c63e8", + "evidence_errors": [], + "failed": 0, + "finished": true, + "historical_software_staged": false, + "incorrect_completion_claim": false, + "interventions_declared": 0, + "motion_executed_count": 0, + "participant": "deterministic_rgb", + "physical_condition": false, + "qualified_completion": false, + "refused_completion_count": 0, + "role": "parent_only_evaluator", + "setup_failed": false, + "source_kind": "mujoco_simulation", + "unresolved": 1, + "visual_completion_attempts": 0, + "visual_false_positive_count": 0, + "visual_positive_count": 0, + "visual_verdicts": [] + } + }, + { + "case_id": "camera-shift", + "group": "challenge", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/camera-shift", + "exit_code": 0, + "wall_call_seconds": 0.21552515029907227, + "public_result": { + "case_sha256": "cbf8a3b2612965dd2246523ba630bd86760192f9f8b3a01d9be9d7af89fc72c6", + "declaration": { + "explanation": "Acquired RGB image has invalid calibration; no motion admitted.\n", + "explanation_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/camera-shift/baseline-explanation.txt", + "explanation_sha256": "15ea03df5cb667b60ca920cefe7d979a3ce091398e4d897c6b2d8f4a1bcec944", + "finished_time_unix": 1791665375.1818979, + "interventions_declared": 0, + "outcome": "unresolved" + }, + "episode_id": "56f14e28ceda47df938551de89395f4d", + "history": [], + "participant": "deterministic_rgb", + "phase": "finished", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/deterministic/camera-shift", + "source_kind": "mujoco_simulation", + "started_time_unix": 1791665375.073507 + }, + "stderr": "", + "evaluation_exit_code": 0, + "evaluation": { + "action_count": 0, + "attempted": 1, + "case_sha256": "cbf8a3b2612965dd2246523ba630bd86760192f9f8b3a01d9be9d7af89fc72c6", + "completed": 0, + "contact_motion_count": 0, + "current_software_ok": false, + "declared_outcome": "unresolved", + "driver_failed": false, + "elapsed_s": 0.10839080810546875, + "episode_id": "56f14e28ceda47df938551de89395f4d", + "evidence_errors": [], + "failed": 0, + "finished": true, + "historical_software_staged": false, + "incorrect_completion_claim": false, + "interventions_declared": 0, + "motion_executed_count": 0, + "participant": "deterministic_rgb", + "physical_condition": false, + "qualified_completion": false, + "refused_completion_count": 0, + "role": "parent_only_evaluator", + "setup_failed": false, + "source_kind": "mujoco_simulation", + "unresolved": 1, + "visual_completion_attempts": 0, + "visual_false_positive_count": 0, + "visual_positive_count": 0, + "visual_verdicts": [] + } + } + ] +} diff --git a/jevdual/results/repeatable-work-orders-20261010/case-freeze.json b/jevdual/results/repeatable-work-orders-20261010/case-freeze.json new file mode 100644 index 0000000..b3e63ed --- /dev/null +++ b/jevdual/results/repeatable-work-orders-20261010/case-freeze.json @@ -0,0 +1,49 @@ +{ + "protocol_commit": "5128f37", + "suite_sha256": "36ded4e5e8b98227ef5f35ce2457ad72f6fbfb92d4999484541959d6dcef40d7", + "cases": [ + { + "case_id": "ordinary-a", + "group": "ordinary", + "file": "ordinary-a.json", + "case_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/cases/ordinary-a.json", + "case_sha256": "4a60068d9e562246812c02d57e978259e24697274f3ea5055bda8175c85a5751" + }, + { + "case_id": "ordinary-b", + "group": "ordinary", + "file": "ordinary-b.json", + "case_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/cases/ordinary-b.json", + "case_sha256": "b6a36fffbf83a68af315944ff1f92cdfd9a0dfa48c921dd11a6f64f4b564b926" + }, + { + "case_id": "ordinary-c", + "group": "ordinary", + "file": "ordinary-c.json", + "case_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/cases/ordinary-c.json", + "case_sha256": "fa34684c4d313a2fb765c2ad1bed38f7cfc37d447436486ff87d88380c25fbb7" + }, + { + "case_id": "low-light", + "group": "challenge", + "file": "low-light.json", + "case_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/cases/low-light.json", + "case_sha256": "30321dd8243b1305de5dad17e6f4f9d1230187e6f8dd6845d82c56508e871d98" + }, + { + "case_id": "occlusion", + "group": "challenge", + "file": "occlusion.json", + "case_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/cases/occlusion.json", + "case_sha256": "934880ad3736e73cb761b64d3cfda109a7e40d08320e770fb3edcb50c90769da" + }, + { + "case_id": "camera-shift", + "group": "challenge", + "file": "camera-shift.json", + "case_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/cases/camera-shift.json", + "case_sha256": "cbf8a3b2612965dd2246523ba630bd86760192f9f8b3a01d9be9d7af89fc72c6" + } + ], + "development_excluded": true +} diff --git a/jevdual/results/repeatable-work-orders-20261010/checks.json b/jevdual/results/repeatable-work-orders-20261010/checks.json new file mode 100644 index 0000000..70b97cb --- /dev/null +++ b/jevdual/results/repeatable-work-orders-20261010/checks.json @@ -0,0 +1,42 @@ +{ + "checks": [ + { + "label": "native_conformance", + "command": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/.venv/bin/python", + "-m", + "unittest", + "discover", + "-s", + "tools/physical-workcell", + "-p", + "test_*.py", + "-v" + ], + "exit_code": 0, + "output": "test_budget_exhaustion_refuses_before_wrapper_or_native_intent (test_episode_cases.EpisodeCaseTests.test_budget_exhaustion_refuses_before_wrapper_or_native_intent) ... ok\ntest_case_source_symlink_and_bad_digest_refuse_before_initialization (test_episode_cases.EpisodeCaseTests.test_case_source_symlink_and_bad_digest_refuse_before_initialization) ... ok\ntest_case_validation_exact_fields_finite_bounds_and_duplicates (test_episode_cases.EpisodeCaseTests.test_case_validation_exact_fields_finite_bounds_and_duplicates) ... ok\ntest_changed_source_retained_case_and_partial_initialization_refuse (test_episode_cases.EpisodeCaseTests.test_changed_source_retained_case_and_partial_initialization_refuse) ... ok\ntest_completion_is_an_admitted_action_without_manufactured_motion (test_episode_cases.EpisodeCaseTests.test_completion_is_an_admitted_action_without_manufactured_motion) ... ok\ntest_false_visual_verdict_and_exact_request_retained_before_oracle (test_episode_cases.EpisodeCaseTests.test_false_visual_verdict_and_exact_request_retained_before_oracle) ... ok\ntest_initial_xy_applied_once_and_repeat_never_resets_motion (test_episode_cases.EpisodeCaseTests.test_initial_xy_applied_once_and_repeat_never_resets_motion) ... ok\ntest_initialization_marker_exact_case_timing_and_no_stray_file_exception (test_episode_cases.EpisodeCaseTests.test_initialization_marker_exact_case_timing_and_no_stray_file_exception) ... ok\ntest_offset_image_has_invalid_calibration_and_no_world_center (test_episode_cases.EpisodeCaseTests.test_offset_image_has_invalid_calibration_and_no_world_center) ... ok\ntest_positive_visual_verdict_survives_native_gate_failure_as_unknown (test_episode_cases.EpisodeCaseTests.test_positive_visual_verdict_survives_native_gate_failure_as_unknown) ... ok\ntest_unavailable_and_occluded_images_remain_inspectable_without_effects (test_episode_cases.EpisodeCaseTests.test_unavailable_and_occluded_images_remain_inspectable_without_effects) ... ok\ntest_actual_rgb_baseline_completes_with_contact_and_independent_saved_file (test_episode_runner.EpisodeTests.test_actual_rgb_baseline_completes_with_contact_and_independent_saved_file) ... ok\ntest_already_placed_case_real_visual_and_software_completion_has_zero_motion (test_episode_runner.EpisodeTests.test_already_placed_case_real_visual_and_software_completion_has_zero_motion) ... ok\ntest_bound_generation_verdict_counts_hidden_false_positive (test_episode_runner.EpisodeTests.test_bound_generation_verdict_counts_hidden_false_positive) ... ok\ntest_case_binding_no_implicit_reset_and_public_init_filtering (test_episode_runner.EpisodeTests.test_case_binding_no_implicit_reset_and_public_init_filtering) ... ok\ntest_doctor_has_no_episode_or_persisted_simulator_effect (test_episode_runner.EpisodeTests.test_doctor_has_no_episode_or_persisted_simulator_effect) ... ok\ntest_failed_rgb_recognition_retains_image_and_declares_unresolved (test_episode_runner.EpisodeTests.test_failed_rgb_recognition_retains_image_and_declares_unresolved) ... ok\ntest_failed_setup_is_durable_counted_and_never_retried (test_episode_runner.EpisodeTests.test_failed_setup_is_durable_counted_and_never_retried) ... ok\ntest_false_completion_claim_is_visible_without_software (test_episode_runner.EpisodeTests.test_false_completion_claim_is_visible_without_software) ... ok\ntest_finish_declaration_elapsed_idempotency_conflict_and_no_effects (test_episode_runner.EpisodeTests.test_finish_declaration_elapsed_idempotency_conflict_and_no_effects) ... ok\ntest_invalid_calibration_retains_image_and_blocks_baseline (test_episode_runner.EpisodeTests.test_invalid_calibration_retains_image_and_blocks_baseline) ... ok\ntest_satisfactory_current_scene_does_not_manufacture_history (test_episode_runner.EpisodeTests.test_satisfactory_current_scene_does_not_manufacture_history) ... ok\ntest_small_budget_reserves_completion_slot_and_performs_no_unbudgeted_motion (test_episode_runner.EpisodeTests.test_small_budget_reserves_completion_slot_and_performs_no_unbudgeted_motion) ... ok\ntest_unexpected_baseline_driver_failure_is_finished_and_counted (test_episode_runner.EpisodeTests.test_unexpected_baseline_driver_failure_is_finished_and_counted) ... ok\ntest_bad_silhouettes_reject_blank_multiple_occluded_and_off_frame (test_image_guided.ImageGuidedTests.test_bad_silhouettes_reject_blank_multiple_occluded_and_off_frame) ... ok\ntest_current_result_tamper_or_missing_preserves_historical_staging_without_replay (test_image_guided.ImageGuidedTests.test_current_result_tamper_or_missing_preserves_historical_staging_without_replay) ... ok\ntest_forged_oracle_extra_field_or_perception_scope_is_not_returned (test_image_guided.ImageGuidedTests.test_forged_oracle_extra_field_or_perception_scope_is_not_returned) ... ok\ntest_lost_wrapper_receipt_blocks_replay_and_new_views (test_image_guided.ImageGuidedTests.test_lost_wrapper_receipt_blocks_replay_and_new_views) ... ok\ntest_native_rgb_calibration_full_silhouette_and_no_oracle_fields (test_image_guided.ImageGuidedTests.test_native_rgb_calibration_full_silhouette_and_no_oracle_fields) ... ok\ntest_partial_view_is_not_rerendered_or_promoted (test_image_guided.ImageGuidedTests.test_partial_view_is_not_rerendered_or_promoted) ... ok\ntest_premature_visual_completion_refuses_before_native_effect (test_image_guided.ImageGuidedTests.test_premature_visual_completion_refuses_before_native_effect) ... ok\ntest_public_fresh_process_view_apply_repeats_are_filtered (test_image_guided.ImageGuidedTests.test_public_fresh_process_view_apply_repeats_are_filtered) ... ok\ntest_rgb_feedback_correction_and_final_private_oracle_agreement (test_image_guided.ImageGuidedTests.test_rgb_feedback_correction_and_final_private_oracle_agreement) ... ok\ntest_source_freeze_and_png_limits_are_enforced (test_image_guided.ImageGuidedTests.test_source_freeze_and_png_limits_are_enforced) ... ok\ntest_tampered_image_stale_proposal_and_changed_token_never_step (test_image_guided.ImageGuidedTests.test_tampered_image_stale_proposal_and_changed_token_never_step) ... ok\ntest_tampered_task_metadata_and_historical_receipt_refuse (test_image_guided.ImageGuidedTests.test_tampered_task_metadata_and_historical_receipt_refuse) ... ok\ntest_wrong_box_reverse_oversize_and_lateral_targets_refuse (test_image_guided.ImageGuidedTests.test_wrong_box_reverse_oversize_and_lateral_targets_refuse) ... ok\ntest_before_condition_false_and_completion_creates_nothing (test_workcell.WorkcellTests.test_before_condition_false_and_completion_creates_nothing) ... ok\ntest_cross_process_lock_prevents_second_owner (test_workcell.WorkcellTests.test_cross_process_lock_prevents_second_owner) ... ok\ntest_fresh_process_continuity_repeat_and_one_digital_result (test_workcell.WorkcellTests.test_fresh_process_continuity_repeat_and_one_digital_result) ... ok\ntest_init_lookup_is_repeat_safe_and_changed_order_refused (test_workcell.WorkcellTests.test_init_lookup_is_repeat_safe_and_changed_order_refused) ... ok\ntest_interrupted_state_publication_does_not_implicitly_reset_or_reexecute (test_workcell.WorkcellTests.test_interrupted_state_publication_does_not_implicitly_reset_or_reexecute) ... ok\ntest_lost_motion_receipt_blocks_replay_and_completion (test_workcell.WorkcellTests.test_lost_motion_receipt_blocks_replay_and_completion) ... ok\ntest_lost_software_receipt_never_promotes_valid_bytes_or_replays (test_workcell.WorkcellTests.test_lost_software_receipt_never_promotes_valid_bytes_or_replays) ... ok\ntest_malformed_and_nonfinite_inputs_fail_without_traceback (test_workcell.WorkcellTests.test_malformed_and_nonfinite_inputs_fail_without_traceback) ... ok\ntest_model_state_order_and_runtime_tamper_refuse_implicit_reset (test_workcell.WorkcellTests.test_model_state_order_and_runtime_tamper_refuse_implicit_reset) ... ok\ntest_noncontact_command_does_not_fulfill_order (test_workcell.WorkcellTests.test_noncontact_command_does_not_fulfill_order) ... ok\ntest_real_contact_changes_block_and_enables_independent_computer_result (test_workcell.WorkcellTests.test_real_contact_changes_block_and_enables_independent_computer_result) ... ok\ntest_tampered_or_missing_result_preserves_history_without_recreation (test_workcell.WorkcellTests.test_tampered_or_missing_result_preserves_history_without_recreation) ... ok\ntest_token_arguments_stale_generation_and_workspace_refuse_before_step (test_workcell.WorkcellTests.test_token_arguments_stale_generation_and_workspace_refuse_before_step) ... ok\n\n----------------------------------------------------------------------\nRan 50 tests in 13.916s\n\nOK\n" + }, + { + "label": "ruff", + "command": [ + "/Users/thomasmain/projects/footwork/jevdual/.venv/bin/ruff", + "check", + "tools/physical-workcell" + ], + "exit_code": 0, + "output": "All checks passed!\n" + }, + { + "label": "source_freeze", + "commit": "b9941eff16912fde5479d015d841ca08d548647d", + "sha256": { + "episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "workcell.py": "965339a7b9b2c0b439805e638ee536f45df8cc6d2c023ea6455afdd19cbdfd89", + "image_guided.py": "7bb7b7f244500c24c444aa314e8902bc5db66146de9b2ed9914e0cf0e9e399fb", + "episode_runner.py": "530c8e65d2b4c613e3c02c8dcc5e9f8d56559e5f119b011bb3e138667b1474e7", + "EPISODES.md": "32b6e8eda50350346ea0b861b61908040044c7650853e47679a9fa7856beab0a", + "proposal.schema.json": "4f632336093c6b9d81741a4deff879b07eea7c9290eb34cbfa75ed84359b90e6" + } + } + ] +} diff --git a/jevdual/results/repeatable-work-orders-20261010/closure-checks.json b/jevdual/results/repeatable-work-orders-20261010/closure-checks.json new file mode 100644 index 0000000..563d08b --- /dev/null +++ b/jevdual/results/repeatable-work-orders-20261010/closure-checks.json @@ -0,0 +1,25 @@ +{ + "recorded_at": "2026-10-10T17:00:21.017059-04:00", + "qualification_episodes_rerun": false, + "retained_episode_count": 12, + "frozen_runtime_hashes_unchanged": true, + "frozen_case_hashes_unchanged": true, + "ordinary_qualified_completion_and_current_software_contact_verified": true, + "challenge_images_retained_and_zero_actions_verified": true, + "notebook_check": { + "command": "uv run python -m evals.notebook docs/notebook --check", + "exit_code": 0, + "summary": "333 entries, 113 predictions: 80 held, 22 failed, 10 inconclusive, 1 open" + }, + "result_redaction": { + "command": "uv run python scripts/redact_results.py results/repeatable-work-orders-20261010", + "exit_code": 0, + "files_changed": 0 + }, + "prose_hygiene": { + "files_checked": 6, + "removed": 0, + "replaced": 0 + }, + "git_diff_check_exit_code": 0 +} diff --git a/jevdual/results/repeatable-work-orders-20261010/comparison.json b/jevdual/results/repeatable-work-orders-20261010/comparison.json new file mode 100644 index 0000000..8cd5d09 --- /dev/null +++ b/jevdual/results/repeatable-work-orders-20261010/comparison.json @@ -0,0 +1,60 @@ +{ + "source_commit": "b9941ef", + "participants": { + "deterministic_rgb": { + "scheduled": 6, + "attempted": 6, + "ordinary_attempted": 3, + "ordinary_completed": 3, + "completed_all": 3, + "unresolved": 3, + "failed": 0, + "setup_failed": 0, + "driver_failed": 0, + "incorrect_completion_claims": 0, + "visual_false_positives": 0, + "interventions_declared": 0, + "motion_executed_count": 11, + "elapsed_s": { + "ordinary-a": 1.1415338516235352, + "ordinary-b": 1.3485901355743408, + "ordinary-c": 1.3220341205596924, + "low-light": 0.1085669994354248, + "occlusion": 0.1074056625366211, + "camera-shift": 0.10839080810546875 + } + }, + "fresh_host": { + "scheduled": 6, + "attempted": 6, + "ordinary_attempted": 3, + "ordinary_completed": 3, + "completed_all": 3, + "unresolved": 3, + "failed": 0, + "setup_failed": 0, + "driver_failed": 0, + "incorrect_completion_claims": 0, + "visual_false_positives": 0, + "interventions_declared": 0, + "motion_executed_count": 13, + "elapsed_s": { + "ordinary-a": 66.88709688186646, + "ordinary-b": 49.85168480873108, + "ordinary-c": 55.4614360332489, + "low-light": 11.956594228744507, + "occlusion": 12.454973936080933, + "camera-shift": 10.831374168395996 + } + } + }, + "first_use_setup_issues": { + "deterministic_rgb": 0, + "fresh_host": 1 + }, + "limits": [ + "one fresh context and six cases; no statistical comparison", + "baseline single process; host model and CLI round trips; elapsed times descriptive", + "fixed colors and bounded forward pusher; no hardware/app/control-service qualification" + ] +} diff --git a/jevdual/results/repeatable-work-orders-20261010/fresh-host-v1.json b/jevdual/results/repeatable-work-orders-20261010/fresh-host-v1.json new file mode 100644 index 0000000..226c64c --- /dev/null +++ b/jevdual/results/repeatable-work-orders-20261010/fresh-host-v1.json @@ -0,0 +1,522 @@ +{ + "source_commit": "b9941ef", + "participant": "fresh Codex context; fork_turns=none; no parent trajectory or baseline results", + "role_separation": "instruction based on a shared filesystem; not adversarial isolation", + "episodes": [ + { + "case_id": "ordinary-a", + "group": "ordinary", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a", + "evaluation_exit_code": 0, + "evaluation": { + "action_count": 5, + "attempted": 1, + "case_sha256": "4a60068d9e562246812c02d57e978259e24697274f3ea5055bda8175c85a5751", + "completed": 1, + "contact_motion_count": 4, + "current_software_ok": true, + "declared_outcome": "completed", + "driver_failed": false, + "elapsed_s": 66.88709688186646, + "episode_id": "ce808923dbb24bba869ec4c2f0596b3c", + "evidence_errors": [], + "failed": 0, + "finished": true, + "historical_software_staged": true, + "incorrect_completion_claim": false, + "interventions_declared": 0, + "motion_executed_count": 4, + "participant": "fresh_host", + "physical_condition": true, + "qualified_completion": true, + "refused_completion_count": 0, + "role": "parent_only_evaluator", + "setup_failed": false, + "source_kind": "mujoco_simulation", + "unresolved": 0, + "visual_completion_attempts": 1, + "visual_false_positive_count": 0, + "visual_positive_count": 1, + "visual_verdicts": [ + { + "bound_native_condition": true, + "generation": 4, + "reason": "fresh RGB center satisfies the conservative zone", + "token": "a-complete", + "visual_false_positive": false, + "visual_positive": true + } + ] + }, + "scoring_artifacts_unchanged": true + }, + { + "case_id": "ordinary-b", + "group": "ordinary", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b", + "evaluation_exit_code": 0, + "evaluation": { + "action_count": 5, + "attempted": 1, + "case_sha256": "b6a36fffbf83a68af315944ff1f92cdfd9a0dfa48c921dd11a6f64f4b564b926", + "completed": 1, + "contact_motion_count": 3, + "current_software_ok": true, + "declared_outcome": "completed", + "driver_failed": false, + "elapsed_s": 49.85168480873108, + "episode_id": "a96a0dda578c40c7ab7fa7a3e00412e4", + "evidence_errors": [], + "failed": 0, + "finished": true, + "historical_software_staged": true, + "incorrect_completion_claim": false, + "interventions_declared": 0, + "motion_executed_count": 4, + "participant": "fresh_host", + "physical_condition": true, + "qualified_completion": true, + "refused_completion_count": 0, + "role": "parent_only_evaluator", + "setup_failed": false, + "source_kind": "mujoco_simulation", + "unresolved": 0, + "visual_completion_attempts": 1, + "visual_false_positive_count": 0, + "visual_positive_count": 1, + "visual_verdicts": [ + { + "bound_native_condition": true, + "generation": 4, + "reason": "fresh RGB center satisfies the conservative zone", + "token": "b-complete", + "visual_false_positive": false, + "visual_positive": true + } + ] + }, + "scoring_artifacts_unchanged": true + }, + { + "case_id": "ordinary-c", + "group": "ordinary", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c", + "evaluation_exit_code": 0, + "evaluation": { + "action_count": 6, + "attempted": 1, + "case_sha256": "fa34684c4d313a2fb765c2ad1bed38f7cfc37d447436486ff87d88380c25fbb7", + "completed": 1, + "contact_motion_count": 4, + "current_software_ok": true, + "declared_outcome": "completed", + "driver_failed": false, + "elapsed_s": 55.4614360332489, + "episode_id": "d1d86c011138446fa7affdaf115de38c", + "evidence_errors": [], + "failed": 0, + "finished": true, + "historical_software_staged": true, + "incorrect_completion_claim": false, + "interventions_declared": 0, + "motion_executed_count": 5, + "participant": "fresh_host", + "physical_condition": true, + "qualified_completion": true, + "refused_completion_count": 0, + "role": "parent_only_evaluator", + "setup_failed": false, + "source_kind": "mujoco_simulation", + "unresolved": 0, + "visual_completion_attempts": 1, + "visual_false_positive_count": 0, + "visual_positive_count": 1, + "visual_verdicts": [ + { + "bound_native_condition": true, + "generation": 5, + "reason": "fresh RGB center satisfies the conservative zone", + "token": "c-complete", + "visual_false_positive": false, + "visual_positive": true + } + ] + }, + "scoring_artifacts_unchanged": true + }, + { + "case_id": "low-light", + "group": "challenge", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/low-light", + "evaluation_exit_code": 0, + "evaluation": { + "action_count": 0, + "attempted": 1, + "case_sha256": "30321dd8243b1305de5dad17e6f4f9d1230187e6f8dd6845d82c56508e871d98", + "completed": 0, + "contact_motion_count": 0, + "current_software_ok": false, + "declared_outcome": "unresolved", + "driver_failed": false, + "elapsed_s": 11.956594228744507, + "episode_id": "d97892314d284652b8a38c00114b898b", + "evidence_errors": [], + "failed": 0, + "finished": true, + "historical_software_staged": false, + "incorrect_completion_claim": false, + "interventions_declared": 0, + "motion_executed_count": 0, + "participant": "fresh_host", + "physical_condition": false, + "qualified_completion": false, + "refused_completion_count": 0, + "role": "parent_only_evaluator", + "setup_failed": false, + "source_kind": "mujoco_simulation", + "unresolved": 1, + "visual_completion_attempts": 0, + "visual_false_positive_count": 0, + "visual_positive_count": 0, + "visual_verdicts": [] + }, + "scoring_artifacts_unchanged": true + }, + { + "case_id": "occlusion", + "group": "challenge", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/occlusion", + "evaluation_exit_code": 0, + "evaluation": { + "action_count": 0, + "attempted": 1, + "case_sha256": "934880ad3736e73cb761b64d3cfda109a7e40d08320e770fb3edcb50c90769da", + "completed": 0, + "contact_motion_count": 0, + "current_software_ok": false, + "declared_outcome": "unresolved", + "driver_failed": false, + "elapsed_s": 12.454973936080933, + "episode_id": "a0e0b348dc89436cbecb2cb89bc1a2bb", + "evidence_errors": [], + "failed": 0, + "finished": true, + "historical_software_staged": false, + "incorrect_completion_claim": false, + "interventions_declared": 0, + "motion_executed_count": 0, + "participant": "fresh_host", + "physical_condition": false, + "qualified_completion": false, + "refused_completion_count": 0, + "role": "parent_only_evaluator", + "setup_failed": false, + "source_kind": "mujoco_simulation", + "unresolved": 1, + "visual_completion_attempts": 0, + "visual_false_positive_count": 0, + "visual_positive_count": 0, + "visual_verdicts": [] + }, + "scoring_artifacts_unchanged": true + }, + { + "case_id": "camera-shift", + "group": "challenge", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/camera-shift", + "evaluation_exit_code": 0, + "evaluation": { + "action_count": 0, + "attempted": 1, + "case_sha256": "cbf8a3b2612965dd2246523ba630bd86760192f9f8b3a01d9be9d7af89fc72c6", + "completed": 0, + "contact_motion_count": 0, + "current_software_ok": false, + "declared_outcome": "unresolved", + "driver_failed": false, + "elapsed_s": 10.831374168395996, + "episode_id": "63f90c15005c43f4b8af26d37031e053", + "evidence_errors": [], + "failed": 0, + "finished": true, + "historical_software_staged": false, + "incorrect_completion_claim": false, + "interventions_declared": 0, + "motion_executed_count": 0, + "participant": "fresh_host", + "physical_condition": false, + "qualified_completion": false, + "refused_completion_count": 0, + "role": "parent_only_evaluator", + "setup_failed": false, + "source_kind": "mujoco_simulation", + "unresolved": 1, + "visual_completion_attempts": 0, + "visual_false_positive_count": 0, + "visual_positive_count": 0, + "visual_verdicts": [] + }, + "scoring_artifacts_unchanged": true + } + ], + "host_summary": { + "participant": "fresh_host", + "source_commit_supplied": "b9941ef", + "native_truth_scored_by_host": false, + "external_interventions": 0, + "doctor_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/doctor.json", + "tool_setup_issues": [ + "Initial doctor output redirection failed because fresh-host directory did not exist. ordinary-a init created it, then doctor executed successfully before its first view. Thus doctor was attempted first but actually ran after first initialization; no reset/retry of any episode." + ], + "permitted_helpers": "Ephemeral Python serialized explicit host-selected proposals and computed exact byte digests; parsed public view/feedback/finish JSON. No move-selection/controller code or hidden input read.", + "cases": [ + { + "case_id": "ordinary-a", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a", + "episode_id": "ce808923dbb24bba869ec4c2f0596b3c", + "case_sha256": "4a60068d9e562246812c02d57e978259e24697274f3ea5055bda8175c85a5751", + "declared_outcome": "completed", + "finish_phase": "finished", + "finish_public_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/finish-public.json", + "explanation_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/explanation.txt", + "explanation_sha256": "368bf56a49c209a5918254d74f80c001ad80c1a73070682abc9150f84bb8364e", + "inspected_image_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/vision-frame-000000.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/vision-frame-000001.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/vision-frame-000002.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/vision-frame-000003.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/vision-frame-000004.png" + ], + "public_packet_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/view-0.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/view-1.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/view-2.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/view-3.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/view-4.json" + ], + "proposal_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/a-move-1.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/a-move-2.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/a-move-3.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/a-move-4.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/a-complete.json" + ], + "proposal_sha256": { + "a-move-1.json": "4f2095025a9258abb723be8d712206cac55c00574907ff529b9cfeddcfa420c8", + "a-move-2.json": "915b3fb6066f3b171ac7289a9d79d9c2ab2f37411222531aa27ed7604127a2af", + "a-move-3.json": "6e13901b848afb1f2a1d54c12d55c0938ccf5753e6728c35e6a13aca55b0f1a3", + "a-move-4.json": "75542874b4d1c3407337ee4cf2c8bdbfa4fd7e059ae26fde1809d1f3920f8c58", + "a-complete.json": "1d97bd2197375916b8af59fde1a20d195501bc2fceb84702fc02b15a65f91f9c" + }, + "external_interventions": 0, + "admitted_move_receipts": 4, + "completion_receipts": 1, + "decision_rationale": "Inspected RGB generations 0 through 4. Orange block and blue pusher were visible and recognized, with valid calibration. Selected forward targets -0.19, -0.085, 0.025, 0.065 at y=0 from each current image and public positions, keeping each forward step below 0.12 m. After four executed moves the RGB center was (0.1158333,0), inside requested x=[0.08,0.16], y=[-0.04,0.04] with margin. The image supports placement. a-complete returned staged, inside_with_margin true, and historical/current software byte checks ok. Declared completed on that public evidence. No external interventions. Setup note: initial doctor redirection failed because fresh-host directory did not exist, so doctor actually ran after ordinary-a init and before its first view; it reported ready. No episode reset or replay occurred.", + "receipt_basis": { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-a/feedback-complete.json", + "phase": "staged", + "inside_with_margin": true, + "historical_software_ok": true, + "current_software_ok": true + }, + "blocked_reason": null + }, + { + "case_id": "ordinary-b", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b", + "episode_id": "a96a0dda578c40c7ab7fa7a3e00412e4", + "case_sha256": "b6a36fffbf83a68af315944ff1f92cdfd9a0dfa48c921dd11a6f64f4b564b926", + "declared_outcome": "completed", + "finish_phase": "finished", + "finish_public_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/finish-public.json", + "explanation_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/explanation.txt", + "explanation_sha256": "bcb800d09c3a552637e5286d5297e4c92ce46467f90eda8f0f49f2cbaef31d52", + "inspected_image_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/vision-frame-000000.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/vision-frame-000001.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/vision-frame-000002.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/vision-frame-000003.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/vision-frame-000004.png" + ], + "public_packet_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/view-0.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/view-1.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/view-2.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/view-3.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/view-4.json" + ], + "proposal_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/b-move-1.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/b-move-2.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/b-move-3.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/b-move-4.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/b-complete.json" + ], + "proposal_sha256": { + "b-move-1.json": "0e02f779b75a8958819eea5812ad4946f28ce40a2a77a1a502435b8b1254dcce", + "b-move-2.json": "7c74f8e18fa64d764901ef64414be41593ac53f838b01e61cfd3c1e2b8f01bde", + "b-move-3.json": "a13b8202e2296711fbb2f8e4324838853307fd39740e13dd68c7a4339c0bfe86", + "b-move-4.json": "441af04ca0f3e01c0cfca7212e5acb2b0200ce3b55913d01edbadb144cc9d53a", + "b-complete.json": "031b0ade26ce4766447da262fb7b6e1ef5723ed2c25be4d814bb2756789df3e6" + }, + "external_interventions": 0, + "admitted_move_receipts": 4, + "completion_receipts": 1, + "decision_rationale": "Inspected RGB generations 0 through 4. Visible orange block and blue pusher matched recognized public boxes and valid calibration. Forward targets -0.19, -0.08, 0.025, 0.125 with y=0.006 were selected successively from fresh RGB to approach and push toward the task region, each below the 0.12 m forward bound. First move had no contact; next three had contact. Final image shows block centered (0.1758333,0.0066667) inside x=[0.14,0.22], y=[-0.04,0.04] with margin. b-complete returned staged and inside_with_margin true with matching historical/current software artifact bytes. Declared completed with zero external interventions.", + "receipt_basis": { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-b/feedback-complete.json", + "phase": "staged", + "inside_with_margin": true, + "historical_software_ok": true, + "current_software_ok": true + }, + "blocked_reason": null + }, + { + "case_id": "ordinary-c", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c", + "episode_id": "d1d86c011138446fa7affdaf115de38c", + "case_sha256": "fa34684c4d313a2fb765c2ad1bed38f7cfc37d447436486ff87d88380c25fbb7", + "declared_outcome": "completed", + "finish_phase": "finished", + "finish_public_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/finish-public.json", + "explanation_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/explanation.txt", + "explanation_sha256": "2ebfa6fe651f5f25cd401aef195e75ebdcd97cf7bfd24bc0e3750e051d588740", + "inspected_image_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/vision-frame-000000.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/vision-frame-000001.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/vision-frame-000002.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/vision-frame-000003.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/vision-frame-000004.png", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/vision-frame-000005.png" + ], + "public_packet_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/view-0.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/view-1.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/view-2.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/view-3.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/view-4.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/view-5.json" + ], + "proposal_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/c-move-1.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/c-move-2.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/c-move-3.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/c-move-4.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/c-move-5.json", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/c-complete.json" + ], + "proposal_sha256": { + "c-move-1.json": "3886bea1183c4866526a87ff1015798af65d101d82909ce2052e5f560e66c292", + "c-move-2.json": "16dedc31d7e35642797900593037ab6feb97659a2a8a96faa7884ee2ec0d96e5", + "c-move-3.json": "b6624ecfb1d5c8a4d67068fc59c264715e6c20564f2a7250445bc08538dcdaa5", + "c-move-4.json": "68a3bc80d01f77011019957a3eabd5d2acee6c11d1068581fbaefe6e0c590b70", + "c-move-5.json": "6120d4e3c26569a7c4112b2ad4e99e165a8057df212d601e60e26d6cddf44be0", + "c-complete.json": "b2614163b7e1146565aa8669c35e64db2a41023bf0ca07c2d89e00c520dddb85" + }, + "external_interventions": 0, + "admitted_move_receipts": 5, + "completion_receipts": 1, + "decision_rationale": "Inspected RGB generations 0 through 5. Visible orange block and blue pusher matched recognized boxes under valid calibration. Selected forward targets -0.19, -0.08, 0.025, 0.13, 0.165 at y=-0.006 successively after fresh image inspection, within bounded step/workspace limits. Generation 4 block center 0.1808333 was too near region minimum for 0.005 m margin; selected final short advance. Generation 5 shows center (0.2158333,-0.0066667) safely in x=[0.18,0.26], y=[-0.04,0.04]. c-complete returned staged, inside_with_margin true and historical/current software byte checks ok. Declared completed on public evidence, with zero external interventions.", + "receipt_basis": { + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/ordinary-c/feedback-complete.json", + "phase": "staged", + "inside_with_margin": true, + "historical_software_ok": true, + "current_software_ok": true + }, + "blocked_reason": null + }, + { + "case_id": "low-light", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/low-light", + "episode_id": "d97892314d284652b8a38c00114b898b", + "case_sha256": "30321dd8243b1305de5dad17e6f4f9d1230187e6f8dd6845d82c56508e871d98", + "declared_outcome": "unresolved", + "finish_phase": "finished", + "finish_public_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/low-light/finish-public.json", + "explanation_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/low-light/explanation.txt", + "explanation_sha256": "3153a09e0e224a648efaffa08f4ad0a451435aefe647ef4b4b63f56480a277d0", + "inspected_image_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/low-light/vision-frame-000000.png" + ], + "public_packet_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/low-light/view-0.json" + ], + "proposal_paths": [], + "proposal_sha256": {}, + "external_interventions": 0, + "admitted_move_receipts": 0, + "completion_receipts": 0, + "decision_rationale": "Actually inspected acquired RGB generation 0. Frame is almost black, with only very faint silhouettes; part boundaries and location cannot be reliably established from the image. Public perception is unavailable with reason RGB block evidence is absent. Calibration is valid but recognition is unsupported, which cannot authorize movement or completion under EPISODES. No proposal submitted and no effect attempted. No permitted public command repairs lighting. Declare unresolved due to unavailable RGB evidence, preserving this attempted case with zero external interventions.", + "receipt_basis": null, + "blocked_reason": "RGB block evidence is absent" + }, + { + "case_id": "occlusion", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/occlusion", + "episode_id": "a0e0b348dc89436cbecb2cb89bc1a2bb", + "case_sha256": "934880ad3736e73cb761b64d3cfda109a7e40d08320e770fb3edcb50c90769da", + "declared_outcome": "unresolved", + "finish_phase": "finished", + "finish_public_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/occlusion/finish-public.json", + "explanation_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/occlusion/explanation.txt", + "explanation_sha256": "be729f9d6fdc892ca5d7539221dd19ce0e847959865a05133c9254a32abc7ed6", + "inspected_image_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/occlusion/vision-frame-000000.png" + ], + "public_packet_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/occlusion/view-0.json" + ], + "proposal_paths": [], + "proposal_sha256": {}, + "external_interventions": 0, + "admitted_move_receipts": 0, + "completion_receipts": 0, + "decision_rationale": "Actually inspected acquired RGB generation 0. Blue pusher and green task region are visible, but a dark occluder covers most of the orange block, leaving only a narrow orange strip. Public perception is ambiguous, reason RGB block silhouette is incomplete, off-frame or implausible. There is no trustworthy full block box/center; valid calibration alone is insufficient. EPISODES forbids effects or completion on unsupported recognition. No proposal or effect attempted; no allowed public command removes occlusion. Declare unresolved due to ambiguous RGB block evidence, retaining the attempted episode, zero external interventions.", + "receipt_basis": null, + "blocked_reason": "RGB block silhouette is incomplete, off-frame or implausible" + }, + { + "case_id": "camera-shift", + "run_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/camera-shift", + "episode_id": "63f90c15005c43f4b8af26d37031e053", + "case_sha256": "cbf8a3b2612965dd2246523ba630bd86760192f9f8b3a01d9be9d7af89fc72c6", + "declared_outcome": "unresolved", + "finish_phase": "finished", + "finish_public_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/camera-shift/finish-public.json", + "explanation_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/camera-shift/explanation.txt", + "explanation_sha256": "bb67f21ec78634e7b890728e9ced93bf6fe8c08820f4b756330af9cac489440e", + "inspected_image_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/camera-shift/vision-frame-000000.png" + ], + "public_packet_paths": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim3-qualification-v1/fresh-host/camera-shift/view-0.json" + ], + "proposal_paths": [], + "proposal_sha256": {}, + "external_interventions": 0, + "admitted_move_receipts": 0, + "completion_receipts": 0, + "decision_rationale": "Actually inspected acquired RGB generation 0. Orange part, blue pusher and green staging region are clearly visible and public perception recognized their pixel boxes. Calibration is invalid: camera extrinsic changed; calibration is unavailable. The packet supplies no world center estimates, so pixels cannot authorize a bounded world target or completion under EPISODES. No proposal/effect attempted. There is no permitted public calibration repair command. Declare unresolved due to unavailable valid calibration, preserving the attempted case with zero external interventions.", + "receipt_basis": null, + "blocked_reason": "camera extrinsic changed; calibration is unavailable" + } + ] + }, + "host_summary_sha256": "4c22bc15c7e5897c4b1bb5669b80fe3804875e35922f352b53ce07a17ab11470", + "post_trial_audit": { + "initial_notebook_read_only": true, + "no_case_native_source_evaluator_baseline_reads_reported": true, + "actual_RGB_views_before_explicit_targets_reported": true, + "serialization_helper_not_controller_reported": true, + "external_interventions": 0, + "source": "fresh host post-trial self-report" + }, + "first_use_deviation": { + "doctor_output_redirection_missing_directory": 1, + "self_corrected": true, + "doctor_ran_after_first_init_before_first_view": true, + "strict_doctor_before_init_not_demonstrated": true + } +} diff --git a/jevdual/results/repeatable-work-orders-20261010/report.md b/jevdual/results/repeatable-work-orders-20261010/report.md new file mode 100644 index 0000000..778dfdb --- /dev/null +++ b/jevdual/results/repeatable-work-orders-20261010/report.md @@ -0,0 +1,116 @@ +# SIM3: Repeatable visual work orders + +Both the deterministic RGB controller and a fresh image-capable host completed all three +ordinary work orders. Both left the three challenged jobs unresolved. This supports +adopting the bounded episode interface and keeping a conventional controller for movement; +the experiment found no AI advantage in movement completion. + +Protocol/case freeze: `5128f37`. Runtime and cold-host documentation freeze: `b9941ef`. +The six parent-selected cases were excluded from implementation development and run once +per participant. No case, runtime or detector settings changed during qualification. + +| Observed metric | Deterministic RGB | Fresh host | +|---|---:|---:| +| Attempted cases | 6/6 | 6/6 | +| Completed ordinary jobs | 3/3 | 3/3 | +| Completed across all cases | 3/6 | 3/6 | +| Unresolved challenged jobs | 3 | 3 | +| Case initialization / driver failures | 0 / 0 | 0 / 0 | +| Incorrect completion claims | 0 | 0 | +| Visual false positives observed | 0 | 0 | +| Declared external interventions | 0 | 0 | +| Executed movements | 11 | 13 | +| First-use setup issues | 0 | 1 | + +## What changed + +Valid rendered images persist before recognition. Packets distinguish acquired images +from recognized, ambiguous or unavailable perception. Case digests bind initial scene, +requested zone, calibration and an eight-action maximum, including completion. Initialization +sets a case's starting position once; motion continues through native MuJoCo contact. + +`episode_runner.py` adds doctor, initialization, deterministic baseline, participant finish +and parent-only evaluation commands. Finish records a declaration without scoring it. +Evaluation checks retained historical evidence, current native state and exact saved +software bytes. A satisfied scene alone cannot manufacture motion or software receipts. +Every completion attempt records its visual verdict before the native truth gate, so an +oracle refusal cannot erase an unsupported visual success. + +The comparator plans from current RGB-derived boxes and calibration, rather than native +poses or a recorded sequence. Both planners use the same observations, bounds and admitted +effects. The fresh host selected its own targets after inspecting actual images. + +## Case outcomes + +| Case | Deterministic result / elapsed | Fresh-host result / elapsed | +|---|---|---| +| ordinary-a | completed / 1.14 s | completed / 66.89 s | +| ordinary-b | completed / 1.35 s | completed / 49.85 s | +| ordinary-c | completed / 1.32 s | completed / 55.46 s | +| low-light | unresolved / 0.11 s | unresolved / 11.96 s | +| occlusion | unresolved / 0.11 s | unresolved / 12.45 s | +| camera-shift | unresolved / 0.11 s | unresolved / 10.83 s | + +Elapsed time runs from initialization to declared finish. The baseline used one process; +the host incurred model and fresh CLI round trips. These are descriptive observations, +not a controlled performance benchmark or hardware timing measurement. + +Ordinary jobs varied starting position and requested zone. Each completed through contact, +a fresh positive visual verdict, qualifying native state and a staged local JSON order +whose saved bytes Footwork checked. Independent scoring found no evidence errors. + +Low illumination yielded unavailable recognition. A static render-only occluder yielded +ambiguous block evidence. Camera displacement retained recognized pixel boxes but invalidated +world calibration. All three images remained inspectable; both participants attempted no +motion or computer effect and explained the unresolved jobs. None counts as completion. + +## Fresh-host use and retained failures + +One fresh Codex context handled all six cases, with within-set learning permitted. It +received public documentation, opaque case paths/digests and owned output paths, without +previous trajectories or baseline results. It inspected 19 images, made 13 movements and +staged three orders. Helpers serialized explicit decisions and parsed public packets; +they did not select targets. Separation relied on instructions on a shared filesystem. +The post-trial self-audit reported no reads of raw cases, native state, implementation, +evaluator or baseline data; this is not adversarial isolation. + +Its initial doctor-output redirection failed before doctor executed because the parent +output directory did not exist. First-case initialization created the directory; the host +then ran doctor successfully before viewing or acting, without a parent hint or episode +rerun. This is one retained first-use issue, separate from zero case initialization failures. +Strict doctor-before-init and installation on a clean machine were not demonstrated. + +Pre-freeze development found a frame-packet variable NameError. The corrected conformance +suite and retained failure summary are referenced in the notebook. Qualification cases were +not used to repair it. Existing unknown-outcome behavior still blocks replay; this package +does not add recovery for a disconnected or independently running controller. + +## Evidence and limits + +- [Comparison totals](comparison.json), [deterministic episodes](baseline-v1.json) and + [fresh-host scores, declarations and audit](fresh-host-v1.json) retain every attempted case. +- [Checks](checks.json) retain 50 passing native-engine/renderer tests, Ruff output and + frozen runtime/documentation hashes. [Case freeze](case-freeze.json) records input digests. +- Private native state, images, proposals and receipts remain under + `tools/physical-workcell/output/sim3-qualification-v1/` in the repository. The read-only + host scoring pass hashed all run files before and after and found no changes. + +Six cases and one host context cannot establish comparative reliability. No observed visual +false positive across these cases is not an accuracy estimate; separate conformance tests +exercise oracle-hidden false positives and incorrect declarations. The setup still uses +known colors, a fixed geometry, bounded forward pushes and the known block-center predicate. +It does not establish whole-part containment, inspection quality or learned recognition. + +This remains Python in Footwork's experiment area, backed by MuJoCo's native engine. +It uses the public Footwork artifact checker for a local JSON result. Physical operations +are not yet governed through Footwork's production capability service. Real camera control, +robot hardware and an application workflow are unqualified by SIM3. + +## Decision + +Keep the deterministic RGB controller for this bounded movement task. AI work-order +interpretation, exception handling and software coordination remain hypotheses, not measured +advantages here. Next, test an independently running controller with status and cancellation, +lost replies and changed controller incarnation. Then bind fulfillment to a versioned local +application order and verify its saved state. Promote an adapter into Footwork core only when +those lifecycle semantics have an actual capability-service caller. diff --git a/jevdual/results/reusable-bench-20261011/checks.json b/jevdual/results/reusable-bench-20261011/checks.json new file mode 100644 index 0000000..253df40 --- /dev/null +++ b/jevdual/results/reusable-bench-20261011/checks.json @@ -0,0 +1,72 @@ +{ + "source_commit": "0091e5f832a3aef24ef73fdc9f1ee76bd64b7e46", + "conformance": { + "passed": true, + "count": 14, + "elapsed_s": 4.266, + "checks": [ + { + "name": "test_corrupted_image_refuses_measurement_and_does_not_create_a_database", + "result": "ok" + }, + { + "name": "test_corrupted_native_state_never_causes_an_implicit_reset", + "result": "ok" + }, + { + "name": "test_corrupted_retained_model_refuses_without_recreating_the_body", + "result": "ok" + }, + { + "name": "test_corrupted_source_binding_refuses_without_a_new_generation", + "result": "ok" + }, + { + "name": "test_diagnostic_force_needs_explicit_enablement_and_finite_bounds", + "result": "ok" + }, + { + "name": "test_exact_motion_replay_retains_one_native_effect_and_conflicts_refuse", + "result": "ok" + }, + { + "name": "test_external_force_invalidates_stale_motion_and_measurement_before_dispatch", + "result": "ok" + }, + { + "name": "test_foreign_or_modified_observation_cannot_authorize_a_motion", + "result": "ok" + }, + { + "name": "test_measurement_column_corruption_is_not_hidden_by_an_unchanged_receipt", + "result": "ok" + }, + { + "name": "test_measurement_is_a_separate_process_with_exact_persisted_image_binding", + "result": "ok" + }, + { + "name": "test_measurement_receipt_corruption_refuses_historical_replay", + "result": "ok" + }, + { + "name": "test_measurement_replay_is_historical_and_does_not_run_another_worker", + "result": "ok" + }, + { + "name": "test_retained_interrupted_intent_blocks_all_new_dispatch", + "result": "ok" + }, + { + "name": "test_successive_job_gate_requires_measurement_and_verified_reset", + "result": "ok" + } + ], + "raw_log_path": "tools/physical-workcell/output/sim6-final-checks/unittest.log", + "raw_log_sha256": "e5ca1e7edd35ec49a416c78bc5f5cb1534e037d6f0f506221ee194f4326a58e9" + }, + "ruff": "passed", + "notebook": "passed", + "diff_check": "passed", + "redaction_files_changed": 0 +} diff --git a/jevdual/results/reusable-bench-20261011/cleanup.json b/jevdual/results/reusable-bench-20261011/cleanup.json new file mode 100644 index 0000000..8c3de30 --- /dev/null +++ b/jevdual/results/reusable-bench-20261011/cleanup.json @@ -0,0 +1,22 @@ +{ + "background_controller_started": false, + "host_process_exit_codes": [ + 0, + 0 + ], + "measurement_workers": [ + { + "job_id": "ordinary", + "worker_pid": 58696, + "exit_code": 0 + }, + { + "job_id": "disturbed", + "worker_pid": 58726, + "exit_code": 0 + } + ], + "all_started_subprocesses_waited": true, + "bench_idle_verified_in_fresh_cli": true, + "evidence_retained": true +} diff --git a/jevdual/results/reusable-bench-20261011/disturbed-score.json b/jevdual/results/reusable-bench-20261011/disturbed-score.json new file mode 100644 index 0000000..bf29597 --- /dev/null +++ b/jevdual/results/reusable-bench-20261011/disturbed-score.json @@ -0,0 +1,379 @@ +{ + "continuity": { + "after": { + "active_job": null, + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "diagnostic_enabled": true, + "generation": 70, + "jobs": { + "disturbed": { + "completed_generation": 70, + "completed_native_time_s": 107.93599999992985, + "job_id": "disturbed", + "measurement_operation_id": "disturbed-measurement", + "measurement_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 52, + "image_sha256": "4a3a1f6bed1d0140562fd77a0b71d21f95db426f05fa78b77eb692fda4e44de9", + "job_id": "disturbed", + "measurement": { + "block_box": [ + 340, + 155, + 382, + 197 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ], + "centroid_pixel_xy": [ + 360.0121880441091, + 175.5113174695299 + ], + "footprint_area_m2": 0.004786111111111111, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1723 + }, + "native_time_s": 80.51799999999375, + "operation_id": "disturbed-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c5ba99a999848efcde59f32b74af4fb92060efb468ecbe76c78767bbaadfe99a", + "version": 1, + "worker_pid": 58726 + }, + "moves": 40, + "phase": "completed", + "reset_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "block_home_verified": true, + "generation": 70, + "image_sha256": "502d9069172166fc632ef00f9983fb5c5115b043a0ef20ef7cb667b7ada67b40", + "job_id": "disturbed", + "kind": "finish_reset", + "native_time_s": 107.93599999992985, + "operation_id": "disturbed-reset", + "pusher_home_verified": true, + "reset_contact_samples": 6764, + "reset_native_steps": 13709, + "state_sha256": "0dbadbe16adc1b92eaf1474894ad4ab0548428609d59a3e570da804614ca423d" + }, + "reset_started_generation": 52, + "started_generation": 29, + "started_native_time_s": 45.39800000000952, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "ordinary": { + "completed_generation": 29, + "completed_native_time_s": 45.39800000000952, + "job_id": "ordinary", + "measurement_operation_id": "ordinary-measurement", + "measurement_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "measurement": { + "block_box": [ + 340, + 158, + 382, + 200 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ], + "centroid_pixel_xy": [ + 360.0243619489559, + 178.52088167053364 + ], + "footprint_area_m2": 0.0047888888888888885, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1724 + }, + "native_time_s": 17.980000000000913, + "operation_id": "ordinary-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef", + "version": 1, + "worker_pid": 58696 + }, + "moves": 29, + "phase": "completed", + "reset_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "block_home_verified": true, + "generation": 29, + "image_sha256": "8541989ea39a283f4a94c00dad6404c12083ec3518652a863b0aea9f1fec18b9", + "job_id": "ordinary", + "kind": "finish_reset", + "native_time_s": 45.39800000000952, + "operation_id": "ordinary-reset", + "pusher_home_verified": true, + "reset_contact_samples": 6757, + "reset_native_steps": 13709, + "state_sha256": "716fba0b934c91016341803c109073fafab195b147eb3670fa6cd2ab8f693833" + }, + "reset_started_generation": 11, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 107.93599999992985, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "before": { + "active_job": null, + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "diagnostic_enabled": true, + "generation": 29, + "jobs": { + "ordinary": { + "completed_generation": 29, + "completed_native_time_s": 45.39800000000952, + "job_id": "ordinary", + "measurement_operation_id": "ordinary-measurement", + "measurement_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "measurement": { + "block_box": [ + 340, + 158, + 382, + 200 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ], + "centroid_pixel_xy": [ + 360.0243619489559, + 178.52088167053364 + ], + "footprint_area_m2": 0.0047888888888888885, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1724 + }, + "native_time_s": 17.980000000000913, + "operation_id": "ordinary-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef", + "version": 1, + "worker_pid": 58696 + }, + "moves": 29, + "phase": "completed", + "reset_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "block_home_verified": true, + "generation": 29, + "image_sha256": "8541989ea39a283f4a94c00dad6404c12083ec3518652a863b0aea9f1fec18b9", + "job_id": "ordinary", + "kind": "finish_reset", + "native_time_s": 45.39800000000952, + "operation_id": "ordinary-reset", + "pusher_home_verified": true, + "reset_contact_samples": 6757, + "reset_native_steps": 13709, + "state_sha256": "716fba0b934c91016341803c109073fafab195b147eb3670fa6cd2ab8f693833" + }, + "reset_started_generation": 11, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 45.39800000000952, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + } + }, + "job_id": "disturbed", + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06769387394718734, + 0.006864712111564605, + 0.03499573045536974 + ], + "external_force_cleared": true, + "generation": 52, + "native_time_s": 80.51799999999375, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 2.7238600186678546e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "c5ba99a999848efcde59f32b74af4fb92060efb468ecbe76c78767bbaadfe99a" + }, + "motion_count": 40, + "native_motion_steps": 30689, + "passed": true, + "planner_role": "frozen_deterministic_color_RGB_host", + "recovery": { + "after_generation": 41, + "before_generation": 40, + "rgb_displacement_m": 0.13333333333333333, + "stale_refused": [ + "move", + "measure" + ] + }, + "reset_contact_samples": 6663, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11790274447954756, + 0.008162951649690725, + 0.034995730455369745 + ], + "external_force_cleared": true, + "generation": 70, + "native_time_s": 107.93599999992985, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 2.7238600186678546e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "0dbadbe16adc1b92eaf1474894ad4ab0548428609d59a3e570da804614ca423d" + }, + "sqlite": { + "count": 2, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 52, + "image_sha256": "4a3a1f6bed1d0140562fd77a0b71d21f95db426f05fa78b77eb692fda4e44de9", + "job_id": "disturbed", + "operation_id": "disturbed-measurement", + "receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 52, + "image_sha256": "4a3a1f6bed1d0140562fd77a0b71d21f95db426f05fa78b77eb692fda4e44de9", + "job_id": "disturbed", + "measurement": { + "block_box": [ + 340, + 155, + 382, + 197 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ], + "centroid_pixel_xy": [ + 360.0121880441091, + 175.5113174695299 + ], + "footprint_area_m2": 0.004786111111111111, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1723 + }, + "native_time_s": 80.51799999999375, + "operation_id": "disturbed-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c5ba99a999848efcde59f32b74af4fb92060efb468ecbe76c78767bbaadfe99a", + "version": 1, + "worker_pid": 58726 + }, + "state_sha256": "c5ba99a999848efcde59f32b74af4fb92060efb468ecbe76c78767bbaadfe99a" + }, + { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "operation_id": "ordinary-measurement", + "receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "measurement": { + "block_box": [ + 340, + 158, + 382, + 200 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ], + "centroid_pixel_xy": [ + 360.0243619489559, + 178.52088167053364 + ], + "footprint_area_m2": 0.0047888888888888885, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1724 + }, + "native_time_s": 17.980000000000913, + "operation_id": "ordinary-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef", + "version": 1, + "worker_pid": 58696 + }, + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef" + } + ] + } +} diff --git a/jevdual/results/reusable-bench-20261011/disturbed.png b/jevdual/results/reusable-bench-20261011/disturbed.png new file mode 100644 index 0000000..fb0fc4d Binary files /dev/null and b/jevdual/results/reusable-bench-20261011/disturbed.png differ diff --git a/jevdual/results/reusable-bench-20261011/frames.json b/jevdual/results/reusable-bench-20261011/frames.json new file mode 100644 index 0000000..87842e2 --- /dev/null +++ b/jevdual/results/reusable-bench-20261011/frames.json @@ -0,0 +1,816 @@ +[ + { + "job": "ordinary", + "generation": 0, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000000-06bd7f2dbe3bd2be.png", + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "rgb_block_xy": [ + -0.12, + 0.0 + ] + }, + { + "job": "ordinary", + "generation": 1, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000001-30fbc0a36b75bdfa.png", + "image_sha256": "f19c8850898f1bbfb79f6d4fe8724cfef401e13249e4d22ed351add263297567", + "rgb_block_xy": [ + -0.12, + 0.0 + ] + }, + { + "job": "ordinary", + "generation": 2, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000002-efb1219bec6b6113.png", + "image_sha256": "607499232ce734cc4da64b2efe4e2f6e2e5206f1cd323287588b38a49c9b531b", + "rgb_block_xy": [ + -0.06916666666666667, + 0.0 + ] + }, + { + "job": "ordinary", + "generation": 3, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000003-00f7e4f0eea3b611.png", + "image_sha256": "16cfb44f7f3e8100544be9f5e5be350c47ba9304267ea9cd11f9ce31619877a6", + "rgb_block_xy": [ + 0.016666666666666666, + 0.0 + ] + }, + { + "job": "ordinary", + "generation": 4, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000004-a88d5d7a2e045d5d.png", + "image_sha256": "797f84e48d5cce42f7a9336d70d468a5b81af79df2e77bc8a612f40ec4b6146c", + "rgb_block_xy": [ + 0.06666666666666667, + 0.0 + ] + }, + { + "job": "ordinary", + "generation": 5, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000005-a4778ca8afdbb83d.png", + "image_sha256": "0650fc6d562b1e06b4742a96194052ac9b8cafad894a1b40b6ce13a091f31790", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 6, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000006-2a5f0b33b6c4e3c8.png", + "image_sha256": "60cd3bd5c2575c1b40a8421d8d4c72507e6f7276281c5702e650e803caf520d1", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 7, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000007-555f34018372553c.png", + "image_sha256": "a97491eeb6bc704d87ac6f336a4f5e538763683c1b6f6c93808a2bf27662604b", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 8, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000008-bb489d76e963cbc1.png", + "image_sha256": "a021eaaed22fab98554c6fddfb50a9bcb288ddffa5fd3bbfc33f05cc179b9f5a", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 9, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000009-f07256f72f9915a4.png", + "image_sha256": "da6d4b2a65d22e2b78580638a84588cf5123a4a680f17ff34c2519a4e75c4926", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 10, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000010-0b2530ff4f75f930.png", + "image_sha256": "1b9d4add8c9bd4d38631e7976a414ae702820d8527e41993b172c092992d8639", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 11, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000011-2036ca40d713f438.png", + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 11, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000011-739139d81e2184d5.png", + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 12, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000012-433bece52239539a.png", + "image_sha256": "03cf8ba125406470564e6eed02faef19f9f3c82e0a90a67f6fd647c88e290c7d", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 13, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000013-40aaf089d6d5cee8.png", + "image_sha256": "da6d4b2a65d22e2b78580638a84588cf5123a4a680f17ff34c2519a4e75c4926", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 14, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000014-adf208b26ff0bf6c.png", + "image_sha256": "5b5b1cbeb670b2f88143d7858214b19a9086270cb4b04fae612c950701407001", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 15, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000015-c3c45ea02c8fdd51.png", + "image_sha256": "cfab87bf495bd7c27d64ebd2fdac7ff6e398fc2e5831413be8e0ccffa71aca57", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 16, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000016-7240c0d26977cec8.png", + "image_sha256": "4ddd3216a9eb4d56ba1834db452402eb24cc61a33519f876e77ecd820cbfa082", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 17, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000017-b1f524520b7575d5.png", + "image_sha256": "e80ff80f87c553bbdfa02906775795ccd5d5fa488eae639004308f36a9f35d42", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 18, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000018-ec5331ff64196736.png", + "image_sha256": "5350b6e41e31f601321915a36020f830496bcff7215f55b0560a046238b5bd72", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 19, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000019-3c567d3d406eead2.png", + "image_sha256": "657231f938f06043dd271c6b4021d1d3daff9fae939710d203bea29c40fb2f06", + "rgb_block_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 20, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000020-50e0b4cbe8e57f3d.png", + "image_sha256": "51b513444102708859d9bd6d90c663597f1e33ff91c3b89ed52cc0b7d863f368", + "rgb_block_xy": [ + -0.0075, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 21, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000021-9d69c38d2f08a75e.png", + "image_sha256": "8b0ad2b3dfcbf3f17d2ae73bf32e1a6403750c540b18d8b13d4ea1c94e46fa74", + "rgb_block_xy": [ + -0.09333333333333332, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 22, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000022-4b0363b952560fdf.png", + "image_sha256": "ac8e4fbca11ce2d968b6e713dbd79de4e3634d7af3a4de687a5e12b2a0140c16", + "rgb_block_xy": [ + -0.11666666666666665, + 0.0016666666666666666 + ] + }, + { + "job": "ordinary", + "generation": 23, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000023-e705832977801ee6.png", + "image_sha256": "c3070feb09a26394068db158583f088d965b830eddc3596295208ef1f71dd49f", + "rgb_block_xy": [ + -0.11833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "ordinary", + "generation": 24, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000024-d64998dcd881c151.png", + "image_sha256": "12120a17ecae93fd3dcfbd12c5973f75505c562e085bcf8c759983b00e623c9f", + "rgb_block_xy": [ + -0.11833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "ordinary", + "generation": 25, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000025-70efbaf4f4c1295b.png", + "image_sha256": "1d32b533cd710a71767e184b2f3778d6436734b3da56c62c83ffa612517d1d55", + "rgb_block_xy": [ + -0.11833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "ordinary", + "generation": 26, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000026-2681f7d655fb5cd9.png", + "image_sha256": "e72aaa93ee103b5a9c832b60319f66a60cd79350430e12394d1afac5926d4fd8", + "rgb_block_xy": [ + -0.11833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "ordinary", + "generation": 27, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000027-930259e07434b01d.png", + "image_sha256": "c631922bd41d53a2ee464c3159699244fe8f6ad8b498b8d135bebec14738db47", + "rgb_block_xy": [ + -0.11833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "ordinary", + "generation": 28, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000028-a777a546f8daaf58.png", + "image_sha256": "76dd16f818d93de4466541e2ec844528328f3fa2bf36f1c54667bb42cfc5613d", + "rgb_block_xy": [ + -0.11833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "ordinary", + "generation": 29, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000029-287882198cb6a568.png", + "image_sha256": "8541989ea39a283f4a94c00dad6404c12083ec3518652a863b0aea9f1fec18b9", + "rgb_block_xy": [ + -0.11833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 29, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000029-501600c40ffa9d0c.png", + "image_sha256": "8541989ea39a283f4a94c00dad6404c12083ec3518652a863b0aea9f1fec18b9", + "rgb_block_xy": [ + -0.11833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 30, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000030-0f96bb0f89fdcc9d.png", + "image_sha256": "94eafbcd7bc044a229799895f695dd6edc5c4dcb0660427c6c6db3b16a19d4f4", + "rgb_block_xy": [ + -0.11833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 31, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000031-1c9c9b5ad2e0e3f1.png", + "image_sha256": "cba782ece218cc172746794534d23047445079e7931fdcd928227542aa8f4b4c", + "rgb_block_xy": [ + -0.06833333333333333, + 0.0016666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 32, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000032-b59e1ab1afbcf81d.png", + "image_sha256": "a5a191b09de9c1d32f19edbebb6d529d395cef06f0eb65810816e11efd34bbeb", + "rgb_block_xy": [ + 0.017499999999999998, + 0.0016666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 33, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000033-36b1d94f25bbd325.png", + "image_sha256": "f6d017a563f47ffa33990f31ed9e3407700e2a7ad6e5632c597b26d3192574fa", + "rgb_block_xy": [ + 0.06666666666666667, + 0.0016666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 34, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000034-39cb9fbfe5a52493.png", + "image_sha256": "ae2a7791cca842c763cfb925ba225ca9ef33477106e0f3d6aec0e8f1acee1c00", + "rgb_block_xy": [ + 0.06833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 35, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000035-8cc7a1d56799bce1.png", + "image_sha256": "5223adda47471ac2562612281410ff3396147e7e85848e44d8f2b69d928989d2", + "rgb_block_xy": [ + 0.06833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 36, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000036-18382a237ba2db75.png", + "image_sha256": "1226116c3478aca946fec896fe1a196063627c3ffa0da3e4e3682d2fad49c011", + "rgb_block_xy": [ + 0.06833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 37, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000037-19a2674cc0238337.png", + "image_sha256": "ea5151cf0e988785f34a15a02ed461db9d7c3455715bc5fb536a6e92eb3b10ef", + "rgb_block_xy": [ + 0.06833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 38, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000038-c790198948e89617.png", + "image_sha256": "bb26cfe741f3a0aa63a9499b0ed12d94040cc12e11c6d741c058e1508c73a068", + "rgb_block_xy": [ + 0.06833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 39, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000039-95f5380378b3015c.png", + "image_sha256": "ce811e5ac5053f7a3d24f54d1974a5368dc3a585a591ceebb811fa5c8f09151c", + "rgb_block_xy": [ + 0.06833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 40, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000040-12feb76f804f3905.png", + "image_sha256": "6b882e01b655351f945ad7e0819eb40545803a3dcdeae5c1b5688475cc5f0cf7", + "rgb_block_xy": [ + 0.06833333333333333, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 41, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000041-12feb76f804f3905.png", + "image_sha256": "0eb29d55f0e6adbb1f6d8c146ce12ace779e202cad6ef9dbe27d1a7f3e263e11", + "rgb_block_xy": [ + -0.065, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 42, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000042-ad5fd4bf34953b81.png", + "image_sha256": "b26333302c61e7effe06418e6b4a99001683029ebd93f84097eccb56e193fdd4", + "rgb_block_xy": [ + -0.065, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 43, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000043-b7fa4cc4b8656c4c.png", + "image_sha256": "23d84ce93a234bae465e517f2bc7bdd0fb3f0932f4abfc4282fc963f9a974867", + "rgb_block_xy": [ + -0.065, + 0.003333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 44, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000044-b9d6c88c4c2ee242.png", + "image_sha256": "8c30a7499c1211a124511e0975cff592091cf119a5d268e3ae756e4244583540", + "rgb_block_xy": [ + 0.021666666666666664, + 0.005833333333333333 + ] + }, + { + "job": "disturbed", + "generation": 45, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000045-6739afa4617b2728.png", + "image_sha256": "1f9020149f3ae06d4924714ed55255139664cc88b8dbd2930d28da19161713dd", + "rgb_block_xy": [ + 0.06583333333333333, + 0.005833333333333333 + ] + }, + { + "job": "disturbed", + "generation": 46, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000046-c416190568c01c84.png", + "image_sha256": "bef433ddb2e49bf51c463f5e15e4ad3a8607a45031367ef1ed6e50b102d7a01f", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 47, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000047-c7c59c956a22767c.png", + "image_sha256": "4207d9cfbbd69a6056a2ba1c3fdfad0242ea4f5612c9ecab6f12e81d405403e7", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 48, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000048-007b5293052819d7.png", + "image_sha256": "25958b371397ae09b5db16a8096fd14b8a9dbc6328ca9d7ca01d80e5e0baddb7", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 49, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000049-84f47e85023670b0.png", + "image_sha256": "75716e1ce4cfe9c339c365621fbd180815e71e82226ba2625b2d74cb2906f10a", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 50, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000050-9f1d6a6991037984.png", + "image_sha256": "583f4b48ea7f4eb3aaa80d2a2647091874e9a2efe2e764ff41cc57d1caa23f1b", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 51, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000051-6947b83c2a1168ce.png", + "image_sha256": "2dedc20f86a88162febee4d3558954e4ba15c2880bc63dd23733cf8ee487fd3a", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 52, + "phase": "prepare", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000052-f36f14f20ec8a394.png", + "image_sha256": "4a3a1f6bed1d0140562fd77a0b71d21f95db426f05fa78b77eb692fda4e44de9", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 52, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000052-e09d3bb49a8cdb26.png", + "image_sha256": "4a3a1f6bed1d0140562fd77a0b71d21f95db426f05fa78b77eb692fda4e44de9", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 53, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000053-341ad57abdf5c712.png", + "image_sha256": "732442781cdeeb13e29b5abc96ef0e4eeec51b5eb948065c5a55f2feee6a37f0", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 54, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000054-4ca922f532bd2de4.png", + "image_sha256": "583f4b48ea7f4eb3aaa80d2a2647091874e9a2efe2e764ff41cc57d1caa23f1b", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 55, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000055-7c5ce8e1da419d8c.png", + "image_sha256": "32a954b258aba9b7174a326728620189dd1142bbe29326016f729ebe83c7fcdb", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 56, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000056-983326261a6980ec.png", + "image_sha256": "88df40cea202c24c9cb3ec3566ea6b4b6569dd16e1ee732f61d26113b6f632a4", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 57, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000057-e0b94e8d5cd07b27.png", + "image_sha256": "2d5803034688a3496fa732e606f353a6f60a47e4d6b9048f4d91dda9aeecaa64", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 58, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000058-9f2588d5ab096b2f.png", + "image_sha256": "86d7f60782324b6ca790e4c4364407ae9867e42ed7bc9e3225a8d810d16e5e21", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 59, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000059-adf77f852de53898.png", + "image_sha256": "f0742e5fb1a676ed0d54d5221e48dea24c32d56bc2348b56d62c1cefb8725930", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 60, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000060-11e0696ff8c5878e.png", + "image_sha256": "c8df37ff3fede29aca872075e12c4b814ce4ad70ff74b09839bc23987aebb5fb", + "rgb_block_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 61, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000061-ae660f641a88ecf5.png", + "image_sha256": "d26192b0557a96643992ed0078b1702d77964a2fe181bef52f4ce829ebcd6bd3", + "rgb_block_xy": [ + -0.008333333333333333, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 62, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000062-03897b2eccf26725.png", + "image_sha256": "b80a7cf7b0157535e5a915374e28523d4d1b9b59dd3a9115ba76b85d113b32f4", + "rgb_block_xy": [ + -0.09499999999999999, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 63, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000063-e985c24723298300.png", + "image_sha256": "c4442c8fff03db2a4f88c719b3210578905d3f390cfb8ea3e926af1ceef62786", + "rgb_block_xy": [ + -0.11666666666666665, + 0.006666666666666666 + ] + }, + { + "job": "disturbed", + "generation": 64, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000064-b40c47852c9d4dbe.png", + "image_sha256": "9ca73ad9c28b9f315e483733c8cc6bff7de90011e88b4aea28b26acc27d823ec", + "rgb_block_xy": [ + -0.11833333333333333, + 0.008333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 65, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000065-27c5bce78d40e889.png", + "image_sha256": "d0e0753954f101c93383f791183531dc21c693fcef2549105a411d5a58a68b8f", + "rgb_block_xy": [ + -0.11833333333333333, + 0.008333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 66, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000066-b9dc4e82191a348e.png", + "image_sha256": "7f264612e0349b0602cb15295fdd36a6ff24045552ff009f15db5a68c1b1ccd0", + "rgb_block_xy": [ + -0.11833333333333333, + 0.008333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 67, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000067-9b79ad9bb4417c1f.png", + "image_sha256": "eef7c9c849c60f39b433499b0fd318c1be3701cb0759c462a42bbde7ea3688c9", + "rgb_block_xy": [ + -0.11833333333333333, + 0.008333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 68, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000068-7417495961f6da42.png", + "image_sha256": "30cc27cd5539b3af2e2cbc96ee59658e56fd968239e0edc7998fe80f7a84f68f", + "rgb_block_xy": [ + -0.11833333333333333, + 0.008333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 69, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000069-05e669438b6c79e1.png", + "image_sha256": "d97413424bef2b40359c564dff587baedc7b5372dbdc9e1636be64af366f9dce", + "rgb_block_xy": [ + -0.11833333333333333, + 0.008333333333333333 + ] + }, + { + "job": "disturbed", + "generation": 70, + "phase": "resetting", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim6-qualified-20261011-v1/bench/images/000070-1328c34380b1fb45.png", + "image_sha256": "502d9069172166fc632ef00f9983fb5c5115b043a0ef20ef7cb667b7ada67b40", + "rgb_block_xy": [ + -0.11833333333333333, + 0.008333333333333333 + ] + } +] diff --git a/jevdual/results/reusable-bench-20261011/fresh-cli-status.json b/jevdual/results/reusable-bench-20261011/fresh-cli-status.json new file mode 100644 index 0000000..dffa932 --- /dev/null +++ b/jevdual/results/reusable-bench-20261011/fresh-cli-status.json @@ -0,0 +1 @@ +{"active_job": null, "bench_id": "92d5adce82a146aeb8df71651fa03959", "diagnostic_enabled": true, "generation": 70, "jobs": {"disturbed": {"completed_generation": 70, "completed_native_time_s": 107.93599999992985, "job_id": "disturbed", "measurement_operation_id": "disturbed-measurement", "measurement_receipt": {"bench_id": "92d5adce82a146aeb8df71651fa03959", "generation": 52, "image_sha256": "4a3a1f6bed1d0140562fd77a0b71d21f95db426f05fa78b77eb692fda4e44de9", "job_id": "disturbed", "measurement": {"block_box": [340, 155, 382, 197], "block_center_xy": [0.06833333333333333, 0.006666666666666666], "centroid_pixel_xy": [360.0121880441091, 175.5113174695299], "footprint_area_m2": 0.004786111111111111, "method": "simulated_orange_RGB_footprint", "orange_pixel_count": 1723}, "native_time_s": 80.51799999999375, "operation_id": "disturbed-measurement", "source_kind": "mujoco_simulation", "state_sha256": "c5ba99a999848efcde59f32b74af4fb92060efb468ecbe76c78767bbaadfe99a", "version": 1, "worker_pid": 58726}, "moves": 40, "phase": "completed", "reset_receipt": {"bench_id": "92d5adce82a146aeb8df71651fa03959", "block_home_verified": true, "generation": 70, "image_sha256": "502d9069172166fc632ef00f9983fb5c5115b043a0ef20ef7cb667b7ada67b40", "job_id": "disturbed", "kind": "finish_reset", "native_time_s": 107.93599999992985, "operation_id": "disturbed-reset", "pusher_home_verified": true, "reset_contact_samples": 6764, "reset_native_steps": 13709, "state_sha256": "0dbadbe16adc1b92eaf1474894ad4ab0548428609d59a3e570da804614ca423d"}, "reset_started_generation": 52, "started_generation": 29, "started_native_time_s": 45.39800000000952, "target_zone": {"x_max": 0.105, "x_min": 0.035, "y_max": 0.025, "y_min": -0.025}}, "ordinary": {"completed_generation": 29, "completed_native_time_s": 45.39800000000952, "job_id": "ordinary", "measurement_operation_id": "ordinary-measurement", "measurement_receipt": {"bench_id": "92d5adce82a146aeb8df71651fa03959", "generation": 11, "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", "job_id": "ordinary", "measurement": {"block_box": [340, 158, 382, 200], "block_center_xy": [0.06833333333333333, 0.0016666666666666666], "centroid_pixel_xy": [360.0243619489559, 178.52088167053364], "footprint_area_m2": 0.0047888888888888885, "method": "simulated_orange_RGB_footprint", "orange_pixel_count": 1724}, "native_time_s": 17.980000000000913, "operation_id": "ordinary-measurement", "source_kind": "mujoco_simulation", "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef", "version": 1, "worker_pid": 58696}, "moves": 29, "phase": "completed", "reset_receipt": {"bench_id": "92d5adce82a146aeb8df71651fa03959", "block_home_verified": true, "generation": 29, "image_sha256": "8541989ea39a283f4a94c00dad6404c12083ec3518652a863b0aea9f1fec18b9", "job_id": "ordinary", "kind": "finish_reset", "native_time_s": 45.39800000000952, "operation_id": "ordinary-reset", "pusher_home_verified": true, "reset_contact_samples": 6757, "reset_native_steps": 13709, "state_sha256": "716fba0b934c91016341803c109073fafab195b147eb3670fa6cd2ab8f693833"}, "reset_started_generation": 11, "started_generation": 0, "started_native_time_s": 1.0000000000000007, "target_zone": {"x_max": 0.105, "x_min": 0.035, "y_max": 0.025, "y_min": -0.025}}}, "native_time_s": 107.93599999992985, "source_fingerprints": {"bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", "mujoco_version": "3.15.0", "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", "state_spec": 16383, "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961"}, "source_kind": "mujoco_simulation", "state": "ready", "unresolved_effect": null, "version": 1} diff --git a/jevdual/results/reusable-bench-20261011/measurement.png b/jevdual/results/reusable-bench-20261011/measurement.png new file mode 100644 index 0000000..4b2f789 Binary files /dev/null and b/jevdual/results/reusable-bench-20261011/measurement.png differ diff --git a/jevdual/results/reusable-bench-20261011/observations.gif b/jevdual/results/reusable-bench-20261011/observations.gif new file mode 100644 index 0000000..29a0eb0 Binary files /dev/null and b/jevdual/results/reusable-bench-20261011/observations.gif differ diff --git a/jevdual/results/reusable-bench-20261011/ordinary-score.json b/jevdual/results/reusable-bench-20261011/ordinary-score.json new file mode 100644 index 0000000..de0b7f6 --- /dev/null +++ b/jevdual/results/reusable-bench-20261011/ordinary-score.json @@ -0,0 +1,207 @@ +{ + "continuity": { + "after": { + "active_job": null, + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "diagnostic_enabled": true, + "generation": 29, + "jobs": { + "ordinary": { + "completed_generation": 29, + "completed_native_time_s": 45.39800000000952, + "job_id": "ordinary", + "measurement_operation_id": "ordinary-measurement", + "measurement_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "measurement": { + "block_box": [ + 340, + 158, + 382, + 200 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ], + "centroid_pixel_xy": [ + 360.0243619489559, + 178.52088167053364 + ], + "footprint_area_m2": 0.0047888888888888885, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1724 + }, + "native_time_s": 17.980000000000913, + "operation_id": "ordinary-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef", + "version": 1, + "worker_pid": 58696 + }, + "moves": 29, + "phase": "completed", + "reset_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "block_home_verified": true, + "generation": 29, + "image_sha256": "8541989ea39a283f4a94c00dad6404c12083ec3518652a863b0aea9f1fec18b9", + "job_id": "ordinary", + "kind": "finish_reset", + "native_time_s": 45.39800000000952, + "operation_id": "ordinary-reset", + "pusher_home_verified": true, + "reset_contact_samples": 6757, + "reset_native_steps": 13709, + "state_sha256": "716fba0b934c91016341803c109073fafab195b147eb3670fa6cd2ab8f693833" + }, + "reset_started_generation": 11, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 45.39800000000952, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "before": { + "active_job": null, + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + } + }, + "job_id": "ordinary", + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06765083286533137, + 0.0012302134613647513, + 0.03499573045536985 + ], + "external_force_cleared": true, + "generation": 11, + "native_time_s": 17.980000000000913, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 2.7238600186678546e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef" + }, + "motion_count": 29, + "native_motion_steps": 22199, + "passed": true, + "planner_role": "frozen_deterministic_color_RGB_host", + "recovery": null, + "reset_contact_samples": 6619, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.117918137735966, + 0.002774860913929776, + 0.03499573045536985 + ], + "external_force_cleared": true, + "generation": 29, + "native_time_s": 45.39800000000952, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 2.7238600186678546e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "716fba0b934c91016341803c109073fafab195b147eb3670fa6cd2ab8f693833" + }, + "sqlite": { + "count": 1, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "operation_id": "ordinary-measurement", + "receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "measurement": { + "block_box": [ + 340, + 158, + 382, + 200 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ], + "centroid_pixel_xy": [ + 360.0243619489559, + 178.52088167053364 + ], + "footprint_area_m2": 0.0047888888888888885, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1724 + }, + "native_time_s": 17.980000000000913, + "operation_id": "ordinary-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef", + "version": 1, + "worker_pid": 58696 + }, + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef" + } + ] + } +} diff --git a/jevdual/results/reusable-bench-20261011/qualification.json b/jevdual/results/reusable-bench-20261011/qualification.json new file mode 100644 index 0000000..36cfad6 --- /dev/null +++ b/jevdual/results/reusable-bench-20261011/qualification.json @@ -0,0 +1,727 @@ +{ + "attempts": [ + { + "all_caller_and_worker_processes_synchronous": true, + "attempt": 1, + "before": { + "active_job": null, + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "elapsed_s": 3.7799429579172283, + "job_id": "ordinary", + "returncode": 0 + }, + { + "all_caller_and_worker_processes_synchronous": true, + "attempt": 1, + "before": { + "active_job": null, + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "diagnostic_enabled": true, + "generation": 29, + "jobs": { + "ordinary": { + "completed_generation": 29, + "completed_native_time_s": 45.39800000000952, + "job_id": "ordinary", + "measurement_operation_id": "ordinary-measurement", + "measurement_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "measurement": { + "block_box": [ + 340, + 158, + 382, + 200 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ], + "centroid_pixel_xy": [ + 360.0243619489559, + 178.52088167053364 + ], + "footprint_area_m2": 0.0047888888888888885, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1724 + }, + "native_time_s": 17.980000000000913, + "operation_id": "ordinary-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef", + "version": 1, + "worker_pid": 58696 + }, + "moves": 29, + "phase": "completed", + "reset_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "block_home_verified": true, + "generation": 29, + "image_sha256": "8541989ea39a283f4a94c00dad6404c12083ec3518652a863b0aea9f1fec18b9", + "job_id": "ordinary", + "kind": "finish_reset", + "native_time_s": 45.39800000000952, + "operation_id": "ordinary-reset", + "pusher_home_verified": true, + "reset_contact_samples": 6757, + "reset_native_steps": 13709, + "state_sha256": "716fba0b934c91016341803c109073fafab195b147eb3670fa6cd2ab8f693833" + }, + "reset_started_generation": 11, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 45.39800000000952, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "elapsed_s": 6.2080469590146095, + "job_id": "disturbed", + "returncode": 0 + } + ], + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "limits": "deterministic RGB policy; axial force disturbance; simulation; no hardware or model trial", + "passed": true, + "required_cycles": 2, + "scores": [ + { + "continuity": { + "after": { + "active_job": null, + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "diagnostic_enabled": true, + "generation": 29, + "jobs": { + "ordinary": { + "completed_generation": 29, + "completed_native_time_s": 45.39800000000952, + "job_id": "ordinary", + "measurement_operation_id": "ordinary-measurement", + "measurement_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "measurement": { + "block_box": [ + 340, + 158, + 382, + 200 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ], + "centroid_pixel_xy": [ + 360.0243619489559, + 178.52088167053364 + ], + "footprint_area_m2": 0.0047888888888888885, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1724 + }, + "native_time_s": 17.980000000000913, + "operation_id": "ordinary-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef", + "version": 1, + "worker_pid": 58696 + }, + "moves": 29, + "phase": "completed", + "reset_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "block_home_verified": true, + "generation": 29, + "image_sha256": "8541989ea39a283f4a94c00dad6404c12083ec3518652a863b0aea9f1fec18b9", + "job_id": "ordinary", + "kind": "finish_reset", + "native_time_s": 45.39800000000952, + "operation_id": "ordinary-reset", + "pusher_home_verified": true, + "reset_contact_samples": 6757, + "reset_native_steps": 13709, + "state_sha256": "716fba0b934c91016341803c109073fafab195b147eb3670fa6cd2ab8f693833" + }, + "reset_started_generation": 11, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 45.39800000000952, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "before": { + "active_job": null, + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + } + }, + "job_id": "ordinary", + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06765083286533137, + 0.0012302134613647513, + 0.03499573045536985 + ], + "external_force_cleared": true, + "generation": 11, + "native_time_s": 17.980000000000913, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 2.7238600186678546e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef" + }, + "motion_count": 29, + "native_motion_steps": 22199, + "passed": true, + "planner_role": "frozen_deterministic_color_RGB_host", + "recovery": null, + "reset_contact_samples": 6619, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.117918137735966, + 0.002774860913929776, + 0.03499573045536985 + ], + "external_force_cleared": true, + "generation": 29, + "native_time_s": 45.39800000000952, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 2.7238600186678546e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "716fba0b934c91016341803c109073fafab195b147eb3670fa6cd2ab8f693833" + }, + "sqlite": { + "count": 1, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "operation_id": "ordinary-measurement", + "receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "measurement": { + "block_box": [ + 340, + 158, + 382, + 200 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ], + "centroid_pixel_xy": [ + 360.0243619489559, + 178.52088167053364 + ], + "footprint_area_m2": 0.0047888888888888885, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1724 + }, + "native_time_s": 17.980000000000913, + "operation_id": "ordinary-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef", + "version": 1, + "worker_pid": 58696 + }, + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef" + } + ] + } + }, + { + "continuity": { + "after": { + "active_job": null, + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "diagnostic_enabled": true, + "generation": 70, + "jobs": { + "disturbed": { + "completed_generation": 70, + "completed_native_time_s": 107.93599999992985, + "job_id": "disturbed", + "measurement_operation_id": "disturbed-measurement", + "measurement_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 52, + "image_sha256": "4a3a1f6bed1d0140562fd77a0b71d21f95db426f05fa78b77eb692fda4e44de9", + "job_id": "disturbed", + "measurement": { + "block_box": [ + 340, + 155, + 382, + 197 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ], + "centroid_pixel_xy": [ + 360.0121880441091, + 175.5113174695299 + ], + "footprint_area_m2": 0.004786111111111111, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1723 + }, + "native_time_s": 80.51799999999375, + "operation_id": "disturbed-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c5ba99a999848efcde59f32b74af4fb92060efb468ecbe76c78767bbaadfe99a", + "version": 1, + "worker_pid": 58726 + }, + "moves": 40, + "phase": "completed", + "reset_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "block_home_verified": true, + "generation": 70, + "image_sha256": "502d9069172166fc632ef00f9983fb5c5115b043a0ef20ef7cb667b7ada67b40", + "job_id": "disturbed", + "kind": "finish_reset", + "native_time_s": 107.93599999992985, + "operation_id": "disturbed-reset", + "pusher_home_verified": true, + "reset_contact_samples": 6764, + "reset_native_steps": 13709, + "state_sha256": "0dbadbe16adc1b92eaf1474894ad4ab0548428609d59a3e570da804614ca423d" + }, + "reset_started_generation": 52, + "started_generation": 29, + "started_native_time_s": 45.39800000000952, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "ordinary": { + "completed_generation": 29, + "completed_native_time_s": 45.39800000000952, + "job_id": "ordinary", + "measurement_operation_id": "ordinary-measurement", + "measurement_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "measurement": { + "block_box": [ + 340, + 158, + 382, + 200 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ], + "centroid_pixel_xy": [ + 360.0243619489559, + 178.52088167053364 + ], + "footprint_area_m2": 0.0047888888888888885, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1724 + }, + "native_time_s": 17.980000000000913, + "operation_id": "ordinary-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef", + "version": 1, + "worker_pid": 58696 + }, + "moves": 29, + "phase": "completed", + "reset_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "block_home_verified": true, + "generation": 29, + "image_sha256": "8541989ea39a283f4a94c00dad6404c12083ec3518652a863b0aea9f1fec18b9", + "job_id": "ordinary", + "kind": "finish_reset", + "native_time_s": 45.39800000000952, + "operation_id": "ordinary-reset", + "pusher_home_verified": true, + "reset_contact_samples": 6757, + "reset_native_steps": 13709, + "state_sha256": "716fba0b934c91016341803c109073fafab195b147eb3670fa6cd2ab8f693833" + }, + "reset_started_generation": 11, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 107.93599999992985, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "before": { + "active_job": null, + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "diagnostic_enabled": true, + "generation": 29, + "jobs": { + "ordinary": { + "completed_generation": 29, + "completed_native_time_s": 45.39800000000952, + "job_id": "ordinary", + "measurement_operation_id": "ordinary-measurement", + "measurement_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "measurement": { + "block_box": [ + 340, + 158, + 382, + 200 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ], + "centroid_pixel_xy": [ + 360.0243619489559, + 178.52088167053364 + ], + "footprint_area_m2": 0.0047888888888888885, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1724 + }, + "native_time_s": 17.980000000000913, + "operation_id": "ordinary-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef", + "version": 1, + "worker_pid": 58696 + }, + "moves": 29, + "phase": "completed", + "reset_receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "block_home_verified": true, + "generation": 29, + "image_sha256": "8541989ea39a283f4a94c00dad6404c12083ec3518652a863b0aea9f1fec18b9", + "job_id": "ordinary", + "kind": "finish_reset", + "native_time_s": 45.39800000000952, + "operation_id": "ordinary-reset", + "pusher_home_verified": true, + "reset_contact_samples": 6757, + "reset_native_steps": 13709, + "state_sha256": "716fba0b934c91016341803c109073fafab195b147eb3670fa6cd2ab8f693833" + }, + "reset_started_generation": 11, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 45.39800000000952, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + } + }, + "job_id": "disturbed", + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06769387394718734, + 0.006864712111564605, + 0.03499573045536974 + ], + "external_force_cleared": true, + "generation": 52, + "native_time_s": 80.51799999999375, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 2.7238600186678546e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "c5ba99a999848efcde59f32b74af4fb92060efb468ecbe76c78767bbaadfe99a" + }, + "motion_count": 40, + "native_motion_steps": 30689, + "passed": true, + "planner_role": "frozen_deterministic_color_RGB_host", + "recovery": { + "after_generation": 41, + "before_generation": 40, + "rgb_displacement_m": 0.13333333333333333, + "stale_refused": [ + "move", + "measure" + ] + }, + "reset_contact_samples": 6663, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11790274447954756, + 0.008162951649690725, + 0.034995730455369745 + ], + "external_force_cleared": true, + "generation": 70, + "native_time_s": 107.93599999992985, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 2.7238600186678546e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "0dbadbe16adc1b92eaf1474894ad4ab0548428609d59a3e570da804614ca423d" + }, + "sqlite": { + "count": 2, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 52, + "image_sha256": "4a3a1f6bed1d0140562fd77a0b71d21f95db426f05fa78b77eb692fda4e44de9", + "job_id": "disturbed", + "operation_id": "disturbed-measurement", + "receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 52, + "image_sha256": "4a3a1f6bed1d0140562fd77a0b71d21f95db426f05fa78b77eb692fda4e44de9", + "job_id": "disturbed", + "measurement": { + "block_box": [ + 340, + 155, + 382, + 197 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.006666666666666666 + ], + "centroid_pixel_xy": [ + 360.0121880441091, + 175.5113174695299 + ], + "footprint_area_m2": 0.004786111111111111, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1723 + }, + "native_time_s": 80.51799999999375, + "operation_id": "disturbed-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c5ba99a999848efcde59f32b74af4fb92060efb468ecbe76c78767bbaadfe99a", + "version": 1, + "worker_pid": 58726 + }, + "state_sha256": "c5ba99a999848efcde59f32b74af4fb92060efb468ecbe76c78767bbaadfe99a" + }, + { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "operation_id": "ordinary-measurement", + "receipt": { + "bench_id": "92d5adce82a146aeb8df71651fa03959", + "generation": 11, + "image_sha256": "21fe1a646ee0bf63a025ef29db8bf162ba5add5a1945981de7aca61ee448de16", + "job_id": "ordinary", + "measurement": { + "block_box": [ + 340, + 158, + 382, + 200 + ], + "block_center_xy": [ + 0.06833333333333333, + 0.0016666666666666666 + ], + "centroid_pixel_xy": [ + 360.0243619489559, + 178.52088167053364 + ], + "footprint_area_m2": 0.0047888888888888885, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1724 + }, + "native_time_s": 17.980000000000913, + "operation_id": "ordinary-measurement", + "source_kind": "mujoco_simulation", + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef", + "version": 1, + "worker_pid": 58696 + }, + "state_sha256": "c60f9a77a055cbf592c8e6e7180701e833e78c31afc648f53808e0e4f38fe2ef" + } + ] + } + } + ], + "source_commit": "0091e5f832a3aef24ef73fdc9f1ee76bd64b7e46", + "source_unchanged": true, + "successful_cycles": 2, + "version": 1 +} diff --git a/jevdual/results/reusable-bench-20261011/report.md b/jevdual/results/reusable-bench-20261011/report.md new file mode 100644 index 0000000..18b231f --- /dev/null +++ b/jevdual/results/reusable-bench-20261011/report.md @@ -0,0 +1,62 @@ +# Successive experiments on one simulated bench + +Frozen source `0091e5f832a3aef24ef73fdc9f1ee76bd64b7e46` completed both preregistered cycles on their first qualification attempts. Each used RGB-guided preparation, a separate computer measurement process, and contact-driven reset. The second recovered after an external force displaced the block by 0.1333 m along x. + +## What ran + +The actual [bench CLI and host](../../../tools/physical-workcell/BENCH.md) ran with pinned MuJoCo 3.15.0 in its existing environment. The host chose motion from exact rendered PNGs and public task data. No native pose guided it. Separate native-state scoring inspected historical measurement and reset states; read-only SQLite scoring checked saved computer results. + +From a checkout at the named source commit: + +```sh +tools/physical-workcell/.venv/bin/python tools/physical-workcell/qualify_bench.py \ + --output tools/physical-workcell/output/sim6-qualified-20261011-v1 \ + --source-commit 0091e5f832a3aef24ef73fdc9f1ee76bd64b7e46 +``` + +Use a new output directory for another attempt. Both host processes exited with code 0. The source matched the named commit before and after the run. [Source hashes](source-freeze.json), [qualification](qualification.json), [fresh-process status](fresh-cli-status.json), and [cleanup](cleanup.json) are retained. Full private journals and integration states remain in `tools/physical-workcell/output/sim6-qualified-20261011-v1/`. + +## Outcomes + +| Job | Motion actions | Measurement generation | Orange pixels | Reset generation | Reset contact samples | +|---|---:|---:|---:|---:|---:| +| Ordinary | 29 | 11 | 1,724 | 29 | 6,619 | +| Disturbed | 40 | 52 | 1,723 | 70 | 6,663 | + +Reset contact counts above come from motions labeled `contact_reset`, separately scored from the host journal. The controller's reset receipt also counts contacts from other reset-phase parking motions. All motion actuator-force maxima stayed within 4 N per axis. + +One bench ID, `92d5adce82a146aeb8df71651fa03959`, spanned both jobs. Native time advanced from 1.000 s to 45.398 s to 107.936 s. Job two began at the exact generation/time left by job one's reset. Integration state was restored between calls and never reinitialized between jobs. + +Separate native scoring found block x=0.06765 m at the ordinary measurement and x=0.06769 m at the recovered measurement. Both reset states placed the block within its home region and the pusher at x=-0.30 m, y approximately 0. + +The measurement worker computed apparent orange footprint areas of approximately 0.004789 and 0.004786 square meters from the simulated orthographic camera. SQLite contained exactly one row per job. Repeating each measurement operation returned its identical historical receipt without another worker or result. + +## Recovery and guards + +The second job reached preparation generation 40 before a -1.5 N diagnostic force was applied for 80 native steps, followed by bounded settling. Generation 41 retained the changed state with the force cleared. The public RGB center changed by 0.1333 m. Both stale-image effect requests refused, leaving durable public state unchanged. Fresh-image recovery motions established placement before measurement at generation 52. + +| Required criterion | Evidence kind | Evidence | Result | +|---|---|---|---| +| One body across successive jobs | Historical continuity | Both score files and fresh CLI status | Passed | +| Preparation before measurement | Historical state | Separate native measurement-state scores | Passed | +| Exact computer result persisted | Current saved state | Read-only SQLite scores and receipt equality | Passed | +| Stale disturbance evidence rejected | Historical effects | Disturbed journal and recovery score | Passed | +| Fresh-image contact recovery | Historical motion | Recovery-purpose native receipts and placement | Passed | +| Contact reset before next job | Historical and current state | Contact steps, reset scores, next-job start | Passed | +| Final bench idle, evidence retained | Current state | Fresh CLI status and cleanup | Passed | + +Fourteen final-source conformance tests passed in 4.266 seconds: replay/conflict semantics, job phases, measurement readback, stale evidence, corrupt source/state/image/model/measurement/SQL bindings, and interrupted-intent refusal. See [checks.json](checks.json); the original test log remains in `tools/physical-workcell/output/sim6-final-checks/unittest.log`. Ruff, the notebook checker and diff checks also passed. + +## Recorded observations + +The replay contains recorded observation frames, not interpolated trajectories or continuous camera video. Orange is the block, blue is the pusher, and green is the measurement region. The displacement occurs during the second cycle. Exact frame digests and RGB positions are in [frames.json](frames.json). + +![Recorded RGB observations across ordinary and disturbed cycles](observations.gif) + +## Limits and next boundary + +This verifies a deterministic color-specific RGB policy on one simulated axial disturbance and two successive jobs. It does not verify language-model planning, arbitrary objects, non-axial recovery, continuous wall-clock physics, hardware, power-loss recovery or a production Footwork capability adapter. Unknown interrupted effects block dispatch and are not automatically repaired. Physical placement and computer persistence are separate operations. + +The next extension is a host-facing prepare-measure-reset task with different measurement requests and the same bench ownership and evidence contract. A model can propose a bounded next action through the image interface; the controller continues owning freshness, limits and verification. Instrument and real actuator adapters remain future work. + +Development evidence remains under `output/sim6-backend-probe1/`, `output/sim6-root-dev1/`, and the conformance fixture directories. Guard refinements preceded the freeze. No physics/controller settings were tuned against this qualification, and no qualification attempts were retried. diff --git a/jevdual/results/reusable-bench-20261011/reset.png b/jevdual/results/reusable-bench-20261011/reset.png new file mode 100644 index 0000000..8c14be8 Binary files /dev/null and b/jevdual/results/reusable-bench-20261011/reset.png differ diff --git a/jevdual/results/reusable-bench-20261011/source-freeze.json b/jevdual/results/reusable-bench-20261011/source-freeze.json new file mode 100644 index 0000000..a8879ae --- /dev/null +++ b/jevdual/results/reusable-bench-20261011/source-freeze.json @@ -0,0 +1,15 @@ +{ + "commit": "0091e5f832a3aef24ef73fdc9f1ee76bd64b7e46", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/bench_runner.py": "cfbf6b638879e8a3498a84171d47804dc511a9e7af6cf9ae8f48ebc564d7b4eb", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/reusable-bench-20261011/start.png b/jevdual/results/reusable-bench-20261011/start.png new file mode 100644 index 0000000..b9b92ea Binary files /dev/null and b/jevdual/results/reusable-bench-20261011/start.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/attempt-ledger.json b/jevdual/results/reusable-body-recovery-20261012/attempt-ledger.json new file mode 100644 index 0000000..ac77355 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/attempt-ledger.json @@ -0,0 +1,20 @@ +[ + { + "root": "sim14-publication-v1", + "source_commit": "eed92cc10c9081f22cf5560031fd145254ca4af9", + "qualification": "historical passing predecessor", + "decisions": 13, + "commands": 40, + "reason": "Final guardian inventory and offline UI changes required a new measured source." + }, + { + "root": "sim14-planning-v1", + "source_commit": "eed92cc10c9081f22cf5560031fd145254ca4af9", + "qualification": "unqualified capture race", + "reservations": 2, + "decisions": 1, + "interrupted": 1, + "commands": 5, + "reason": "Running UI capture and later API state raced a lost dispatch reply; original captures preserved. Exact receipt recovered read-only; stopped without job completion." + } +] diff --git a/jevdual/results/reusable-body-recovery-20261012/checks/body-contract-conformance.json b/jevdual/results/reusable-body-recovery-20261012/checks/body-contract-conformance.json new file mode 100644 index 0000000..cec8d32 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/checks/body-contract-conformance.json @@ -0,0 +1,222 @@ +{ + "log": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-body-contract-conformance-v2.log", + "tests_passed": 43, + "seconds": 63.611, + "case_roots": [ + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-body-contract-ae2bc20b52e4", + "test_id": "test_body_contract.BodyContractTests.test_alternate_command_recovers_after_reply_loss_and_client_restart_without_replay", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-body-contract-875cbbe4d9f2", + "test_id": "test_body_contract.BodyContractTests.test_unbound_historical_receipt_does_not_release_unknown_command", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-body-contract-162f94cdb580", + "test_id": "test_body_contract.BodyContractTests.test_bench_schema_still_refuses_alternate_operations_and_unsafe_motion", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-body-contract-60f3d9fcb693", + "test_id": "test_body_contract.BodyContractTests.test_required_operation_schema_refuses_before_send_or_journal", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-body-contract-ac54c860b48e", + "test_id": "test_body_contract.BodyContractTests.test_unknown_lower_proof_blocks_completed_outer_receipt_until_exact_proof", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-body-contract-28789cfeba9c", + "test_id": "test_body_contract.BodyContractTests.test_shared_envelope_refuses_wrong_identity_epoch_state_and_schema", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-body-contract-9c561606fd0b", + "test_id": "test_body_contract.BodyContractTests.test_shared_transport_import_has_no_simulator_dependency", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-be5a4f384b", + "test_id": "test_body_network.BodyNetworkTests.test_exact_concurrent_duplicate_executes_one_native_effect", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-5781a589e9", + "test_id": "test_body_network.BodyNetworkTests.test_accepted_intent_without_lower_receipt_blocks_restart", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-ae56b59eb4", + "test_id": "test_body_network.BodyNetworkTests.test_conflicting_duplicate_refuses_without_second_effect", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-ee015656d4", + "test_id": "test_body_network.BodyNetworkTests.test_missing_command_not_seen_then_not_accepted_after_lease_expiry", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-3223407c01", + "test_id": "test_body_network.BodyNetworkTests.test_known_result_survives_restart_and_old_lease_is_fenced", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-87e877e9dc", + "test_id": "test_body_network.BodyNetworkTests.test_invalid_arguments_never_create_intent", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-31080ff719", + "test_id": "test_body_network.BodyNetworkTests.test_identity_source_origin_and_exclusive_lease_refuse", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-11c1a4d12e", + "test_id": "test_body_network.BodyNetworkTests.test_image_crosses_tcp_with_exact_challenge_sha", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-ec47bb78c4", + "test_id": "test_body_network.BodyNetworkTests.test_read_only_recovery_requires_indexed_hash_verified_lower_receipt", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-0f89f21b08", + "test_id": "test_body_network.BodyNetworkTests.test_stale_observation_is_retained_known_dispatch_refusal", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-64b119ed98", + "test_id": "test_body_network.BodyNetworkTests.test_terminal_pointer_persist_failure_keeps_cache_and_disk_unknown", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-4e17d10350", + "test_id": "test_body_network.BodyNetworkTests.test_expired_and_rotated_leases_fence_new_admission", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/body-net-test-252db9f768", + "test_id": "test_body_network.BodyNetworkTests.test_lease_rechecked_after_slow_validation_before_intent", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-272c4605c3a3", + "test_id": "test_body_client.BodyClientTests.test_endpoint_pin_and_lifetime_client_lock", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-63bc74ffb717", + "test_id": "test_body_client.BodyClientTests.test_exact_256_record_client_journal_reloads_and_refuses_send_257", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-5c1de8bf4576", + "test_id": "test_body_client.BodyClientTests.test_malformed_proxy_headers_never_forward_or_admit_a_native_command", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-4f4fc027d009", + "test_id": "test_body_client.BodyClientTests.test_unknown_request_corruption_cannot_clear_or_resend", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-edeb4dbda07c", + "test_id": "test_body_client.BodyClientTests.test_rotating_lease_and_exclusive_client_holder", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-dfc0fabfda3b", + "test_id": "test_body_client.BodyClientTests.test_drop_before_remains_unknown_across_client_restart_until_lease_fence", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-0d828fc06ed6", + "test_id": "test_body_client.BodyClientTests.test_duplicate_real_socket_dispatch_has_one_native_effect", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-75052e44bdc9", + "test_id": "test_body_client.BodyClientTests.test_delays_before_and_after_forwarding_use_real_transport", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-193c9585c4a1", + "test_id": "test_body_client.BodyClientTests.test_unbound_error_after_native_success_stays_pending", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-2b5c946f33b2", + "test_id": "test_body_client.BodyClientTests.test_images_are_downloaded_hash_bound_without_changing_nested_packet", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim9-client-999d22063bac", + "test_id": "test_body_client.BodyClientTests.test_drop_after_native_effect_reconciles_without_resending", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-488b0e0f48", + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_09_identity_source_epoch_and_exclusive_lease_fence", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-4bce85857e", + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_02_bounded_delays_preserve_exact_effect", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-9dbd7e5104", + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_06_conflicting_duplicate_refuses_without_second_effect", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-5f5abeb233", + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_04_drop_after_effect_reconciles_without_resending", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-40aa82f185", + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_01_successive_network_cycles_with_RGB_only_policy", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-682ea46467", + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_08_delay_past_lease_expiry_never_admits_late_effect", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-cf2549e025", + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_10_process_restart_preserves_terminal_history_and_body", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-41c0221c94", + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_07_stale_image_proposal_cannot_dispatch_effect", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-7979f7737d", + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_12_accepted_without_lower_proof_and_client_restart_stays_unknown", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-5e6df0fafa", + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_03_drop_before_acceptance_stays_pending_until_fenced", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-4b670d1056", + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_11_restart_recovers_exact_lower_receipt_without_dispatch", + "passed": true + }, + { + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/n9test-f5722da49c", + "test_id": "test_network_body_faults.NetworkBodyFaultTests.test_05_exact_concurrent_duplicate_has_one_result_one_motion", + "passed": true + } + ] +} diff --git a/jevdual/results/reusable-body-recovery-20261012/checks/sim14-body-contract-conformance-v2.txt b/jevdual/results/reusable-body-recovery-20261012/checks/sim14-body-contract-conformance-v2.txt new file mode 100644 index 0000000..906e57e --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/checks/sim14-body-contract-conformance-v2.txt @@ -0,0 +1,48 @@ +test_alternate_command_recovers_after_reply_loss_and_client_restart_without_replay (test_body_contract.BodyContractTests.test_alternate_command_recovers_after_reply_loss_and_client_restart_without_replay) ... ok +test_bench_schema_still_refuses_alternate_operations_and_unsafe_motion (test_body_contract.BodyContractTests.test_bench_schema_still_refuses_alternate_operations_and_unsafe_motion) ... ok +test_required_operation_schema_refuses_before_send_or_journal (test_body_contract.BodyContractTests.test_required_operation_schema_refuses_before_send_or_journal) ... ok +test_shared_envelope_refuses_wrong_identity_epoch_state_and_schema (test_body_contract.BodyContractTests.test_shared_envelope_refuses_wrong_identity_epoch_state_and_schema) ... ok +test_shared_transport_import_has_no_simulator_dependency (test_body_contract.BodyContractTests.test_shared_transport_import_has_no_simulator_dependency) ... ok +test_unbound_historical_receipt_does_not_release_unknown_command (test_body_contract.BodyContractTests.test_unbound_historical_receipt_does_not_release_unknown_command) ... ok +test_unknown_lower_proof_blocks_completed_outer_receipt_until_exact_proof (test_body_contract.BodyContractTests.test_unknown_lower_proof_blocks_completed_outer_receipt_until_exact_proof) ... ok +test_accepted_intent_without_lower_receipt_blocks_restart (test_body_network.BodyNetworkTests.test_accepted_intent_without_lower_receipt_blocks_restart) ... ok +test_conflicting_duplicate_refuses_without_second_effect (test_body_network.BodyNetworkTests.test_conflicting_duplicate_refuses_without_second_effect) ... ok +test_exact_concurrent_duplicate_executes_one_native_effect (test_body_network.BodyNetworkTests.test_exact_concurrent_duplicate_executes_one_native_effect) ... ok +test_expired_and_rotated_leases_fence_new_admission (test_body_network.BodyNetworkTests.test_expired_and_rotated_leases_fence_new_admission) ... ok +test_identity_source_origin_and_exclusive_lease_refuse (test_body_network.BodyNetworkTests.test_identity_source_origin_and_exclusive_lease_refuse) ... ok +test_image_crosses_tcp_with_exact_challenge_sha (test_body_network.BodyNetworkTests.test_image_crosses_tcp_with_exact_challenge_sha) ... ok +test_invalid_arguments_never_create_intent (test_body_network.BodyNetworkTests.test_invalid_arguments_never_create_intent) ... ok +test_known_result_survives_restart_and_old_lease_is_fenced (test_body_network.BodyNetworkTests.test_known_result_survives_restart_and_old_lease_is_fenced) ... ok +test_lease_rechecked_after_slow_validation_before_intent (test_body_network.BodyNetworkTests.test_lease_rechecked_after_slow_validation_before_intent) ... ok +test_missing_command_not_seen_then_not_accepted_after_lease_expiry (test_body_network.BodyNetworkTests.test_missing_command_not_seen_then_not_accepted_after_lease_expiry) ... ok +test_read_only_recovery_requires_indexed_hash_verified_lower_receipt (test_body_network.BodyNetworkTests.test_read_only_recovery_requires_indexed_hash_verified_lower_receipt) ... ok +test_stale_observation_is_retained_known_dispatch_refusal (test_body_network.BodyNetworkTests.test_stale_observation_is_retained_known_dispatch_refusal) ... ok +test_terminal_pointer_persist_failure_keeps_cache_and_disk_unknown (test_body_network.BodyNetworkTests.test_terminal_pointer_persist_failure_keeps_cache_and_disk_unknown) ... ok +test_delays_before_and_after_forwarding_use_real_transport (test_body_client.BodyClientTests.test_delays_before_and_after_forwarding_use_real_transport) ... ok +test_drop_after_native_effect_reconciles_without_resending (test_body_client.BodyClientTests.test_drop_after_native_effect_reconciles_without_resending) ... ok +test_drop_before_remains_unknown_across_client_restart_until_lease_fence (test_body_client.BodyClientTests.test_drop_before_remains_unknown_across_client_restart_until_lease_fence) ... ok +test_duplicate_real_socket_dispatch_has_one_native_effect (test_body_client.BodyClientTests.test_duplicate_real_socket_dispatch_has_one_native_effect) ... ok +test_endpoint_pin_and_lifetime_client_lock (test_body_client.BodyClientTests.test_endpoint_pin_and_lifetime_client_lock) ... ok +test_exact_256_record_client_journal_reloads_and_refuses_send_257 (test_body_client.BodyClientTests.test_exact_256_record_client_journal_reloads_and_refuses_send_257) ... ok +test_images_are_downloaded_hash_bound_without_changing_nested_packet (test_body_client.BodyClientTests.test_images_are_downloaded_hash_bound_without_changing_nested_packet) ... ok +test_malformed_proxy_headers_never_forward_or_admit_a_native_command (test_body_client.BodyClientTests.test_malformed_proxy_headers_never_forward_or_admit_a_native_command) ... ok +test_rotating_lease_and_exclusive_client_holder (test_body_client.BodyClientTests.test_rotating_lease_and_exclusive_client_holder) ... ok +test_unbound_error_after_native_success_stays_pending (test_body_client.BodyClientTests.test_unbound_error_after_native_success_stays_pending) ... ok +test_unknown_request_corruption_cannot_clear_or_resend (test_body_client.BodyClientTests.test_unknown_request_corruption_cannot_clear_or_resend) ... ok +test_01_successive_network_cycles_with_RGB_only_policy (test_network_body_faults.NetworkBodyFaultTests.test_01_successive_network_cycles_with_RGB_only_policy) ... ok +test_02_bounded_delays_preserve_exact_effect (test_network_body_faults.NetworkBodyFaultTests.test_02_bounded_delays_preserve_exact_effect) ... ok +test_03_drop_before_acceptance_stays_pending_until_fenced (test_network_body_faults.NetworkBodyFaultTests.test_03_drop_before_acceptance_stays_pending_until_fenced) ... ok +test_04_drop_after_effect_reconciles_without_resending (test_network_body_faults.NetworkBodyFaultTests.test_04_drop_after_effect_reconciles_without_resending) ... ok +test_05_exact_concurrent_duplicate_has_one_result_one_motion (test_network_body_faults.NetworkBodyFaultTests.test_05_exact_concurrent_duplicate_has_one_result_one_motion) ... ok +test_06_conflicting_duplicate_refuses_without_second_effect (test_network_body_faults.NetworkBodyFaultTests.test_06_conflicting_duplicate_refuses_without_second_effect) ... ok +test_07_stale_image_proposal_cannot_dispatch_effect (test_network_body_faults.NetworkBodyFaultTests.test_07_stale_image_proposal_cannot_dispatch_effect) ... ok +test_08_delay_past_lease_expiry_never_admits_late_effect (test_network_body_faults.NetworkBodyFaultTests.test_08_delay_past_lease_expiry_never_admits_late_effect) ... ok +test_09_identity_source_epoch_and_exclusive_lease_fence (test_network_body_faults.NetworkBodyFaultTests.test_09_identity_source_epoch_and_exclusive_lease_fence) ... ok +test_10_process_restart_preserves_terminal_history_and_body (test_network_body_faults.NetworkBodyFaultTests.test_10_process_restart_preserves_terminal_history_and_body) ... ok +test_11_restart_recovers_exact_lower_receipt_without_dispatch (test_network_body_faults.NetworkBodyFaultTests.test_11_restart_recovers_exact_lower_receipt_without_dispatch) ... ok +test_12_accepted_without_lower_proof_and_client_restart_stays_unknown (test_network_body_faults.NetworkBodyFaultTests.test_12_accepted_without_lower_proof_and_client_restart_stays_unknown) ... ok + +---------------------------------------------------------------------- +Ran 43 tests in 63.611s + +OK diff --git a/jevdual/results/reusable-body-recovery-20261012/checks/sim14-final-guards-v2.txt b/jevdual/results/reusable-body-recovery-20261012/checks/sim14-final-guards-v2.txt new file mode 100644 index 0000000..203b54d --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/checks/sim14-final-guards-v2.txt @@ -0,0 +1,30 @@ +test_after_native_SIGKILL_retains_real_actuation_but_stays_unknown_without_replay (test_crash_body.CrashBodyTests.test_after_native_SIGKILL_retains_real_actuation_but_stays_unknown_without_replay) ... ok +test_crash_gate_refuses_a_nondiagnostic_body_and_stale_marker (test_crash_body.CrashBodyTests.test_crash_gate_refuses_a_nondiagnostic_body_and_stale_marker) ... ok +test_indexed_lower_receipt_SIGKILL_recovers_exact_command_without_motion_replay (test_crash_body.CrashBodyTests.test_indexed_lower_receipt_SIGKILL_recovers_exact_command_without_motion_replay) ... ok +test_auxiliary_exact_state_is_not_admission_authority (test_score_workbench_crashes.CrashScorerTests.test_auxiliary_exact_state_is_not_admission_authority) ... ok +test_crash_boundary_rejects_successor_command_before_recovery (test_score_workbench_crashes.CrashScorerTests.test_crash_boundary_rejects_successor_command_before_recovery) ... ok +test_crash_boundary_rejects_wrong_token_boot_and_owned_children (test_score_workbench_crashes.CrashScorerTests.test_crash_boundary_rejects_wrong_token_boot_and_owned_children) ... ok +test_dead_host_browser_cache_requires_exact_provenance_and_is_not_live_state (test_score_workbench_crashes.CrashScorerTests.test_dead_host_browser_cache_requires_exact_provenance_and_is_not_live_state) ... ok +test_native_gap_browser_cannot_claim_completion_or_successor (test_score_workbench_crashes.CrashScorerTests.test_native_gap_browser_cannot_claim_completion_or_successor) ... ok +test_only_native_gap_accepts_database_not_yet_created_without_writing (test_score_workbench_crashes.CrashScorerTests.test_only_native_gap_accepts_database_not_yet_created_without_writing) ... ok +test_receipt_after_resume_is_refused (test_score_workbench_crashes.CrashScorerTests.test_receipt_after_resume_is_refused) ... ok +test_terminal_envelope_rejects_digest_identity_and_epoch_substitution (test_score_workbench_crashes.CrashScorerTests.test_terminal_envelope_rejects_digest_identity_and_epoch_substitution) ... ok +test_changed_retired_call_files_or_live_group_fail_independent_scoring (test_workbench_recovery_trials.RecoveryTrialTests.test_changed_retired_call_files_or_live_group_fail_independent_scoring) ... ok +test_descendant_capture_and_retirement_covers_secondary_groups (test_workbench_recovery_trials.RecoveryTrialTests.test_descendant_capture_and_retirement_covers_secondary_groups) ... ok +test_guardian_descendant_proof_requires_bound_completion_and_snapshot_disabled (test_workbench_recovery_trials.RecoveryTrialTests.test_guardian_descendant_proof_requires_bound_completion_and_snapshot_disabled) ... ok +test_manual_pid_crashes_are_not_accepted (test_workbench_recovery_trials.RecoveryTrialTests.test_manual_pid_crashes_are_not_accepted) ... ok +test_merge_retains_every_group_across_sets_id_or_exec (test_workbench_recovery_trials.RecoveryTrialTests.test_merge_retains_every_group_across_sets_id_or_exec) ... ok +test_non_dispatch_event_cannot_arm_gate (test_workbench_recovery_trials.RecoveryTrialTests.test_non_dispatch_event_cannot_arm_gate) ... ok +test_planner_boundary_requires_closure_before_fresh_explicit_resume (test_workbench_recovery_trials.RecoveryTrialTests.test_planner_boundary_requires_closure_before_fresh_explicit_resume) ... ok +test_process_absence_uses_start_identity_and_checks_whole_group (test_workbench_recovery_trials.RecoveryTrialTests.test_process_absence_uses_start_identity_and_checks_whole_group) ... ok +test_publication_gate_binds_retained_original_stage_and_durable_event (test_workbench_recovery_trials.RecoveryTrialTests.test_publication_gate_binds_retained_original_stage_and_durable_event) ... ok +test_receipt_recovery_keeps_original_token_while_clearing_pending (test_workbench_recovery_trials.RecoveryTrialTests.test_receipt_recovery_keeps_original_token_while_clearing_pending) ... ok +test_reparented_detached_writer_cannot_be_inferred_to_have_exited (test_workbench_recovery_trials.RecoveryTrialTests.test_reparented_detached_writer_cannot_be_inferred_to_have_exited) ... ok +test_reuses_exact_prior_wire_relay_and_indexed_command (test_workbench_recovery_trials.RecoveryTrialTests.test_reuses_exact_prior_wire_relay_and_indexed_command) ... ok +test_unclosed_guardian_and_replayed_command_are_refused (test_workbench_recovery_trials.RecoveryTrialTests.test_unclosed_guardian_and_replayed_command_are_refused) ... ok +test_wrong_command_or_sequence_refuses_diagnostic (test_workbench_recovery_trials.RecoveryTrialTests.test_wrong_command_or_sequence_refuses_diagnostic) ... ok + +---------------------------------------------------------------------- +Ran 25 tests in 1.147s + +OK diff --git a/jevdual/results/reusable-body-recovery-20261012/checks/sim14-planner-leader-proof-final.txt b/jevdual/results/reusable-body-recovery-20261012/checks/sim14-planner-leader-proof-final.txt new file mode 100644 index 0000000..ecf8981 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/checks/sim14-planner-leader-proof-final.txt @@ -0,0 +1,30 @@ +test_nonblocking_delivery_preserves_entire_maximum_bound_prompt (test_planner_guard.PlannerBackpressureTests.test_nonblocking_delivery_preserves_entire_maximum_bound_prompt) ... ok +test_observed_detached_descendant_withholds_closure_without_signalling_unrelated_process (test_planner_guard.PlannerBackpressureTests.test_observed_detached_descendant_withholds_closure_without_signalling_unrelated_process) ... ok +test_sigkill_host_with_full_nonreading_stdin_retires_exact_owned_group (test_planner_guard.PlannerBackpressureTests.test_sigkill_host_with_full_nonreading_stdin_retires_exact_owned_group) ... ok +test_closed_action_schema_finite_targets_and_no_success_flag (test_codex_policy.PolicyTests.test_closed_action_schema_finite_targets_and_no_success_flag) ... ok +test_fake_cli_bound_image_schema_tool_isolation_and_reservation (test_codex_policy.PolicyTests.test_fake_cli_bound_image_schema_tool_isolation_and_reservation) ... ok +test_usage_optional_and_any_tool_event_refuses (test_codex_policy.PolicyTests.test_usage_optional_and_any_tool_event_refuses) ... ok +test_active_planner_host_sigkill_retires_group_and_resume_uses_fresh_image (test_workbench_crash_runtime.WorkbenchCrashRuntimeTests.test_active_planner_host_sigkill_retires_group_and_resume_uses_fresh_image) ... ok +test_host_sigkill_before_guardian_launch_keeps_reserved_budget_without_replay (test_workbench_crash_runtime.WorkbenchCrashRuntimeTests.test_host_sigkill_before_guardian_launch_keeps_reserved_budget_without_replay) ... ok +test_publication_tail_sigkill_repairs_only_audit_index_without_replay (test_workbench_crash_runtime.WorkbenchCrashRuntimeTests.test_publication_tail_sigkill_repairs_only_audit_index_without_replay) ... ok +test_sigkill_host_after_known_body_effect_recovers_exact_receipt_without_replay (test_workbench_crash_runtime.WorkbenchCrashRuntimeTests.test_sigkill_host_after_known_body_effect_recovers_exact_receipt_without_replay) ... ok +test_before_guardian_launch_has_no_old_writer (test_workbench_recovery_guards.RecoveryGuardTests.test_before_guardian_launch_has_no_old_writer) ... ok +test_contiguous_audit_tail_preserves_stage_and_applied_identity (test_workbench_recovery_guards.RecoveryGuardTests.test_contiguous_audit_tail_preserves_stage_and_applied_identity) ... ok +test_empty_complete_descendant_proof_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_empty_complete_descendant_proof_refuses) ... ok +test_guardian_start_without_closure_is_ambiguous (test_workbench_recovery_guards.RecoveryGuardTests.test_guardian_start_without_closure_is_ambiguous) ... ok +test_inconsistent_closure_refuses_without_pid_signals (test_workbench_recovery_guards.RecoveryGuardTests.test_inconsistent_closure_refuses_without_pid_signals) ... ok +test_inconsistent_or_incomplete_descendant_proof_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_inconsistent_or_incomplete_descendant_proof_refuses) ... ok +test_indexed_activity_conflict_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_indexed_activity_conflict_refuses) ... ok +test_malformed_tail_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_malformed_tail_refuses) ... ok +test_missing_event_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_missing_event_refuses) ... ok +test_missing_indexed_file_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_missing_indexed_file_refuses) ... ok +test_orphan_guardian_artifacts_refuse (test_workbench_recovery_guards.RecoveryGuardTests.test_orphan_guardian_artifacts_refuse) ... ok +test_reservation_owner_conflict_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_reservation_owner_conflict_refuses) ... ok +test_spawned_planner_without_descendant_proof_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_spawned_planner_without_descendant_proof_refuses) ... ok +test_symlink_event_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_symlink_event_refuses) ... ok +test_truncated_or_unbound_leader_descendant_proof_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_truncated_or_unbound_leader_descendant_proof_refuses) ... ok + +---------------------------------------------------------------------- +Ran 25 tests in 10.685s + +OK diff --git a/jevdual/results/reusable-body-recovery-20261012/checks/sim14-runtime-regression.txt b/jevdual/results/reusable-body-recovery-20261012/checks/sim14-runtime-regression.txt new file mode 100644 index 0000000..3367416 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/checks/sim14-runtime-regression.txt @@ -0,0 +1,64 @@ +test_acknowledged_second_boot_survives_outage_without_epoch_regression (test_network_operator.NetworkOperatorTests.test_acknowledged_second_boot_survives_outage_without_epoch_regression) ... ok +test_acquisition_first_reboot_cannot_authorize_old_decision_with_fresh_lease (test_network_operator.NetworkOperatorTests.test_acquisition_first_reboot_cannot_authorize_old_decision_with_fresh_lease) ... ok +test_changed_image_and_source_refuse_without_network_effect (test_network_operator.NetworkOperatorTests.test_changed_image_and_source_refuse_without_network_effect) ... ok +test_checking_does_not_release_unknown_and_exact_receipt_stays_paused (test_network_operator.NetworkOperatorTests.test_checking_does_not_release_unknown_and_exact_receipt_stays_paused) ... ok +test_checking_followed_by_changed_source_identity_cancels_and_blocks (test_network_operator.NetworkOperatorTests.test_checking_followed_by_changed_source_identity_cancels_and_blocks) ... ok +test_checking_health_cannot_admit_when_fresh_tcp_proof_times_out (test_network_operator.NetworkOperatorTests.test_checking_health_cannot_admit_when_fresh_tcp_proof_times_out) ... ok +test_checking_health_fresh_unknown_body_blocks_before_lease_or_stage (test_network_operator.NetworkOperatorTests.test_checking_health_fresh_unknown_body_blocks_before_lease_or_stage) ... ok +test_checking_health_keeps_verified_boot_change_as_hard_veto (test_network_operator.NetworkOperatorTests.test_checking_health_keeps_verified_boot_change_as_hard_veto) ... ok +test_full_local_manifest_capacity_keeps_wire_limits (test_network_operator.NetworkOperatorTests.test_full_local_manifest_capacity_keeps_wire_limits) ... ok +test_graceful_host_restart_labels_unfinished_planning_as_paused (test_network_operator.NetworkOperatorTests.test_graceful_host_restart_labels_unfinished_planning_as_paused) ... ok +test_health_first_fast_body_reboot_withholds_old_model_decision (test_network_operator.NetworkOperatorTests.test_health_first_fast_body_reboot_withholds_old_model_decision) ... ok +test_healthy_delayed_probe_preserves_model_then_requires_fresh_admission_proof (test_network_operator.NetworkOperatorTests.test_healthy_delayed_probe_preserves_model_then_requires_fresh_admission_proof) ... ok +test_idle_body_outage_becomes_unavailable_without_effect_polling (test_network_operator.NetworkOperatorTests.test_idle_body_outage_becomes_unavailable_without_effect_polling) ... ok +test_invalid_body_identity_health_is_blocked_without_thread_crash (test_network_operator.NetworkOperatorTests.test_invalid_body_identity_health_is_blocked_without_thread_crash) ... ok +test_lost_dispatch_disconnect_recovery_and_explicit_resume (test_network_operator.NetworkOperatorTests.test_lost_dispatch_disconnect_recovery_and_explicit_resume) ... ok +test_pause_after_host_admission_allows_exact_receipt_but_no_successor (test_network_operator.NetworkOperatorTests.test_pause_after_host_admission_allows_exact_receipt_but_no_successor) ... ok +test_pause_before_host_admission_suppresses_stage_and_socket_command (test_network_operator.NetworkOperatorTests.test_pause_before_host_admission_suppresses_stage_and_socket_command) ... ok +test_pause_during_fresh_admission_proof_prevents_lease_and_command (test_network_operator.NetworkOperatorTests.test_pause_during_fresh_admission_proof_prevents_lease_and_command) ... ok +test_pause_during_model_withholds_unadmitted_decision (test_network_operator.NetworkOperatorTests.test_pause_during_model_withholds_unadmitted_decision) ... ok +test_pause_wins_while_resume_waits_for_actual_socket_status (test_network_operator.NetworkOperatorTests.test_pause_wins_while_resume_waits_for_actual_socket_status) ... ok +test_restart_recovers_exact_queue_stage_once_from_historical_receipt (test_network_operator.NetworkOperatorTests.test_restart_recovers_exact_queue_stage_once_from_historical_receipt) ... ok +test_stale_health_timeout_after_known_socket_dispatch_does_not_pause (test_network_operator.NetworkOperatorTests.test_stale_health_timeout_after_known_socket_dispatch_does_not_pause) ... ok +test_state_and_frame_are_responsive_while_transport_pending (test_network_operator.NetworkOperatorTests.test_state_and_frame_are_responsive_while_transport_pending) ... ok +test_two_successive_jobs_and_disturbance_using_declared_replay (test_network_operator.NetworkOperatorTests.test_two_successive_jobs_and_disturbance_using_declared_replay) ... ok +test_unknown_dispatch_survives_body_epoch_then_exact_recovery_and_resume (test_network_operator.NetworkOperatorTests.test_unknown_dispatch_survives_body_epoch_then_exact_recovery_and_resume) ... ok +test_unknown_start_observe_and_proposal_settle_without_replay_or_old_decision (test_network_operator.NetworkOperatorTests.test_unknown_start_observe_and_proposal_settle_without_replay_or_old_decision) ... ok +test_controls_require_empty_exact_json_body (test_network_operator_http.NetworkHTTPTests.test_controls_require_empty_exact_json_body) ... ok +test_fault_schema_is_bounded_and_dispatch_only (test_network_operator_http.NetworkHTTPTests.test_fault_schema_is_bounded_and_dispatch_only) ... ok +test_http_body_framing_is_closed_before_mutation (test_network_operator_http.NetworkHTTPTests.test_http_body_framing_is_closed_before_mutation) ... ok +test_missing_foreign_or_duplicate_headers_never_mutate (test_network_operator_http.NetworkHTTPTests.test_missing_foreign_or_duplicate_headers_never_mutate) ... ok +test_network_html_read_only_state_and_unknown_routes (test_network_operator_http.NetworkHTTPTests.test_network_html_read_only_state_and_unknown_routes) ... ok +test_new_controls_admitted_only_with_exact_identity (test_network_operator_http.NetworkHTTPTests.test_new_controls_admitted_only_with_exact_identity) ... ok +test_exact_fresh_untagged_state_before_lease_and_command_passes (test_workbench_health.AdmissionScorerTests.test_exact_fresh_untagged_state_before_lease_and_command_passes) ... ok +test_one_proof_cannot_authorize_a_second_command (test_workbench_health.AdmissionScorerTests.test_one_proof_cannot_authorize_a_second_command) ... ok +test_tagged_stale_foreign_or_unbound_proof_fails (test_workbench_health.AdmissionScorerTests.test_tagged_stale_foreign_or_unbound_proof_fails) ... ok +test_only_tagged_identity_and_state_receive_actual_delay (test_workbench_health.HealthHarnessTests.test_only_tagged_identity_and_state_receive_actual_delay) ... ok +test_owned_control_selects_state_without_changing_normal_reads (test_workbench_health.HealthHarnessTests.test_owned_control_selects_state_without_changing_normal_reads) ... ok +test_post_bytes_identity_headers_immediate_and_no_retry (test_workbench_health.HealthHarnessTests.test_post_bytes_identity_headers_immediate_and_no_retry) ... ok +test_probe_annotation_cannot_change_effect_or_image_routes (test_workbench_health.HealthHarnessTests.test_probe_annotation_cannot_change_effect_or_image_routes) ... ok +test_cancelled_reply_stays_withheld_after_event_clears (test_operator_runtime.OperatorTests.test_cancelled_reply_stays_withheld_after_event_clears) ... ok +test_frame_identity_changes_when_zone_repaint_keeps_generation (test_operator_runtime.OperatorTests.test_frame_identity_changes_when_zone_repaint_keeps_generation) ... ok +test_get_state_and_frame_do_not_capture_or_call_model (test_operator_runtime.OperatorTests.test_get_state_and_frame_do_not_capture_or_call_model) ... ok +test_global_reserved_call_budget_blocks_starting_another_job (test_operator_runtime.OperatorTests.test_global_reserved_call_budget_blocks_starting_another_job) ... ok +test_http_origin_identity_duplicate_keys_and_body_schema (test_operator_runtime.OperatorTests.test_http_origin_identity_duplicate_keys_and_body_schema) ... ok +test_interrupted_start_intent_blocks_restart_without_replay (test_operator_runtime.OperatorTests.test_interrupted_start_intent_blocks_restart_without_replay) ... ok +test_invalid_finish_reset_refuses_native_guard (test_operator_runtime.OperatorTests.test_invalid_finish_reset_refuses_native_guard) ... ok +test_model_cannot_claim_completion_or_bypass_guard (test_operator_runtime.OperatorTests.test_model_cannot_claim_completion_or_bypass_guard) ... ok +test_model_unavailable_pauses_counts_attempt_and_never_completes (test_operator_runtime.OperatorTests.test_model_unavailable_pauses_counts_attempt_and_never_completes) ... ok +test_mutated_operator_manifest_and_fabricated_completion_refuse (test_operator_runtime.OperatorTests.test_mutated_operator_manifest_and_fabricated_completion_refuse) ... ok +test_new_identity_bound_to_separate_persistent_bench (test_operator_runtime.OperatorTests.test_new_identity_bound_to_separate_persistent_bench) ... ok +test_pause_between_outer_loop_check_and_step_admission_starts_nothing (test_operator_runtime.OperatorTests.test_pause_between_outer_loop_check_and_step_admission_starts_nothing) ... ok +test_pause_during_model_prevents_late_reply_dispatch (test_operator_runtime.OperatorTests.test_pause_during_model_prevents_late_reply_dispatch) ... ok +test_queue_validation_duplicate_and_path_schema (test_operator_runtime.OperatorTests.test_queue_validation_duplicate_and_path_schema) ... ok +test_restart_retains_body_and_queue_without_replay (test_operator_runtime.OperatorTests.test_restart_retains_body_and_queue_without_replay) ... ok +test_shutdown_cancelled_model_exit_persists_stopped_after_worker_exits (test_operator_runtime.OperatorTests.test_shutdown_cancelled_model_exit_persists_stopped_after_worker_exits) ... ok +test_source_change_refuses_mutation (test_operator_runtime.OperatorTests.test_source_change_refuses_mutation) ... ok +test_stop_decision_pauses_without_completion (test_operator_runtime.OperatorTests.test_stop_decision_pauses_without_completion) ... ok +test_two_jobs_replay_public_action_fixture_and_disturbance_guard (test_operator_runtime.OperatorTests.test_two_jobs_replay_public_action_fixture_and_disturbance_guard) ... ok +test_unknown_native_blocks_resume_and_no_reset (test_operator_runtime.OperatorTests.test_unknown_native_blocks_resume_and_no_reset) ... ok + +---------------------------------------------------------------------- +Ran 59 tests in 67.954s + +OK diff --git a/jevdual/results/reusable-body-recovery-20261012/checks/test-identifiers.json b/jevdual/results/reusable-body-recovery-20261012/checks/test-identifiers.json new file mode 100644 index 0000000..8324ee6 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/checks/test-identifiers.json @@ -0,0 +1,154 @@ +[ + "test_01_successive_network_cycles_with_RGB_only_policy (test_network_body_faults.NetworkBodyFaultTests.test_01_successive_network_cycles_with_RGB_only_policy)", + "test_02_bounded_delays_preserve_exact_effect (test_network_body_faults.NetworkBodyFaultTests.test_02_bounded_delays_preserve_exact_effect)", + "test_03_drop_before_acceptance_stays_pending_until_fenced (test_network_body_faults.NetworkBodyFaultTests.test_03_drop_before_acceptance_stays_pending_until_fenced)", + "test_04_drop_after_effect_reconciles_without_resending (test_network_body_faults.NetworkBodyFaultTests.test_04_drop_after_effect_reconciles_without_resending)", + "test_05_exact_concurrent_duplicate_has_one_result_one_motion (test_network_body_faults.NetworkBodyFaultTests.test_05_exact_concurrent_duplicate_has_one_result_one_motion)", + "test_06_conflicting_duplicate_refuses_without_second_effect (test_network_body_faults.NetworkBodyFaultTests.test_06_conflicting_duplicate_refuses_without_second_effect)", + "test_07_stale_image_proposal_cannot_dispatch_effect (test_network_body_faults.NetworkBodyFaultTests.test_07_stale_image_proposal_cannot_dispatch_effect)", + "test_08_delay_past_lease_expiry_never_admits_late_effect (test_network_body_faults.NetworkBodyFaultTests.test_08_delay_past_lease_expiry_never_admits_late_effect)", + "test_09_identity_source_epoch_and_exclusive_lease_fence (test_network_body_faults.NetworkBodyFaultTests.test_09_identity_source_epoch_and_exclusive_lease_fence)", + "test_10_process_restart_preserves_terminal_history_and_body (test_network_body_faults.NetworkBodyFaultTests.test_10_process_restart_preserves_terminal_history_and_body)", + "test_11_restart_recovers_exact_lower_receipt_without_dispatch (test_network_body_faults.NetworkBodyFaultTests.test_11_restart_recovers_exact_lower_receipt_without_dispatch)", + "test_12_accepted_without_lower_proof_and_client_restart_stays_unknown (test_network_body_faults.NetworkBodyFaultTests.test_12_accepted_without_lower_proof_and_client_restart_stays_unknown)", + "test_accepted_intent_without_lower_receipt_blocks_restart (test_body_network.BodyNetworkTests.test_accepted_intent_without_lower_receipt_blocks_restart)", + "test_acknowledged_second_boot_survives_outage_without_epoch_regression (test_network_operator.NetworkOperatorTests.test_acknowledged_second_boot_survives_outage_without_epoch_regression)", + "test_acquisition_first_reboot_cannot_authorize_old_decision_with_fresh_lease (test_network_operator.NetworkOperatorTests.test_acquisition_first_reboot_cannot_authorize_old_decision_with_fresh_lease)", + "test_active_planner_host_sigkill_retires_group_and_resume_uses_fresh_image (test_workbench_crash_runtime.WorkbenchCrashRuntimeTests.test_active_planner_host_sigkill_retires_group_and_resume_uses_fresh_image)", + "test_after_native_SIGKILL_retains_real_actuation_but_stays_unknown_without_replay (test_crash_body.CrashBodyTests.test_after_native_SIGKILL_retains_real_actuation_but_stays_unknown_without_replay)", + "test_alternate_command_recovers_after_reply_loss_and_client_restart_without_replay (test_body_contract.BodyContractTests.test_alternate_command_recovers_after_reply_loss_and_client_restart_without_replay)", + "test_auxiliary_exact_state_is_not_admission_authority (test_score_workbench_crashes.CrashScorerTests.test_auxiliary_exact_state_is_not_admission_authority)", + "test_before_guardian_launch_has_no_old_writer (test_workbench_recovery_guards.RecoveryGuardTests.test_before_guardian_launch_has_no_old_writer)", + "test_bench_schema_still_refuses_alternate_operations_and_unsafe_motion (test_body_contract.BodyContractTests.test_bench_schema_still_refuses_alternate_operations_and_unsafe_motion)", + "test_cancelled_reply_stays_withheld_after_event_clears (test_operator_runtime.OperatorTests.test_cancelled_reply_stays_withheld_after_event_clears)", + "test_changed_image_and_source_refuse_without_network_effect (test_network_operator.NetworkOperatorTests.test_changed_image_and_source_refuse_without_network_effect)", + "test_changed_retired_call_files_or_live_group_fail_independent_scoring (test_workbench_recovery_trials.RecoveryTrialTests.test_changed_retired_call_files_or_live_group_fail_independent_scoring)", + "test_checking_does_not_release_unknown_and_exact_receipt_stays_paused (test_network_operator.NetworkOperatorTests.test_checking_does_not_release_unknown_and_exact_receipt_stays_paused)", + "test_checking_followed_by_changed_source_identity_cancels_and_blocks (test_network_operator.NetworkOperatorTests.test_checking_followed_by_changed_source_identity_cancels_and_blocks)", + "test_checking_health_cannot_admit_when_fresh_tcp_proof_times_out (test_network_operator.NetworkOperatorTests.test_checking_health_cannot_admit_when_fresh_tcp_proof_times_out)", + "test_checking_health_fresh_unknown_body_blocks_before_lease_or_stage (test_network_operator.NetworkOperatorTests.test_checking_health_fresh_unknown_body_blocks_before_lease_or_stage)", + "test_checking_health_keeps_verified_boot_change_as_hard_veto (test_network_operator.NetworkOperatorTests.test_checking_health_keeps_verified_boot_change_as_hard_veto)", + "test_closed_action_schema_finite_targets_and_no_success_flag (test_codex_policy.PolicyTests.test_closed_action_schema_finite_targets_and_no_success_flag)", + "test_conflicting_duplicate_refuses_without_second_effect (test_body_network.BodyNetworkTests.test_conflicting_duplicate_refuses_without_second_effect)", + "test_contiguous_audit_tail_preserves_stage_and_applied_identity (test_workbench_recovery_guards.RecoveryGuardTests.test_contiguous_audit_tail_preserves_stage_and_applied_identity)", + "test_controls_require_empty_exact_json_body (test_network_operator_http.NetworkHTTPTests.test_controls_require_empty_exact_json_body)", + "test_crash_boundary_rejects_successor_command_before_recovery (test_score_workbench_crashes.CrashScorerTests.test_crash_boundary_rejects_successor_command_before_recovery)", + "test_crash_boundary_rejects_wrong_token_boot_and_owned_children (test_score_workbench_crashes.CrashScorerTests.test_crash_boundary_rejects_wrong_token_boot_and_owned_children)", + "test_crash_gate_refuses_a_nondiagnostic_body_and_stale_marker (test_crash_body.CrashBodyTests.test_crash_gate_refuses_a_nondiagnostic_body_and_stale_marker)", + "test_dead_host_browser_cache_requires_exact_provenance_and_is_not_live_state (test_score_workbench_crashes.CrashScorerTests.test_dead_host_browser_cache_requires_exact_provenance_and_is_not_live_state)", + "test_delays_before_and_after_forwarding_use_real_transport (test_body_client.BodyClientTests.test_delays_before_and_after_forwarding_use_real_transport)", + "test_descendant_capture_and_retirement_covers_secondary_groups (test_workbench_recovery_trials.RecoveryTrialTests.test_descendant_capture_and_retirement_covers_secondary_groups)", + "test_drop_after_native_effect_reconciles_without_resending (test_body_client.BodyClientTests.test_drop_after_native_effect_reconciles_without_resending)", + "test_drop_before_remains_unknown_across_client_restart_until_lease_fence (test_body_client.BodyClientTests.test_drop_before_remains_unknown_across_client_restart_until_lease_fence)", + "test_duplicate_real_socket_dispatch_has_one_native_effect (test_body_client.BodyClientTests.test_duplicate_real_socket_dispatch_has_one_native_effect)", + "test_empty_complete_descendant_proof_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_empty_complete_descendant_proof_refuses)", + "test_endpoint_pin_and_lifetime_client_lock (test_body_client.BodyClientTests.test_endpoint_pin_and_lifetime_client_lock)", + "test_exact_256_record_client_journal_reloads_and_refuses_send_257 (test_body_client.BodyClientTests.test_exact_256_record_client_journal_reloads_and_refuses_send_257)", + "test_exact_concurrent_duplicate_executes_one_native_effect (test_body_network.BodyNetworkTests.test_exact_concurrent_duplicate_executes_one_native_effect)", + "test_exact_fresh_untagged_state_before_lease_and_command_passes (test_workbench_health.AdmissionScorerTests.test_exact_fresh_untagged_state_before_lease_and_command_passes)", + "test_expired_and_rotated_leases_fence_new_admission (test_body_network.BodyNetworkTests.test_expired_and_rotated_leases_fence_new_admission)", + "test_fake_cli_bound_image_schema_tool_isolation_and_reservation (test_codex_policy.PolicyTests.test_fake_cli_bound_image_schema_tool_isolation_and_reservation)", + "test_fault_schema_is_bounded_and_dispatch_only (test_network_operator_http.NetworkHTTPTests.test_fault_schema_is_bounded_and_dispatch_only)", + "test_frame_identity_changes_when_zone_repaint_keeps_generation (test_operator_runtime.OperatorTests.test_frame_identity_changes_when_zone_repaint_keeps_generation)", + "test_full_local_manifest_capacity_keeps_wire_limits (test_network_operator.NetworkOperatorTests.test_full_local_manifest_capacity_keeps_wire_limits)", + "test_get_state_and_frame_do_not_capture_or_call_model (test_operator_runtime.OperatorTests.test_get_state_and_frame_do_not_capture_or_call_model)", + "test_global_reserved_call_budget_blocks_starting_another_job (test_operator_runtime.OperatorTests.test_global_reserved_call_budget_blocks_starting_another_job)", + "test_graceful_host_restart_labels_unfinished_planning_as_paused (test_network_operator.NetworkOperatorTests.test_graceful_host_restart_labels_unfinished_planning_as_paused)", + "test_guardian_descendant_proof_requires_bound_completion_and_snapshot_disabled (test_workbench_recovery_trials.RecoveryTrialTests.test_guardian_descendant_proof_requires_bound_completion_and_snapshot_disabled)", + "test_guardian_start_without_closure_is_ambiguous (test_workbench_recovery_guards.RecoveryGuardTests.test_guardian_start_without_closure_is_ambiguous)", + "test_health_first_fast_body_reboot_withholds_old_model_decision (test_network_operator.NetworkOperatorTests.test_health_first_fast_body_reboot_withholds_old_model_decision)", + "test_healthy_delayed_probe_preserves_model_then_requires_fresh_admission_proof (test_network_operator.NetworkOperatorTests.test_healthy_delayed_probe_preserves_model_then_requires_fresh_admission_proof)", + "test_host_sigkill_before_guardian_launch_keeps_reserved_budget_without_replay (test_workbench_crash_runtime.WorkbenchCrashRuntimeTests.test_host_sigkill_before_guardian_launch_keeps_reserved_budget_without_replay)", + "test_http_body_framing_is_closed_before_mutation (test_network_operator_http.NetworkHTTPTests.test_http_body_framing_is_closed_before_mutation)", + "test_http_origin_identity_duplicate_keys_and_body_schema (test_operator_runtime.OperatorTests.test_http_origin_identity_duplicate_keys_and_body_schema)", + "test_identity_source_origin_and_exclusive_lease_refuse (test_body_network.BodyNetworkTests.test_identity_source_origin_and_exclusive_lease_refuse)", + "test_idle_body_outage_becomes_unavailable_without_effect_polling (test_network_operator.NetworkOperatorTests.test_idle_body_outage_becomes_unavailable_without_effect_polling)", + "test_image_crosses_tcp_with_exact_challenge_sha (test_body_network.BodyNetworkTests.test_image_crosses_tcp_with_exact_challenge_sha)", + "test_images_are_downloaded_hash_bound_without_changing_nested_packet (test_body_client.BodyClientTests.test_images_are_downloaded_hash_bound_without_changing_nested_packet)", + "test_inconsistent_closure_refuses_without_pid_signals (test_workbench_recovery_guards.RecoveryGuardTests.test_inconsistent_closure_refuses_without_pid_signals)", + "test_inconsistent_or_incomplete_descendant_proof_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_inconsistent_or_incomplete_descendant_proof_refuses)", + "test_indexed_activity_conflict_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_indexed_activity_conflict_refuses)", + "test_indexed_lower_receipt_SIGKILL_recovers_exact_command_without_motion_replay (test_crash_body.CrashBodyTests.test_indexed_lower_receipt_SIGKILL_recovers_exact_command_without_motion_replay)", + "test_interrupted_start_intent_blocks_restart_without_replay (test_operator_runtime.OperatorTests.test_interrupted_start_intent_blocks_restart_without_replay)", + "test_invalid_arguments_never_create_intent (test_body_network.BodyNetworkTests.test_invalid_arguments_never_create_intent)", + "test_invalid_body_identity_health_is_blocked_without_thread_crash (test_network_operator.NetworkOperatorTests.test_invalid_body_identity_health_is_blocked_without_thread_crash)", + "test_invalid_finish_reset_refuses_native_guard (test_operator_runtime.OperatorTests.test_invalid_finish_reset_refuses_native_guard)", + "test_known_result_survives_restart_and_old_lease_is_fenced (test_body_network.BodyNetworkTests.test_known_result_survives_restart_and_old_lease_is_fenced)", + "test_lease_rechecked_after_slow_validation_before_intent (test_body_network.BodyNetworkTests.test_lease_rechecked_after_slow_validation_before_intent)", + "test_lost_dispatch_disconnect_recovery_and_explicit_resume (test_network_operator.NetworkOperatorTests.test_lost_dispatch_disconnect_recovery_and_explicit_resume)", + "test_malformed_proxy_headers_never_forward_or_admit_a_native_command (test_body_client.BodyClientTests.test_malformed_proxy_headers_never_forward_or_admit_a_native_command)", + "test_malformed_tail_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_malformed_tail_refuses)", + "test_manual_pid_crashes_are_not_accepted (test_workbench_recovery_trials.RecoveryTrialTests.test_manual_pid_crashes_are_not_accepted)", + "test_merge_retains_every_group_across_sets_id_or_exec (test_workbench_recovery_trials.RecoveryTrialTests.test_merge_retains_every_group_across_sets_id_or_exec)", + "test_missing_command_not_seen_then_not_accepted_after_lease_expiry (test_body_network.BodyNetworkTests.test_missing_command_not_seen_then_not_accepted_after_lease_expiry)", + "test_missing_event_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_missing_event_refuses)", + "test_missing_foreign_or_duplicate_headers_never_mutate (test_network_operator_http.NetworkHTTPTests.test_missing_foreign_or_duplicate_headers_never_mutate)", + "test_missing_indexed_file_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_missing_indexed_file_refuses)", + "test_model_cannot_claim_completion_or_bypass_guard (test_operator_runtime.OperatorTests.test_model_cannot_claim_completion_or_bypass_guard)", + "test_model_unavailable_pauses_counts_attempt_and_never_completes (test_operator_runtime.OperatorTests.test_model_unavailable_pauses_counts_attempt_and_never_completes)", + "test_mutated_operator_manifest_and_fabricated_completion_refuse (test_operator_runtime.OperatorTests.test_mutated_operator_manifest_and_fabricated_completion_refuse)", + "test_native_gap_browser_cannot_claim_completion_or_successor (test_score_workbench_crashes.CrashScorerTests.test_native_gap_browser_cannot_claim_completion_or_successor)", + "test_network_html_read_only_state_and_unknown_routes (test_network_operator_http.NetworkHTTPTests.test_network_html_read_only_state_and_unknown_routes)", + "test_new_controls_admitted_only_with_exact_identity (test_network_operator_http.NetworkHTTPTests.test_new_controls_admitted_only_with_exact_identity)", + "test_new_identity_bound_to_separate_persistent_bench (test_operator_runtime.OperatorTests.test_new_identity_bound_to_separate_persistent_bench)", + "test_non_dispatch_event_cannot_arm_gate (test_workbench_recovery_trials.RecoveryTrialTests.test_non_dispatch_event_cannot_arm_gate)", + "test_nonblocking_delivery_preserves_entire_maximum_bound_prompt (test_planner_guard.PlannerBackpressureTests.test_nonblocking_delivery_preserves_entire_maximum_bound_prompt)", + "test_observed_detached_descendant_withholds_closure_without_signalling_unrelated_process (test_planner_guard.PlannerBackpressureTests.test_observed_detached_descendant_withholds_closure_without_signalling_unrelated_process)", + "test_one_proof_cannot_authorize_a_second_command (test_workbench_health.AdmissionScorerTests.test_one_proof_cannot_authorize_a_second_command)", + "test_only_native_gap_accepts_database_not_yet_created_without_writing (test_score_workbench_crashes.CrashScorerTests.test_only_native_gap_accepts_database_not_yet_created_without_writing)", + "test_only_tagged_identity_and_state_receive_actual_delay (test_workbench_health.HealthHarnessTests.test_only_tagged_identity_and_state_receive_actual_delay)", + "test_orphan_guardian_artifacts_refuse (test_workbench_recovery_guards.RecoveryGuardTests.test_orphan_guardian_artifacts_refuse)", + "test_owned_control_selects_state_without_changing_normal_reads (test_workbench_health.HealthHarnessTests.test_owned_control_selects_state_without_changing_normal_reads)", + "test_pause_after_host_admission_allows_exact_receipt_but_no_successor (test_network_operator.NetworkOperatorTests.test_pause_after_host_admission_allows_exact_receipt_but_no_successor)", + "test_pause_before_host_admission_suppresses_stage_and_socket_command (test_network_operator.NetworkOperatorTests.test_pause_before_host_admission_suppresses_stage_and_socket_command)", + "test_pause_between_outer_loop_check_and_step_admission_starts_nothing (test_operator_runtime.OperatorTests.test_pause_between_outer_loop_check_and_step_admission_starts_nothing)", + "test_pause_during_fresh_admission_proof_prevents_lease_and_command (test_network_operator.NetworkOperatorTests.test_pause_during_fresh_admission_proof_prevents_lease_and_command)", + "test_pause_during_model_prevents_late_reply_dispatch (test_operator_runtime.OperatorTests.test_pause_during_model_prevents_late_reply_dispatch)", + "test_pause_during_model_withholds_unadmitted_decision (test_network_operator.NetworkOperatorTests.test_pause_during_model_withholds_unadmitted_decision)", + "test_pause_wins_while_resume_waits_for_actual_socket_status (test_network_operator.NetworkOperatorTests.test_pause_wins_while_resume_waits_for_actual_socket_status)", + "test_planner_boundary_requires_closure_before_fresh_explicit_resume (test_workbench_recovery_trials.RecoveryTrialTests.test_planner_boundary_requires_closure_before_fresh_explicit_resume)", + "test_post_bytes_identity_headers_immediate_and_no_retry (test_workbench_health.HealthHarnessTests.test_post_bytes_identity_headers_immediate_and_no_retry)", + "test_probe_annotation_cannot_change_effect_or_image_routes (test_workbench_health.HealthHarnessTests.test_probe_annotation_cannot_change_effect_or_image_routes)", + "test_process_absence_uses_start_identity_and_checks_whole_group (test_workbench_recovery_trials.RecoveryTrialTests.test_process_absence_uses_start_identity_and_checks_whole_group)", + "test_publication_gate_binds_retained_original_stage_and_durable_event (test_workbench_recovery_trials.RecoveryTrialTests.test_publication_gate_binds_retained_original_stage_and_durable_event)", + "test_publication_tail_sigkill_repairs_only_audit_index_without_replay (test_workbench_crash_runtime.WorkbenchCrashRuntimeTests.test_publication_tail_sigkill_repairs_only_audit_index_without_replay)", + "test_queue_validation_duplicate_and_path_schema (test_operator_runtime.OperatorTests.test_queue_validation_duplicate_and_path_schema)", + "test_read_only_recovery_requires_indexed_hash_verified_lower_receipt (test_body_network.BodyNetworkTests.test_read_only_recovery_requires_indexed_hash_verified_lower_receipt)", + "test_receipt_after_resume_is_refused (test_score_workbench_crashes.CrashScorerTests.test_receipt_after_resume_is_refused)", + "test_receipt_recovery_keeps_original_token_while_clearing_pending (test_workbench_recovery_trials.RecoveryTrialTests.test_receipt_recovery_keeps_original_token_while_clearing_pending)", + "test_reparented_detached_writer_cannot_be_inferred_to_have_exited (test_workbench_recovery_trials.RecoveryTrialTests.test_reparented_detached_writer_cannot_be_inferred_to_have_exited)", + "test_required_operation_schema_refuses_before_send_or_journal (test_body_contract.BodyContractTests.test_required_operation_schema_refuses_before_send_or_journal)", + "test_reservation_owner_conflict_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_reservation_owner_conflict_refuses)", + "test_restart_recovers_exact_queue_stage_once_from_historical_receipt (test_network_operator.NetworkOperatorTests.test_restart_recovers_exact_queue_stage_once_from_historical_receipt)", + "test_restart_retains_body_and_queue_without_replay (test_operator_runtime.OperatorTests.test_restart_retains_body_and_queue_without_replay)", + "test_reuses_exact_prior_wire_relay_and_indexed_command (test_workbench_recovery_trials.RecoveryTrialTests.test_reuses_exact_prior_wire_relay_and_indexed_command)", + "test_rotating_lease_and_exclusive_client_holder (test_body_client.BodyClientTests.test_rotating_lease_and_exclusive_client_holder)", + "test_shared_envelope_refuses_wrong_identity_epoch_state_and_schema (test_body_contract.BodyContractTests.test_shared_envelope_refuses_wrong_identity_epoch_state_and_schema)", + "test_shared_transport_import_has_no_simulator_dependency (test_body_contract.BodyContractTests.test_shared_transport_import_has_no_simulator_dependency)", + "test_shutdown_cancelled_model_exit_persists_stopped_after_worker_exits (test_operator_runtime.OperatorTests.test_shutdown_cancelled_model_exit_persists_stopped_after_worker_exits)", + "test_sigkill_host_after_known_body_effect_recovers_exact_receipt_without_replay (test_workbench_crash_runtime.WorkbenchCrashRuntimeTests.test_sigkill_host_after_known_body_effect_recovers_exact_receipt_without_replay)", + "test_sigkill_host_with_full_nonreading_stdin_retires_exact_owned_group (test_planner_guard.PlannerBackpressureTests.test_sigkill_host_with_full_nonreading_stdin_retires_exact_owned_group)", + "test_source_change_refuses_mutation (test_operator_runtime.OperatorTests.test_source_change_refuses_mutation)", + "test_spawned_planner_without_descendant_proof_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_spawned_planner_without_descendant_proof_refuses)", + "test_stale_health_timeout_after_known_socket_dispatch_does_not_pause (test_network_operator.NetworkOperatorTests.test_stale_health_timeout_after_known_socket_dispatch_does_not_pause)", + "test_stale_observation_is_retained_known_dispatch_refusal (test_body_network.BodyNetworkTests.test_stale_observation_is_retained_known_dispatch_refusal)", + "test_state_and_frame_are_responsive_while_transport_pending (test_network_operator.NetworkOperatorTests.test_state_and_frame_are_responsive_while_transport_pending)", + "test_stop_decision_pauses_without_completion (test_operator_runtime.OperatorTests.test_stop_decision_pauses_without_completion)", + "test_symlink_event_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_symlink_event_refuses)", + "test_tagged_stale_foreign_or_unbound_proof_fails (test_workbench_health.AdmissionScorerTests.test_tagged_stale_foreign_or_unbound_proof_fails)", + "test_terminal_envelope_rejects_digest_identity_and_epoch_substitution (test_score_workbench_crashes.CrashScorerTests.test_terminal_envelope_rejects_digest_identity_and_epoch_substitution)", + "test_terminal_pointer_persist_failure_keeps_cache_and_disk_unknown (test_body_network.BodyNetworkTests.test_terminal_pointer_persist_failure_keeps_cache_and_disk_unknown)", + "test_truncated_or_unbound_leader_descendant_proof_refuses (test_workbench_recovery_guards.RecoveryGuardTests.test_truncated_or_unbound_leader_descendant_proof_refuses)", + "test_two_jobs_replay_public_action_fixture_and_disturbance_guard (test_operator_runtime.OperatorTests.test_two_jobs_replay_public_action_fixture_and_disturbance_guard)", + "test_two_successive_jobs_and_disturbance_using_declared_replay (test_network_operator.NetworkOperatorTests.test_two_successive_jobs_and_disturbance_using_declared_replay)", + "test_unbound_error_after_native_success_stays_pending (test_body_client.BodyClientTests.test_unbound_error_after_native_success_stays_pending)", + "test_unbound_historical_receipt_does_not_release_unknown_command (test_body_contract.BodyContractTests.test_unbound_historical_receipt_does_not_release_unknown_command)", + "test_unclosed_guardian_and_replayed_command_are_refused (test_workbench_recovery_trials.RecoveryTrialTests.test_unclosed_guardian_and_replayed_command_are_refused)", + "test_unknown_dispatch_survives_body_epoch_then_exact_recovery_and_resume (test_network_operator.NetworkOperatorTests.test_unknown_dispatch_survives_body_epoch_then_exact_recovery_and_resume)", + "test_unknown_lower_proof_blocks_completed_outer_receipt_until_exact_proof (test_body_contract.BodyContractTests.test_unknown_lower_proof_blocks_completed_outer_receipt_until_exact_proof)", + "test_unknown_native_blocks_resume_and_no_reset (test_operator_runtime.OperatorTests.test_unknown_native_blocks_resume_and_no_reset)", + "test_unknown_request_corruption_cannot_clear_or_resend (test_body_client.BodyClientTests.test_unknown_request_corruption_cannot_clear_or_resend)", + "test_unknown_start_observe_and_proposal_settle_without_replay_or_old_decision (test_network_operator.NetworkOperatorTests.test_unknown_start_observe_and_proposal_settle_without_replay_or_old_decision)", + "test_usage_optional_and_any_tool_event_refuses (test_codex_policy.PolicyTests.test_usage_optional_and_any_tool_event_refuses)", + "test_wrong_command_or_sequence_refuses_diagnostic (test_workbench_recovery_trials.RecoveryTrialTests.test_wrong_command_or_sequence_refuses_diagnostic)" +] diff --git a/jevdual/results/reusable-body-recovery-20261012/checks/validation-attempts.json b/jevdual/results/reusable-body-recovery-20261012/checks/validation-attempts.json new file mode 100644 index 0000000..edf60b7 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/checks/validation-attempts.json @@ -0,0 +1,12 @@ +{ + "corrected_invocations": [ + { + "check": "initial root Ruff path", + "reason": "Used two parent directories from jevdual instead of one; corrected to absolute paths" + }, + { + "check": "archived helper snapshot", + "reason": "Standalone Ruff found import order in exact handoff source snapshot; production/test source scope passed separately. Archived helper preserves executed source bytes." + } + ] +} diff --git a/jevdual/results/reusable-body-recovery-20261012/checks/verification.json b/jevdual/results/reusable-body-recovery-20261012/checks/verification.json new file mode 100644 index 0000000..eafe87a --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/checks/verification.json @@ -0,0 +1,9 @@ +{ + "ruff": "passed", + "notebook": "passed", + "redaction_changed_files": 0, + "diff_check": "passed", + "source_commit": "c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54", + "ruff_scope": "Changed production, diagnostic and test sources; archived byte-for-byte handoff helper snapshot excluded", + "archived_handoff_snapshot_lint": "Import ordering advisory only; snapshot preserves the running helper hash" +} diff --git a/jevdual/results/reusable-body-recovery-20261012/handoff-forwarder.py b/jevdual/results/reusable-body-recovery-20261012/handoff-forwarder.py new file mode 100644 index 0000000..416e9e5 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/handoff-forwarder.py @@ -0,0 +1,103 @@ +"""Handoff-only loopback forwarding: no faults, response cache or automatic retries.""" +import json +import http.client +import signal +import sys +import threading +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from urllib.parse import urlsplit + +sys.path.insert(0, str(Path(__file__).resolve().parents[2])) +import body_client +import order_protocol as protocol + +config = json.loads((Path(__file__).parent / "config.json").read_text()) +origin = config["relay_origin"] +stopped = threading.Event() + + +class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def log_message(self, *_): + pass + + def forward(self): + self.connection.settimeout(15) + try: + route = urlsplit(self.path) + if (self.headers.get_all("Host") != [urlsplit(origin).netloc] or + self.headers.get_all("Origin") or self.headers.get_all("Transfer-Encoding") or + route.scheme or route.netloc or route.fragment or len(self.path) > 8192): + raise protocol.Refusal("closed loopback framing required") + if (self.command == "GET" and not (route.path in ("/identity", "/state") or + route.path.startswith(("/commands/", "/images/"))) or + self.command == "POST" and self.path not in ("/lease", "/commands")): + raise protocol.Refusal("unsupported body route") + lengths = self.headers.get_all("Content-Length", []) + if len(lengths) > 1 or lengths and (not lengths[0].isascii() or not lengths[0].isdigit()): + raise protocol.Refusal("invalid content length") + length = int(lengths[0]) if lengths else 0 + if (length > protocol.MAX_BYTES or self.command == "GET" and length or + self.command == "POST" and (not length or self.headers.get_all("Content-Type") != ["application/json"])): + raise protocol.Refusal("bounded JSON framing required") + payload = self.rfile.read(length) + if len(payload) != length: + raise protocol.Refusal("incomplete body") + headers = {k: v for k, v in self.headers.items() if k.lower() not in ("host", "connection", "content-length")} + code, data = body_client.http_call(config["body_origin"], self.command, self.path, headers, + payload if self.command == "POST" else None, timeout=15, maximum=body_client.MAX_IMAGE) + mime = "image/png" if route.path.startswith("/images/") else "application/json" + except (OSError, http.client.HTTPException, ValueError, protocol.Refusal): + code, data, mime = 502, protocol.encoded({"error": "Transport unavailable; request was not retried"}), "application/json" + self.send_response(code) + self.send_header("Content-Type", mime) + self.send_header("Content-Length", str(len(data))) + self.send_header("Cache-Control", "no-store") + self.send_header("Connection", "close") + self.end_headers() + self.wfile.write(data) + self.close_connection = True + + do_GET = forward + do_POST = forward + + +class Server(ThreadingHTTPServer): + daemon_threads = False + block_on_close = True + + def __init__(self, address): + self.capacity = threading.BoundedSemaphore(8) + super().__init__(address, Handler) + + def process_request(self, request, address): + if not self.capacity.acquire(False): + self.shutdown_request(request) + return + try: + super().process_request(request, address) + except BaseException: + self.capacity.release() + raise + + def process_request_thread(self, request, address): + try: + super().process_request_thread(request, address) + finally: + self.capacity.release() + + +server = Server(("127.0.0.1", urlsplit(origin).port)) +signal.signal(signal.SIGTERM, lambda *_: stopped.set()) +signal.signal(signal.SIGINT, lambda *_: stopped.set()) +thread = threading.Thread(target=server.serve_forever, kwargs={"poll_interval": .1}) +thread.start() +print(json.dumps({"origin": origin, "upstream": config["body_origin"], "faults": False, "retries": 0}), flush=True) +try: + stopped.wait() +finally: + server.shutdown() + thread.join() + server.server_close() diff --git a/jevdual/results/reusable-body-recovery-20261012/handoff.json b/jevdual/results/reusable-body-recovery-20261012/handoff.json new file mode 100644 index 0000000..ce77266 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/handoff.json @@ -0,0 +1,339 @@ +{ + "browser_origin": "http://127.0.0.1:51419", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2", + "processes": [ + { + "role": "body", + "pid": 2745, + "group": 2745, + "arguments": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/body_network.py", + "--root", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/body", + "--port", + "51417" + ] + }, + { + "role": "relay", + "pid": 2746, + "group": 2746, + "arguments": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-handoff/forwarder.py" + ] + }, + { + "role": "workbench", + "pid": 2754, + "group": 2754, + "arguments": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/network_bench_operator.py", + "serve", + "--root", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/operator", + "--port", + "51419", + "--backend", + "codex" + ] + } + ], + "source_commit": "c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54", + "qualified_before_handoff": true, + "state": { + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "simulation": true, + "runner_status": "paused", + "jobs": [ + { + "call_count": 15, + "disturbance_test": false, + "error": null, + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "label": "Retire planner and recover reply", + "last_intent": "Finish reset now that the block is centered in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "d2820395d03d485492e1225f1972a12d", + "block_home_verified": true, + "generation": 11, + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "kind": "finish_reset", + "native_time_s": 22.94799999999816, + "operation_id": "agent-e8c188b67ec74303916d4a975c30c34e", + "pusher_home_verified": true, + "reset_contact_samples": 3132, + "reset_native_steps": 8206, + "state_sha256": "31f1e85639b1f4712ff98a8b2c3158de32f0e08e3204141921cfac1648fe0b6e" + }, + "status": "completed", + "target": "A" + } + ], + "active_job_id": null, + "model_request_pending": false, + "transport_pending": false, + "pending_operation": null, + "activity": [ + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "dispatch: command-eca41d65274f4d50a46869065ea7bad5", + "seq": 134, + "time": 1791860859.371604 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "d291139d0c6ba08c36f28acac8990f595344de8d206c27712639b5b5af8b1eb9" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 135, + "time": 1791860859.427273 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 136, + "time": 1791860859.468482 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "observe: command-65e5701648ce496aa2a1433078aabc7f", + "seq": 137, + "time": 1791860859.645217 + }, + { + "intent": "Retire planner and recover reply", + "kind": "inference", + "outcome": "14", + "seq": 138, + "time": 1791860859.646932 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "94bb8b8563324b4d327c8070a8e1cf99c5a133b5a104fd99fd3c2942c9d8e71a" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 139, + "time": 1791860867.169062 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 140, + "time": 1791860867.212686 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "propose: command-96098171ac4447cf9cb6a7b2facb798d", + "seq": 141, + "time": 1791860867.295264 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "6fed8287d24fddf7219a4010d559602cf9635bb8478a086949e4e3b3ce1676cd" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 142, + "time": 1791860867.361056 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 143, + "time": 1791860867.4049459 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "dispatch: command-3d83235444f841cdbba5d555c9d5246d", + "seq": 144, + "time": 1791860867.637874 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "510775598cb2b10ed20272d4daadd242ac0e590c38dd52fbfddf9c5381986ecc" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 145, + "time": 1791860867.700633 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 146, + "time": 1791860867.7447581 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "observe: command-047b4fe7d49c425c84abf5b3840b8f46", + "seq": 147, + "time": 1791860867.9181042 + }, + { + "intent": "Retire planner and recover reply", + "kind": "inference", + "outcome": "15", + "seq": 148, + "time": 1791860867.9198852 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "d96a40d00cc5f53d52e520d2589d9974be0c73d045b8b23e49d9e8faddb12074" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 149, + "time": 1791860874.962473 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 150, + "time": 1791860875.005901 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "propose: command-4f154fdba3aa4493846a014f77143055", + "seq": 151, + "time": 1791860875.085974 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "357b8fa432c119b488a0055f0626a482438a04263bc4c3849910d503196533c3" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 152, + "time": 1791860875.1431081 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 153, + "time": 1791860875.183298 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "dispatch: command-8ef12068ba8a4f199cd3e55144180692", + "seq": 154, + "time": 1791860875.4448261 + }, + { + "seq": 155, + "time": 1791861863.539073, + "kind": "planner_retired", + "intent": "", + "outcome": "Prior planner retired; explicit Resume obtains a fresh image", + "evidence": { + "calls": [ + { + "call": "0bc391fc-81d4-4d6a-a267-965b153f8cab-01", + "reason": "host liveness pipe closed" + } + ] + } + }, + { + "seq": 156, + "time": 1791861863.599765, + "kind": "halt", + "intent": "", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image" + }, + { + "seq": 157, + "time": 1791861863.6009989, + "kind": "body_epoch_changed", + "intent": "", + "outcome": "1 -> 2" + } + ], + "usage": { + "calls": 15, + "input_tokens": 183745, + "output_tokens": 2157, + "reported_calls": 14, + "token_usage_complete": false + }, + "readiness": { + "ready": false, + "reason": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image" + }, + "current_frame": { + "available": true, + "generation": 11, + "image_url": "/api/frame?generation=11&sha256=970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f" + }, + "connection": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 2, + "fault": null, + "reason": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "state": "connected" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + } + }, + "native_manifest_sha256": "3faaeb3e05fc65ab981b12a207127dda8d83bcd0fde25d522fba913dd099b9f1", + "commands": 44, + "replayed_actions": 0, + "forwarder_sha256": "00b888539b9f313c76f53e2984bf1136f8469f90ed097be8d432eee32935c1e8", + "relay_scope": "No faults, cache, automatic retries, health-log budget or trial deadline; bounded loopback forwarding only" +} diff --git a/jevdual/results/reusable-body-recovery-20261012/handoff.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/handoff.ledger.txt new file mode 100644 index 0000000..b80bc5e --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/handoff.ledger.txt @@ -0,0 +1,47 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 checkbox Retire planner and recover reply Completed, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 14 text Retire planner and recover reply + 15 text Paused + 16 button Resume, ID: pause + 17 container Description: Body connection, ID: network-card + 18 text Body connected + 19 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 22 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 23 text Test the simulated connection + 24 container Camera observation + 25 text OVERHEAD CAMERA + 26 text Last camera observation + 27 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 29 text Captured · generation 11 + 30 text COMPLETED + 31 text Measurement recorded and reset verified. The bench is ready for another job. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 37 text Historical result + 38 container measurement-result + 39 text 1,726 + 40 text Orange pixels + 41 container + 42 text 4,794.4 + 43 text mm² + 44 text Apparent footprint + 45 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 46 button Repeat job, ID: repeat \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/handoff.png b/jevdual/results/reusable-body-recovery-20261012/handoff.png new file mode 100644 index 0000000..ecc7b31 Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/handoff.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/handoff.txt b/jevdual/results/reusable-body-recovery-20261012/handoff.txt new file mode 100644 index 0000000..9eb5002 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/handoff.txt @@ -0,0 +1,81 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 checkbox Retire planner and recover reply Completed, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 14 text Retire planner and recover reply + 15 text Paused + 16 button Resume, ID: pause + 17 container Description: Body connection, ID: network-card + 18 text Body connected + 19 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 22 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 23 text Test the simulated connection + 24 container Camera observation + 25 text OVERHEAD CAMERA + 26 text Last camera observation + 27 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 29 text Captured · generation 11 + 30 text COMPLETED + 31 text Measurement recorded and reset verified. The bench is ready for another job. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 37 text Historical result + 38 container measurement-result + 39 text 1,726 + 40 text Orange pixels + 41 container + 42 text 4,794.4 + 43 text mm² + 44 text Apparent footprint + 45 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 46 button Repeat job, ID: repeat + 47 container Activity + 48 heading Activity, Value: 2, ID: activity-heading + 49 text Activity + 50 text 24 events + 51 content list activity + 52 container + 53 text 11:24 PM Body restarted 1 -> 2 + 54 container + 55 text 11:24 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 56 container + 57 text 11:24 PM Previous planner retired The previous planner has stopped. Resume to plan from a fresh camera image. + 58 container + 59 text 11:07 PM Action receipt checked Retire planner and recover reply + 60 container + 61 text 11:07 PM Sending a guarded action + 62 container + 63 text 11:07 PM Fresh body check verified The next command uses current body evidence. + 64 container + 65 text 11:07 PM Action proposal accepted Retire planner and recover reply + 66 container + 67 text 11:07 PM Checking the planned action + 68 container + 69 text 11:07 PM Fresh body check verified The next command uses current body evidence. + 70 container + 71 text 11:07 PM AI planning requested Retire planner and recover reply + 72 container + 73 text 11:07 PM Camera observation received Retire planner and recover reply + 74 container + 75 text 11:07 PM Requesting camera observation + 76 button (collapsed) Session details, Secondary Actions: Expand + 77 text Session details + 78 text 15 model requests · Reported tokens: 183,745 input / 2,157 output · Reporting incomplete (14 of 15 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/crash-ledger.json b/jevdual/results/reusable-body-recovery-20261012/native-gap/crash-ledger.json new file mode 100644 index 0000000..5292e52 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/crash-ledger.json @@ -0,0 +1,50 @@ +{ + "boundaries": [ + { + "case": "body-native-gap", + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "finished_snapshot": "000004-finished.json", + "gate": { + "barrier": "after_native", + "bench_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "diagnostic_source_sha256": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "manifest_generation": 0, + "native_generation": 1, + "native_state_sha256": "3dd31d583a8b7ec3f4a7fa51f403baa50fda8f9839a705a1787505c87464e425", + "native_steps": 1550, + "native_time_s": 4.0999999999997705, + "operation_id": "agent-f38f26f75d5948c98118fc8c65842276", + "pid": 53902, + "proposal_id": "proposal-28a486115a0242468ba8651ede499eb6", + "pusher_block_contact_samples": 3164, + "request_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "version": 1 + }, + "new_epoch": 2, + "offline_snapshot": "000002-offline.json", + "old_epoch": 1, + "pre_snapshot": "000001-before.json", + "process": { + "boot": 1, + "children_after": [], + "children_before": [], + "exit_observed": true, + "group": 53902, + "initialized": true, + "log": "body-01.log", + "pid": 53902, + "port": 51423, + "returncode": -9, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/body", + "signal": "SIGKILL", + "stopped_time": 1791860466.394902 + }, + "request_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "restored_snapshot": "000003-restored.json", + "role": "body" + } + ], + "case": "body-native-gap" +} diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/final-public.json b/jevdual/results/reusable-body-recovery-20261012/native-gap/final-public.json new file mode 100644 index 0000000..6263a4d --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/final-public.json @@ -0,0 +1,217 @@ +{ + "active_job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "activity": [ + { + "intent": "Missing native receipt stays blocked", + "kind": "queued", + "outcome": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "seq": 1, + "time": 1791860458.630235 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "4dc75e6883288bfe4e51f1e294b4b8038c3615b279b0a9f4a56db5c0ad416c97" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "start_job", + "seq": 2, + "time": 1791860458.641203 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 3, + "time": 1791860458.654922 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "start_job: command-7c7f1616df1245399a248717c004c424", + "seq": 4, + "time": 1791860458.670542 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "25fe15ee4cbe707cc7b0b6c17cad92924f2d036f63ebced3d77f032e64d845d4" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 5, + "time": 1791860458.679942 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 6, + "time": 1791860458.692619 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "observe: command-58f43389dab14ea1af8f011dcac3b415", + "seq": 7, + "time": 1791860458.866333 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "inference", + "outcome": "1", + "seq": 8, + "time": 1791860458.8678331 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "8a574b41e61612e1c3e1485e09bfc9ee7e16ad5fb7b4c54b51ec9de900d52995" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 9, + "time": 1791860466.171602 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 10, + "time": 1791860466.1831439 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "propose: command-19630ef4ff684508b4053bffd3261aca", + "seq": 11, + "time": 1791860466.199779 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "e5c43221ed1c25504133986f98a2f894eb2f6b38e9e095b8f3ed209f9b10e859" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 12, + "time": 1791860466.209346 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 13, + "time": 1791860466.2203572 + }, + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 14, + "time": 1791860466.3972118 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 15, + "time": 1791860467.0656672 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "seq": 16, + "time": 1791860545.814935 + }, + { + "intent": "", + "kind": "body_epoch_changed", + "outcome": "1 -> 2", + "seq": 17, + "time": 1791860545.815804 + }, + { + "intent": "", + "kind": "halt", + "outcome": "accepted intent has no verified terminal proof", + "seq": 18, + "time": 1791860885.3836958 + }, + { + "intent": "", + "kind": "halt", + "outcome": "accepted intent has no verified terminal proof", + "seq": 19, + "time": 1791860982.512386 + } + ], + "app_id": "536221822098429cac997e554389c561", + "connection": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 2, + "fault": null, + "reason": "Independent body has unresolved effects", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "accepted intent has no verified terminal proof", + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "label": "Missing native receipt stays blocked", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "accepted intent has no verified terminal proof" + }, + "recovery": { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "operation": "dispatch", + "reason": "accepted intent has no verified terminal proof", + "state": "uncertain" + }, + "runner_status": "blocked", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 1, + "input_tokens": 12607, + "output_tokens": 194, + "reported_calls": 1, + "token_usage_complete": true + } +} diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/hard-crash-score.json b/jevdual/results/reusable-body-recovery-20261012/native-gap/hard-crash-score.json new file mode 100644 index 0000000..dd055e7 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/hard-crash-score.json @@ -0,0 +1,143 @@ +{ + "admissions": [ + { + "command_id": "command-7c7f1616df1245399a248717c004c424", + "passed": true, + "proof_event": 2 + }, + { + "command_id": "command-58f43389dab14ea1af8f011dcac3b415", + "passed": true, + "proof_event": 5 + }, + { + "command_id": "command-19630ef4ff684508b4053bffd3261aca", + "passed": true, + "proof_event": 9 + }, + { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "passed": true, + "proof_event": 12 + } + ], + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "browser": { + "passed": true, + "required_names": [ + "crash-blocked", + "crash-offline", + "crash-restored", + "crash-still-blocked" + ] + }, + "calls": [ + { + "action": "move", + "call": 1, + "camera_generation": 0, + "challenge_id": "challenge-c4a2aac3e0e44153bb8a38ba9a847b50", + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "model_pid": 62581, + "native_gap": true, + "passed": true, + "proposal_id": "proposal-28a486115a0242468ba8651ede499eb6" + } + ], + "case": "body-native-gap", + "completed_cycles": 0, + "crash": { + "case": "body-native-gap", + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "new_epoch": 2, + "old_epoch": 1, + "passed": true, + "reconciled_sequence": null, + "request_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "restored_sequence": 17, + "resume_sequence": null, + "role": "body", + "snapshot_command_count": 4 + }, + "event_cutoff": 19, + "native": { + "after": { + "block_center_in_zone": false, + "block_xyz": [ + 0.06581381720865363, + -8.39496500218506e-11, + 0.03499805666564847 + ], + "external_force_cleared": true, + "generation": 1, + "native_time_s": 4.0999999999997705, + "pusher_home": false, + "pusher_xyz": [ + 0.010816304329139825, + 6.653769644544919e-18, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "3dd31d583a8b7ec3f4a7fa51f403baa50fda8f9839a705a1787505c87464e425" + }, + "before": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11999998822184511, + 1.630059551357049e-11, + 0.03499573045537004 + ], + "external_force_cleared": true, + "generation": 0, + "native_time_s": 1.0000000000000007, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 0.0, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "3a8c7d1b2c047630a067a0590f939c066b6c621349b6021d26609c85b2e4644b" + }, + "marker": { + "barrier": "after_native", + "bench_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "diagnostic_source_sha256": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "manifest_generation": 0, + "native_generation": 1, + "native_state_sha256": "3dd31d583a8b7ec3f4a7fa51f403baa50fda8f9839a705a1787505c87464e425", + "native_steps": 1550, + "native_time_s": 4.0999999999997705, + "operation_id": "agent-f38f26f75d5948c98118fc8c65842276", + "pid": 53902, + "proposal_id": "proposal-28a486115a0242468ba8651ede499eb6", + "pusher_block_contact_samples": 3164, + "request_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "version": 1 + }, + "native_displacement_m": 0.3108163043291398, + "passed": true, + "reason": "Advanced native state without indexed terminal proof remains unknown", + "recovered": false, + "safely_blocked": true + }, + "owned_processes_exited": true, + "passed": true, + "safely_blocked": true, + "scope": "selected process SIGKILL boundaries; actual image-only Codex browser; loopback simulation; no arbitrary power-loss guarantee", + "single_wire_post_every_command": true, + "source_commit": "c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54", + "source_unchanged": true, + "sqlite": { + "count": 0, + "database_created": false, + "reason": "Native gap occurred before the first computer measurement", + "role": "independent_read_only_sqlite_scorer", + "rows": [] + }, + "transport_command_count": 4, + "version": 1 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/process-ledger.json b/jevdual/results/reusable-body-recovery-20261012/native-gap/process-ledger.json new file mode 100644 index 0000000..8227311 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/process-ledger.json @@ -0,0 +1,63 @@ +{ + "app_id": "536221822098429cac997e554389c561", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "body_origin": "http://127.0.0.1:51423", + "browser_origin": "http://127.0.0.1:51425", + "case": "body-native-gap", + "owned_processes_exited": true, + "planner": "actual_codex_cli_images_over_TCP", + "processes": [ + { + "boot": 1, + "children_after": [], + "children_before": [], + "exit_observed": true, + "group": 53902, + "initialized": true, + "log": "body-01.log", + "pid": 53902, + "port": 51423, + "returncode": -9, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/body", + "signal": "SIGKILL", + "stopped_time": 1791860466.394902 + }, + { + "exit_observed": true, + "group": 53986, + "pid": 53986, + "returncode": 0, + "role": "initialization" + }, + { + "boot": 1, + "exit_observed": true, + "group": 54014, + "initialized": false, + "log": "workbench-01.log", + "pid": 54014, + "port": 51425, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/operator", + "stopped_time": 1791861031.334891 + }, + { + "boot": 2, + "exit_observed": true, + "group": 64365, + "initialized": false, + "log": "body-02.log", + "pid": 64365, + "port": 51423, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/body", + "stopped_time": 1791861031.45898 + } + ], + "ready": true, + "simulation": true, + "source_commit": "c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54" +} diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/snapshots/000001-before.json b/jevdual/results/reusable-body-recovery-20261012/native-gap/snapshots/000001-before.json new file mode 100644 index 0000000..5e8bf35 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/snapshots/000001-before.json @@ -0,0 +1,329 @@ +{ + "body_command": { + "digest_valid": true, + "intent": { + "request": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "arguments": { + "proposal_id": "proposal-28a486115a0242468ba8651ede499eb6" + }, + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "client_id": "client-99cf4b51858b41f49568551e639d3311", + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "epoch": 1, + "lease_id": "cf9403073fc842869dd638f8403d963b", + "operation": "dispatch", + "protocol": "footwork.body/1", + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "request_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "result_sha256": null + }, + "terminal": null + }, + "body_identity": null, + "body_state": null, + "client": { + "commands": { + "command-19630ef4ff684508b4053bffd3261aca": { + "operation": "propose", + "request_sha256": "cb7928d05b419594331d63a3bef90d42e440e4a3ef6b9623a6d5c4939231f429", + "send_count": 1, + "state": "completed" + }, + "command-20204cceb73b473eb66857af0b813c1d": { + "operation": "dispatch", + "request_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "send_count": 1, + "state": "unknown" + }, + "command-58f43389dab14ea1af8f011dcac3b415": { + "operation": "observe", + "request_sha256": "4ce1073576807cdceabb307c865c5a51f4a5a2577ddba3ad19d24332c3b4f894", + "send_count": 1, + "state": "completed" + }, + "command-7c7f1616df1245399a248717c004c424": { + "operation": "start_job", + "request_sha256": "5e674b785e9d70d9894c809ba1bdf202cc84bdbed725e4175cf32d38bad96d8a", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-99cf4b51858b41f49568551e639d3311", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "cf9403073fc842869dd638f8403d963b" + }, + "pending_command_ids": [ + "command-20204cceb73b473eb66857af0b813c1d" + ] + }, + "host": { + "app_id": "536221822098429cac997e554389c561", + "body_identity": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/operator/client/images/challenge-c4a2aac3e0e44153bb8a38ba9a847b50-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "runner_status": "running", + "sequence": 13, + "stage": { + "arguments": { + "proposal_id": "proposal-28a486115a0242468ba8651ede499eb6" + }, + "before_commands": [ + "command-19630ef4ff684508b4053bffd3261aca", + "command-58f43389dab14ea1af8f011dcac3b415", + "command-7c7f1616df1245399a248717c004c424" + ], + "command_id": null, + "context": { + "action": { + "action": "move", + "intent": "Push the block right so its center reaches the measurement region.", + "target_x": 0.015, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/operator/calls/f86aaa43-84f4-4074-98d1-5b2a52c98280-01", + "decision_epoch": 0, + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280" + }, + "control_epoch": 0, + "operation": "dispatch", + "proof": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "e5c43221ed1c25504133986f98a2f894eb2f6b38e9e095b8f3ed209f9b10e859" + } + }, + "usage": { + "calls": 1, + "input_tokens": 12607, + "output_tokens": 194, + "reported_calls": 1 + } + }, + "label": "before", + "posts": [ + { + "command_id": "command-7c7f1616df1245399a248717c004c424", + "payload_sha256": "5e674b785e9d70d9894c809ba1bdf202cc84bdbed725e4175cf32d38bad96d8a", + "sequence": 8 + }, + { + "command_id": "command-58f43389dab14ea1af8f011dcac3b415", + "payload_sha256": "4ce1073576807cdceabb307c865c5a51f4a5a2577ddba3ad19d24332c3b4f894", + "sequence": 16 + }, + { + "command_id": "command-19630ef4ff684508b4053bffd3261aca", + "payload_sha256": "cb7928d05b419594331d63a3bef90d42e440e4a3ef6b9623a6d5c4939231f429", + "sequence": 28 + }, + { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "payload_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "sequence": 36 + } + ], + "processes": { + "body": 53902, + "workbench": 54014 + }, + "public": { + "active_job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "activity": [ + { + "intent": "Missing native receipt stays blocked", + "kind": "queued", + "outcome": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "seq": 1, + "time": 1791860458.630235 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "4dc75e6883288bfe4e51f1e294b4b8038c3615b279b0a9f4a56db5c0ad416c97" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "start_job", + "seq": 2, + "time": 1791860458.641203 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 3, + "time": 1791860458.654922 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "start_job: command-7c7f1616df1245399a248717c004c424", + "seq": 4, + "time": 1791860458.670542 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "25fe15ee4cbe707cc7b0b6c17cad92924f2d036f63ebced3d77f032e64d845d4" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 5, + "time": 1791860458.679942 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 6, + "time": 1791860458.692619 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "observe: command-58f43389dab14ea1af8f011dcac3b415", + "seq": 7, + "time": 1791860458.866333 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "inference", + "outcome": "1", + "seq": 8, + "time": 1791860458.8678331 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "8a574b41e61612e1c3e1485e09bfc9ee7e16ad5fb7b4c54b51ec9de900d52995" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 9, + "time": 1791860466.171602 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 10, + "time": 1791860466.1831439 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "propose: command-19630ef4ff684508b4053bffd3261aca", + "seq": 11, + "time": 1791860466.199779 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "e5c43221ed1c25504133986f98a2f894eb2f6b38e9e095b8f3ed209f9b10e859" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 12, + "time": 1791860466.209346 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 13, + "time": 1791860466.2203572 + } + ], + "app_id": "536221822098429cac997e554389c561", + "connection": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "fault": null, + "reason": "Fresh pinned ready proof verified before command admission", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": null, + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "label": "Missing native receipt stays blocked", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": true, + "reason": "Ready for a network inspection" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No uncertain command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": true, + "usage": { + "calls": 1, + "input_tokens": 12607, + "output_tokens": 194, + "reported_calls": 1, + "token_usage_complete": true + } + }, + "time": 1791860466.34336 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/snapshots/000002-offline.json b/jevdual/results/reusable-body-recovery-20261012/native-gap/snapshots/000002-offline.json new file mode 100644 index 0000000..726e65f --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/snapshots/000002-offline.json @@ -0,0 +1,336 @@ +{ + "body_command": { + "digest_valid": true, + "intent": { + "request": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "arguments": { + "proposal_id": "proposal-28a486115a0242468ba8651ede499eb6" + }, + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "client_id": "client-99cf4b51858b41f49568551e639d3311", + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "epoch": 1, + "lease_id": "cf9403073fc842869dd638f8403d963b", + "operation": "dispatch", + "protocol": "footwork.body/1", + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "request_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "result_sha256": null + }, + "terminal": null + }, + "body_identity": null, + "body_state": null, + "client": { + "commands": { + "command-19630ef4ff684508b4053bffd3261aca": { + "operation": "propose", + "request_sha256": "cb7928d05b419594331d63a3bef90d42e440e4a3ef6b9623a6d5c4939231f429", + "send_count": 1, + "state": "completed" + }, + "command-20204cceb73b473eb66857af0b813c1d": { + "operation": "dispatch", + "request_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "send_count": 1, + "state": "unknown" + }, + "command-58f43389dab14ea1af8f011dcac3b415": { + "operation": "observe", + "request_sha256": "4ce1073576807cdceabb307c865c5a51f4a5a2577ddba3ad19d24332c3b4f894", + "send_count": 1, + "state": "completed" + }, + "command-7c7f1616df1245399a248717c004c424": { + "operation": "start_job", + "request_sha256": "5e674b785e9d70d9894c809ba1bdf202cc84bdbed725e4175cf32d38bad96d8a", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-99cf4b51858b41f49568551e639d3311", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "cf9403073fc842869dd638f8403d963b" + }, + "pending_command_ids": [ + "command-20204cceb73b473eb66857af0b813c1d" + ] + }, + "host": { + "app_id": "536221822098429cac997e554389c561", + "body_identity": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/operator/client/images/challenge-c4a2aac3e0e44153bb8a38ba9a847b50-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "runner_status": "blocked", + "sequence": 14, + "stage": { + "arguments": { + "proposal_id": "proposal-28a486115a0242468ba8651ede499eb6" + }, + "before_commands": [ + "command-19630ef4ff684508b4053bffd3261aca", + "command-58f43389dab14ea1af8f011dcac3b415", + "command-7c7f1616df1245399a248717c004c424" + ], + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "context": { + "action": { + "action": "move", + "intent": "Push the block right so its center reaches the measurement region.", + "target_x": 0.015, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/operator/calls/f86aaa43-84f4-4074-98d1-5b2a52c98280-01", + "decision_epoch": 0, + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280" + }, + "control_epoch": 0, + "operation": "dispatch", + "proof": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "e5c43221ed1c25504133986f98a2f894eb2f6b38e9e095b8f3ed209f9b10e859" + } + }, + "usage": { + "calls": 1, + "input_tokens": 12607, + "output_tokens": 194, + "reported_calls": 1 + } + }, + "label": "offline", + "posts": [ + { + "command_id": "command-7c7f1616df1245399a248717c004c424", + "payload_sha256": "5e674b785e9d70d9894c809ba1bdf202cc84bdbed725e4175cf32d38bad96d8a", + "sequence": 8 + }, + { + "command_id": "command-58f43389dab14ea1af8f011dcac3b415", + "payload_sha256": "4ce1073576807cdceabb307c865c5a51f4a5a2577ddba3ad19d24332c3b4f894", + "sequence": 16 + }, + { + "command_id": "command-19630ef4ff684508b4053bffd3261aca", + "payload_sha256": "cb7928d05b419594331d63a3bef90d42e440e4a3ef6b9623a6d5c4939231f429", + "sequence": 28 + }, + { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "payload_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "sequence": 36 + } + ], + "processes": { + "body": null, + "workbench": 54014 + }, + "public": { + "active_job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "activity": [ + { + "intent": "Missing native receipt stays blocked", + "kind": "queued", + "outcome": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "seq": 1, + "time": 1791860458.630235 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "4dc75e6883288bfe4e51f1e294b4b8038c3615b279b0a9f4a56db5c0ad416c97" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "start_job", + "seq": 2, + "time": 1791860458.641203 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 3, + "time": 1791860458.654922 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "start_job: command-7c7f1616df1245399a248717c004c424", + "seq": 4, + "time": 1791860458.670542 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "25fe15ee4cbe707cc7b0b6c17cad92924f2d036f63ebced3d77f032e64d845d4" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 5, + "time": 1791860458.679942 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 6, + "time": 1791860458.692619 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "observe: command-58f43389dab14ea1af8f011dcac3b415", + "seq": 7, + "time": 1791860458.866333 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "inference", + "outcome": "1", + "seq": 8, + "time": 1791860458.8678331 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "8a574b41e61612e1c3e1485e09bfc9ee7e16ad5fb7b4c54b51ec9de900d52995" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 9, + "time": 1791860466.171602 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 10, + "time": 1791860466.1831439 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "propose: command-19630ef4ff684508b4053bffd3261aca", + "seq": 11, + "time": 1791860466.199779 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "e5c43221ed1c25504133986f98a2f894eb2f6b38e9e095b8f3ed209f9b10e859" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 12, + "time": 1791860466.209346 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 13, + "time": 1791860466.2203572 + }, + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 14, + "time": 1791860466.3972118 + } + ], + "app_id": "536221822098429cac997e554389c561", + "connection": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "fault": null, + "reason": "Fresh pinned ready proof verified before command admission", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "label": "Missing native receipt stays blocked", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "reply is uncertain; reconcile exact command without resending" + }, + "recovery": { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 1, + "input_tokens": 12607, + "output_tokens": 194, + "reported_calls": 1, + "token_usage_complete": true + } + }, + "time": 1791860466.4342148 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/snapshots/000003-restored.json b/jevdual/results/reusable-body-recovery-20261012/native-gap/snapshots/000003-restored.json new file mode 100644 index 0000000..a779a65 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/snapshots/000003-restored.json @@ -0,0 +1,419 @@ +{ + "body_command": { + "digest_valid": true, + "intent": { + "request": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "arguments": { + "proposal_id": "proposal-28a486115a0242468ba8651ede499eb6" + }, + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "client_id": "client-99cf4b51858b41f49568551e639d3311", + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "epoch": 1, + "lease_id": "cf9403073fc842869dd638f8403d963b", + "operation": "dispatch", + "protocol": "footwork.body/1", + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "request_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "result_sha256": null + }, + "terminal": null + }, + "body_identity": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "moves": 0, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "unknown", + "unresolved_effect": { + "generation": 0, + "kind": "move", + "operation_id": "agent-f38f26f75d5948c98118fc8c65842276" + }, + "version": 1 + }, + "identity": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": null, + "state": "unknown", + "unresolved_commands": [ + "command-20204cceb73b473eb66857af0b813c1d" + ] + }, + "client": { + "commands": { + "command-19630ef4ff684508b4053bffd3261aca": { + "operation": "propose", + "request_sha256": "cb7928d05b419594331d63a3bef90d42e440e4a3ef6b9623a6d5c4939231f429", + "send_count": 1, + "state": "completed" + }, + "command-20204cceb73b473eb66857af0b813c1d": { + "operation": "dispatch", + "request_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "send_count": 1, + "state": "unknown" + }, + "command-58f43389dab14ea1af8f011dcac3b415": { + "operation": "observe", + "request_sha256": "4ce1073576807cdceabb307c865c5a51f4a5a2577ddba3ad19d24332c3b4f894", + "send_count": 1, + "state": "completed" + }, + "command-7c7f1616df1245399a248717c004c424": { + "operation": "start_job", + "request_sha256": "5e674b785e9d70d9894c809ba1bdf202cc84bdbed725e4175cf32d38bad96d8a", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-99cf4b51858b41f49568551e639d3311", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "cf9403073fc842869dd638f8403d963b" + }, + "pending_command_ids": [ + "command-20204cceb73b473eb66857af0b813c1d" + ] + }, + "host": { + "app_id": "536221822098429cac997e554389c561", + "body_identity": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/operator/client/images/challenge-c4a2aac3e0e44153bb8a38ba9a847b50-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "runner_status": "blocked", + "sequence": 17, + "stage": { + "arguments": { + "proposal_id": "proposal-28a486115a0242468ba8651ede499eb6" + }, + "before_commands": [ + "command-19630ef4ff684508b4053bffd3261aca", + "command-58f43389dab14ea1af8f011dcac3b415", + "command-7c7f1616df1245399a248717c004c424" + ], + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "context": { + "action": { + "action": "move", + "intent": "Push the block right so its center reaches the measurement region.", + "target_x": 0.015, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/operator/calls/f86aaa43-84f4-4074-98d1-5b2a52c98280-01", + "decision_epoch": 0, + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280" + }, + "control_epoch": 0, + "operation": "dispatch", + "proof": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "e5c43221ed1c25504133986f98a2f894eb2f6b38e9e095b8f3ed209f9b10e859" + } + }, + "usage": { + "calls": 1, + "input_tokens": 12607, + "output_tokens": 194, + "reported_calls": 1 + } + }, + "label": "restored", + "posts": [ + { + "command_id": "command-7c7f1616df1245399a248717c004c424", + "payload_sha256": "5e674b785e9d70d9894c809ba1bdf202cc84bdbed725e4175cf32d38bad96d8a", + "sequence": 8 + }, + { + "command_id": "command-58f43389dab14ea1af8f011dcac3b415", + "payload_sha256": "4ce1073576807cdceabb307c865c5a51f4a5a2577ddba3ad19d24332c3b4f894", + "sequence": 16 + }, + { + "command_id": "command-19630ef4ff684508b4053bffd3261aca", + "payload_sha256": "cb7928d05b419594331d63a3bef90d42e440e4a3ef6b9623a6d5c4939231f429", + "sequence": 28 + }, + { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "payload_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "sequence": 36 + } + ], + "processes": { + "body": 64365, + "workbench": 54014 + }, + "public": { + "active_job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "activity": [ + { + "intent": "Missing native receipt stays blocked", + "kind": "queued", + "outcome": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "seq": 1, + "time": 1791860458.630235 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "4dc75e6883288bfe4e51f1e294b4b8038c3615b279b0a9f4a56db5c0ad416c97" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "start_job", + "seq": 2, + "time": 1791860458.641203 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 3, + "time": 1791860458.654922 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "start_job: command-7c7f1616df1245399a248717c004c424", + "seq": 4, + "time": 1791860458.670542 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "25fe15ee4cbe707cc7b0b6c17cad92924f2d036f63ebced3d77f032e64d845d4" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 5, + "time": 1791860458.679942 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 6, + "time": 1791860458.692619 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "observe: command-58f43389dab14ea1af8f011dcac3b415", + "seq": 7, + "time": 1791860458.866333 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "inference", + "outcome": "1", + "seq": 8, + "time": 1791860458.8678331 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "8a574b41e61612e1c3e1485e09bfc9ee7e16ad5fb7b4c54b51ec9de900d52995" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 9, + "time": 1791860466.171602 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 10, + "time": 1791860466.1831439 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "propose: command-19630ef4ff684508b4053bffd3261aca", + "seq": 11, + "time": 1791860466.199779 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "e5c43221ed1c25504133986f98a2f894eb2f6b38e9e095b8f3ed209f9b10e859" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 12, + "time": 1791860466.209346 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 13, + "time": 1791860466.2203572 + }, + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 14, + "time": 1791860466.3972118 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 15, + "time": 1791860467.0656672 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "seq": 16, + "time": 1791860545.814935 + }, + { + "intent": "", + "kind": "body_epoch_changed", + "outcome": "1 -> 2", + "seq": 17, + "time": 1791860545.815804 + } + ], + "app_id": "536221822098429cac997e554389c561", + "connection": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 2, + "fault": null, + "reason": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "label": "Missing native receipt stays blocked", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image" + }, + "recovery": { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "operation": "dispatch", + "reason": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "state": "uncertain" + }, + "runner_status": "blocked", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 1, + "input_tokens": 12607, + "output_tokens": 194, + "reported_calls": 1, + "token_usage_complete": true + } + }, + "time": 1791860545.831948 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/snapshots/000004-finished.json b/jevdual/results/reusable-body-recovery-20261012/native-gap/snapshots/000004-finished.json new file mode 100644 index 0000000..ab43917 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/snapshots/000004-finished.json @@ -0,0 +1,433 @@ +{ + "body_command": { + "digest_valid": true, + "intent": { + "request": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "arguments": { + "proposal_id": "proposal-28a486115a0242468ba8651ede499eb6" + }, + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "client_id": "client-99cf4b51858b41f49568551e639d3311", + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "epoch": 1, + "lease_id": "cf9403073fc842869dd638f8403d963b", + "operation": "dispatch", + "protocol": "footwork.body/1", + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "request_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "result_sha256": null + }, + "terminal": null + }, + "body_identity": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "moves": 0, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "unknown", + "unresolved_effect": { + "generation": 0, + "kind": "move", + "operation_id": "agent-f38f26f75d5948c98118fc8c65842276" + }, + "version": 1 + }, + "identity": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": null, + "state": "unknown", + "unresolved_commands": [ + "command-20204cceb73b473eb66857af0b813c1d" + ] + }, + "client": { + "commands": { + "command-19630ef4ff684508b4053bffd3261aca": { + "operation": "propose", + "request_sha256": "cb7928d05b419594331d63a3bef90d42e440e4a3ef6b9623a6d5c4939231f429", + "send_count": 1, + "state": "completed" + }, + "command-20204cceb73b473eb66857af0b813c1d": { + "operation": "dispatch", + "request_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "send_count": 1, + "state": "unknown" + }, + "command-58f43389dab14ea1af8f011dcac3b415": { + "operation": "observe", + "request_sha256": "4ce1073576807cdceabb307c865c5a51f4a5a2577ddba3ad19d24332c3b4f894", + "send_count": 1, + "state": "completed" + }, + "command-7c7f1616df1245399a248717c004c424": { + "operation": "start_job", + "request_sha256": "5e674b785e9d70d9894c809ba1bdf202cc84bdbed725e4175cf32d38bad96d8a", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-99cf4b51858b41f49568551e639d3311", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "cf9403073fc842869dd638f8403d963b" + }, + "pending_command_ids": [ + "command-20204cceb73b473eb66857af0b813c1d" + ] + }, + "host": { + "app_id": "536221822098429cac997e554389c561", + "body_identity": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/operator/client/images/challenge-c4a2aac3e0e44153bb8a38ba9a847b50-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "runner_status": "blocked", + "sequence": 19, + "stage": { + "arguments": { + "proposal_id": "proposal-28a486115a0242468ba8651ede499eb6" + }, + "before_commands": [ + "command-19630ef4ff684508b4053bffd3261aca", + "command-58f43389dab14ea1af8f011dcac3b415", + "command-7c7f1616df1245399a248717c004c424" + ], + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "context": { + "action": { + "action": "move", + "intent": "Push the block right so its center reaches the measurement region.", + "target_x": 0.015, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/operator/calls/f86aaa43-84f4-4074-98d1-5b2a52c98280-01", + "decision_epoch": 0, + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280" + }, + "control_epoch": 0, + "operation": "dispatch", + "proof": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "e5c43221ed1c25504133986f98a2f894eb2f6b38e9e095b8f3ed209f9b10e859" + } + }, + "usage": { + "calls": 1, + "input_tokens": 12607, + "output_tokens": 194, + "reported_calls": 1 + } + }, + "label": "finished", + "posts": [ + { + "command_id": "command-7c7f1616df1245399a248717c004c424", + "payload_sha256": "5e674b785e9d70d9894c809ba1bdf202cc84bdbed725e4175cf32d38bad96d8a", + "sequence": 8 + }, + { + "command_id": "command-58f43389dab14ea1af8f011dcac3b415", + "payload_sha256": "4ce1073576807cdceabb307c865c5a51f4a5a2577ddba3ad19d24332c3b4f894", + "sequence": 16 + }, + { + "command_id": "command-19630ef4ff684508b4053bffd3261aca", + "payload_sha256": "cb7928d05b419594331d63a3bef90d42e440e4a3ef6b9623a6d5c4939231f429", + "sequence": 28 + }, + { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "payload_sha256": "fa142a5edc57c58e9ef9c4421bbdc5044a24afe4727e726b1cc6ebe543c6a522", + "sequence": 36 + } + ], + "processes": { + "body": 64365, + "workbench": 54014 + }, + "public": { + "active_job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "activity": [ + { + "intent": "Missing native receipt stays blocked", + "kind": "queued", + "outcome": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "seq": 1, + "time": 1791860458.630235 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "4dc75e6883288bfe4e51f1e294b4b8038c3615b279b0a9f4a56db5c0ad416c97" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "start_job", + "seq": 2, + "time": 1791860458.641203 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 3, + "time": 1791860458.654922 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "start_job: command-7c7f1616df1245399a248717c004c424", + "seq": 4, + "time": 1791860458.670542 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "25fe15ee4cbe707cc7b0b6c17cad92924f2d036f63ebced3d77f032e64d845d4" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 5, + "time": 1791860458.679942 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 6, + "time": 1791860458.692619 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "observe: command-58f43389dab14ea1af8f011dcac3b415", + "seq": 7, + "time": 1791860458.866333 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "inference", + "outcome": "1", + "seq": 8, + "time": 1791860458.8678331 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "8a574b41e61612e1c3e1485e09bfc9ee7e16ad5fb7b4c54b51ec9de900d52995" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 9, + "time": 1791860466.171602 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 10, + "time": 1791860466.1831439 + }, + { + "intent": "Missing native receipt stays blocked", + "kind": "rpc_applied", + "outcome": "propose: command-19630ef4ff684508b4053bffd3261aca", + "seq": 11, + "time": 1791860466.199779 + }, + { + "evidence": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "e5c43221ed1c25504133986f98a2f894eb2f6b38e9e095b8f3ed209f9b10e859" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 12, + "time": 1791860466.209346 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 13, + "time": 1791860466.2203572 + }, + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 14, + "time": 1791860466.3972118 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 15, + "time": 1791860467.0656672 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "seq": 16, + "time": 1791860545.814935 + }, + { + "intent": "", + "kind": "body_epoch_changed", + "outcome": "1 -> 2", + "seq": 17, + "time": 1791860545.815804 + }, + { + "intent": "", + "kind": "halt", + "outcome": "accepted intent has no verified terminal proof", + "seq": 18, + "time": 1791860885.3836958 + }, + { + "intent": "", + "kind": "halt", + "outcome": "accepted intent has no verified terminal proof", + "seq": 19, + "time": 1791860982.512386 + } + ], + "app_id": "536221822098429cac997e554389c561", + "connection": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 2, + "fault": null, + "reason": "Independent body has unresolved effects", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "accepted intent has no verified terminal proof", + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "label": "Missing native receipt stays blocked", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "accepted intent has no verified terminal proof" + }, + "recovery": { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "operation": "dispatch", + "reason": "accepted intent has no verified terminal proof", + "state": "uncertain" + }, + "runner_status": "blocked", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 1, + "input_tokens": 12607, + "output_tokens": 194, + "reported_calls": 1, + "token_usage_complete": true + } + }, + "time": 1791861030.916094 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/source-freeze.json b/jevdual/results/reusable-body-recovery-20261012/native-gap/source-freeze.json new file mode 100644 index 0000000..55ec0a3 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/source-freeze.json @@ -0,0 +1,41 @@ +{ + "commit": "c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/bench_body_contract.py": "a4dd11d946a3226c6bc32f135b5d4484e86ac684ac3aa941d52d093062933a2b", + "tools/physical-workcell/body_client.py": "3ff0449f0865136d8c2d0913f34ca1dc8d9f5f25216ccbdcd1bf6530dcfb6fd1", + "tools/physical-workcell/body_contract.py": "3a8ba3f44d5f8ab9b5ab30e53642a78b176608fdab7719a68d725dc03d6974bd", + "tools/physical-workcell/body_network.py": "5dbb4480765ebb75bcdbafc870b1d6a468f56d4f89d924d8cd4ec851c4c02d0a", + "tools/physical-workcell/body_protocol.py": "301fca90dc215694468f5c6989080cf796ee0c205ff045f5e18e4968a2ca6bdf", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "d7ba2e5f3a520a4679f8309b866e45ba3abd1d22e4f8849b970302159af134a3", + "tools/physical-workcell/crash_body.py": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/network_bench_operator.html": "10efef540879da4a710cee2aafe6712213ad645c8fd7890382da1048f96a6686", + "tools/physical-workcell/network_bench_operator.py": "9079729ea4e8b56ccdf2a6d187b0c5d79a23a33688748fe55d92d10922500b40", + "tools/physical-workcell/network_fault_proxy.py": "44b0b52f7c213c0f5f93b8997ba72be4e750bc9f1a4f7f6d7b2a8c4f1233b595", + "tools/physical-workcell/network_operator_runtime.py": "530666b4d4e54cc8e67a6180ac38821590bc42acd8a460c3d9066ad02542e229", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/persistent_body_client.py": "57ba866da8bcb53995ea9d93fbefbfd0556e53a2bde2b445deb4f427cd3d2a14", + "tools/physical-workcell/planner_guard.py": "52eeb806b824d62fc78d500d3bf285fcf8c78facedec8c17721eabae0e7c87da", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/qualify_network_body.py": "769c2514277a6f3101d8ff237041039c1e4ca85a5a76ec453dd9c9f87997a09c", + "tools/physical-workcell/qualify_network_workbench.py": "e5c72acda2ea8fae3658faba907448765df2f1e34e05749fbe85faed28ef003b", + "tools/physical-workcell/qualify_workbench_crashes.py": "da971a4190389414dd2114b86d2b7dd40adb2dba2c16e304782eab362244932e", + "tools/physical-workcell/qualify_workbench_restarts.py": "2bc3edc5ea39b69f3372d4274b757f06472409407a22cd3149dfa52fbf314dcb", + "tools/physical-workcell/record_network_workbench.py": "7604865caa309c7002c01a2384da84eb62d8b22dcd63ab256618c536bc5fedc4", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_network_body.py": "d1f7150a5ff23775664b5656fd21379502ba71175487fd7aadb2b51bffcdaf8c", + "tools/physical-workcell/score_network_workbench.py": "67a53586081fe425bd14b0a0fbbf3d7f0fc1e66daccda06765bb88f3d9d4d84c", + "tools/physical-workcell/score_workbench_crashes.py": "10bec02c29afdb9963006884fd9ce310a52be8ba0995698cc11f5d6e4475b998", + "tools/physical-workcell/score_workbench_restarts.py": "181cbf4ee1bbd5483f38edc4a84244fc3efa98dfd21b5c9751d43edfe8f9f5a5", + "tools/physical-workcell/test_workbench_crash_harness.py": "b5a1398f9ccec220731f30c9f082260e378743f6fa6c3d954f008c2e79aeaf6e", + "tools/physical-workcell/workbench_link.py": "79571f62c5a9b5fb843a852bc34a2a509b0dc6dc1a6e091fa9f948ab98fae420", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui-ledger.json b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui-ledger.json new file mode 100644 index 0000000..989bc8e --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui-ledger.json @@ -0,0 +1,216 @@ +{ + "steps": [ + { + "browser_action": "Observed body unavailable after native motion saved without completion receipt", + "cached_public": null, + "client_commands": 4, + "name": "crash-offline", + "offline": false, + "pending_command_ids": [ + "command-20204cceb73b473eb66857af0b813c1d" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/ui/crash-offline.png", + "state": { + "active_job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "app_id": "536221822098429cac997e554389c561", + "connection": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 1, + "fault": null, + "reason": "Remote end closed connection without response", + "state": "unavailable" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Remote end closed connection without response", + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "label": "Missing native receipt stays blocked", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "paused" + }, + "time": 1791860545.301014, + "visible_text": "Browser tab: 7, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51425/\".\n0 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51425/\n\t1 container\n\t\t2 text Footwork Bench\n\t\t3 text SIMULATION\n\t4 container Jobs\n\t\t5 text WORK QUEUE\n\t\t6 button (disabled) \uff0b New job, ID: new-job\n\t\t47 checkbox Missing native receipt stays blocked Paused, Value: 1\n\t\t8 text One bench. Successive jobs.\n\t\t9 text The AI prepares the part, takes a measurement, and returns it home before the next job.\n\t10 container\n\t\t11 heading Bench, Value: 1\n\t\t\t12 text Bench\n\t\t48 text Missing native receipt stays blocked\n\t\t49 text Paused\n\t\t15 button (disabled) Resume, ID: pause\n\t\t50 text reply is uncertain; reconcile exact command without resending\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t51 text Body unavailable\n\t\t\t52 text Remote end closed connection without response\n\t\t\t19 button Reconnect, ID: disconnect\n\t\t\t20 button (disabled) Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t53 text Retained observation \u00b7 body offline\n\t\t\t54 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t55 text Retained \u00b7 offline \u00b7 generation 0\n\t\t56 text DISCONNECTED\n\t\t57 text The body connection is unavailable. The last camera observation is retained.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t58 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "New body boot retains exact unresolved command and disables continuation", + "cached_public": null, + "client_commands": 4, + "name": "crash-restored", + "offline": false, + "pending_command_ids": [ + "command-20204cceb73b473eb66857af0b813c1d" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/ui/crash-restored.png", + "state": { + "active_job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "app_id": "536221822098429cac997e554389c561", + "connection": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 2, + "fault": null, + "reason": "Independent body has unresolved effects", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "label": "Missing native receipt stays blocked", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "operation": "dispatch", + "reason": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791860804.216393, + "visible_text": "Browser tab: 7, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51425/\".\n0 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51425/\n\t1 container\n\t\t2 text Footwork Bench\n\t\t3 text SIMULATION\n\t4 container Jobs\n\t\t5 text WORK QUEUE\n\t\t6 button (disabled) \uff0b New job, ID: new-job\n\t\t55 checkbox Missing native receipt stays blocked Needs attention, Value: 1\n\t\t8 text One bench. Successive jobs.\n\t\t9 text The AI prepares the part, takes a measurement, and returns it home before the next job.\n\t10 container\n\t\t11 heading Bench, Value: 1\n\t\t\t12 text Bench\n\t\t56 text Missing native receipt stays blocked\n\t\t57 text Needs attention\n\t\t15 button (disabled) Pause, ID: pause\n\t\t58 text Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t59 text Body connected\n\t\t\t60 text Independent body has unresolved effects\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t61 text Last observation \u00b7 Missing native receipt stays blocked\n\t\t\t54 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t62 text Captured \u00b7 generation 0\n\t\t63 text OUTCOME UNCERTAIN\n\t\t64 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t65 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Clicked Check receipt; missing native completion proof remains uncertain and commands unchanged", + "cached_public": null, + "client_commands": 4, + "name": "crash-blocked", + "offline": false, + "pending_command_ids": [ + "command-20204cceb73b473eb66857af0b813c1d" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/ui/crash-blocked.png", + "state": { + "active_job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "app_id": "536221822098429cac997e554389c561", + "connection": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 2, + "fault": null, + "reason": "Independent body has unresolved effects", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "accepted intent has no verified terminal proof", + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "label": "Missing native receipt stays blocked", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "operation": "dispatch", + "reason": "accepted intent has no verified terminal proof", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791860945.7255082, + "visible_text": "Browser tab: 7, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51425/\".\n0 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51425/\n\t1 container\n\t\t2 text Footwork Bench\n\t\t3 text SIMULATION\n\t4 container Jobs\n\t\t5 text WORK QUEUE\n\t\t6 button (disabled) \uff0b New job, ID: new-job\n\t\t55 checkbox Missing native receipt stays blocked Needs attention, Value: 1\n\t\t8 text One bench. Successive jobs.\n\t\t9 text The AI prepares the part, takes a measurement, and returns it home before the next job.\n\t10 container\n\t\t11 heading Bench, Value: 1\n\t\t\t12 text Bench\n\t\t56 text Missing native receipt stays blocked\n\t\t57 text Needs attention\n\t\t15 button (disabled) Pause, ID: pause\n\t\t58 text Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t59 text Body connected\n\t\t\t60 text Independent body has unresolved effects\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t61 text Last observation \u00b7 Missing native receipt stays blocked\n\t\t\t54 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t62 text Captured \u00b7 generation 0\n\t\t63 text OUTCOME UNCERTAIN\n\t\t64 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t65 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Repeated exact Check receipt; same unresolved token, no new commands or continuation", + "cached_public": null, + "client_commands": 4, + "name": "crash-still-blocked", + "offline": false, + "pending_command_ids": [ + "command-20204cceb73b473eb66857af0b813c1d" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1/ui/crash-still-blocked.png", + "state": { + "active_job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "app_id": "536221822098429cac997e554389c561", + "connection": { + "adapter_id": "6c2d8f9b74544957a632173cfe554639", + "body_id": "2bfcf6ea4ac04b44b20db138b4c03e80", + "epoch": 2, + "fault": null, + "reason": "Independent body has unresolved effects", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "accepted intent has no verified terminal proof", + "job_id": "f86aaa43-84f4-4074-98d1-5b2a52c98280", + "label": "Missing native receipt stays blocked", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-20204cceb73b473eb66857af0b813c1d", + "operation": "dispatch", + "reason": "accepted intent has no verified terminal proof", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791861030.412361, + "visible_text": "Browser tab: 7, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51425/\".\n0 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51425/\n\t1 container\n\t\t2 text Footwork Bench\n\t\t3 text SIMULATION\n\t4 container Jobs\n\t\t5 text WORK QUEUE\n\t\t6 button (disabled) \uff0b New job, ID: new-job\n\t\t68 checkbox Missing native receipt stays blocked Needs attention, Value: 1\n\t\t8 text One bench. Successive jobs.\n\t\t9 text The AI prepares the part, takes a measurement, and returns it home before the next job.\n\t10 container\n\t\t11 heading Bench, Value: 1\n\t\t\t12 text Bench\n\t\t69 text Missing native receipt stays blocked\n\t\t70 text Needs attention\n\t\t15 button (disabled) Pause, ID: pause\n\t\t71 text accepted intent has no verified terminal proof\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t72 text Body connected\n\t\t\t73 text Independent body has unresolved effects\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t74 text Last observation \u00b7 Missing native receipt stays blocked\n\t\t\t54 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t75 text Captured \u00b7 generation 0\n\t\t76 text OUTCOME UNCERTAIN\n\t\t77 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t78 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + } + ] +} diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-blocked.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-blocked.ledger.txt new file mode 100644 index 0000000..3f437f4 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-blocked.ledger.txt @@ -0,0 +1,40 @@ +Browser tab: 7, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51425/". +0 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51425/ + 1 container + 2 text Footwork Bench + 3 text SIMULATION + 4 container Jobs + 5 text WORK QUEUE + 6 button (disabled) + New job, ID: new-job + 55 checkbox Missing native receipt stays blocked Needs attention, Value: 1 + 8 text One bench. Successive jobs. + 9 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 10 container + 11 heading Bench, Value: 1 + 12 text Bench + 56 text Missing native receipt stays blocked + 57 text Needs attention + 15 button (disabled) Pause, ID: pause + 58 text Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 16 container Description: Body connection, ID: network-card + 59 text Body connected + 60 text Independent body has unresolved effects + 19 button Disconnect, ID: disconnect + 20 button Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 61 text Last observation · Missing native receipt stays blocked + 54 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 62 text Captured · generation 0 + 63 text OUTCOME UNCERTAIN + 64 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 65 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-blocked.png b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-blocked.png new file mode 100644 index 0000000..9c9397a Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-blocked.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-blocked.txt b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-blocked.txt new file mode 100644 index 0000000..4b4b379 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-blocked.txt @@ -0,0 +1,74 @@ +Browser tab: 7, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51425/". +0 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51425/ + 1 container + 2 text Footwork Bench + 3 text SIMULATION + 4 container Jobs + 5 text WORK QUEUE + 6 button (disabled) + New job, ID: new-job + 55 checkbox Missing native receipt stays blocked Needs attention, Value: 1 + 8 text One bench. Successive jobs. + 9 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 10 container + 11 heading Bench, Value: 1 + 12 text Bench + 56 text Missing native receipt stays blocked + 57 text Needs attention + 15 button (disabled) Pause, ID: pause + 58 text Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 16 container Description: Body connection, ID: network-card + 59 text Body connected + 60 text Independent body has unresolved effects + 19 button Disconnect, ID: disconnect + 20 button Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 61 text Last observation · Missing native receipt stays blocked + 54 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 62 text Captured · generation 0 + 63 text OUTCOME UNCERTAIN + 64 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 65 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 66 text 17 events + 67 content list activity + 68 container + 69 text 11:02 PM Body restarted 1 -> 2 + 70 container + 71 text 11:02 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 72 container + 73 text 11:01 PM Agent stopped for review Remote end closed connection without response + 74 container + 75 text 11:01 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 76 container + 77 text 11:01 PM Sending a guarded action + 78 container + 79 text 11:01 PM Fresh body check verified The next command uses current body evidence. + 80 container + 81 text 11:01 PM Action proposal accepted Missing native receipt stays blocked + 82 container + 83 text 11:01 PM Checking the planned action + 84 container + 85 text 11:01 PM Fresh body check verified The next command uses current body evidence. + 86 container + 87 text 11:00 PM AI planning requested Missing native receipt stays blocked + 88 container + 89 text 11:00 PM Camera observation received Missing native receipt stays blocked + 90 container + 91 text 11:00 PM Requesting camera observation + 45 button (collapsed) Session details, Secondary Actions: Expand + 46 text Session details + 92 text 1 model requests · Reported tokens: 12,607 input / 194 output + +The focused UI element is 0 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51425/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-offline.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-offline.ledger.txt new file mode 100644 index 0000000..9dae0a7 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-offline.ledger.txt @@ -0,0 +1,40 @@ +Browser tab: 7, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51425/". +0 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51425/ + 1 container + 2 text Footwork Bench + 3 text SIMULATION + 4 container Jobs + 5 text WORK QUEUE + 6 button (disabled) + New job, ID: new-job + 47 checkbox Missing native receipt stays blocked Paused, Value: 1 + 8 text One bench. Successive jobs. + 9 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 10 container + 11 heading Bench, Value: 1 + 12 text Bench + 48 text Missing native receipt stays blocked + 49 text Paused + 15 button (disabled) Resume, ID: pause + 50 text reply is uncertain; reconcile exact command without resending + 16 container Description: Body connection, ID: network-card + 51 text Body unavailable + 52 text Remote end closed connection without response + 19 button Reconnect, ID: disconnect + 20 button (disabled) Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 53 text Retained observation · body offline + 54 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 55 text Retained · offline · generation 0 + 56 text DISCONNECTED + 57 text The body connection is unavailable. The last camera observation is retained. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 58 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-offline.png b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-offline.png new file mode 100644 index 0000000..d189a90 Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-offline.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-offline.txt b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-offline.txt new file mode 100644 index 0000000..f4123b2 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-offline.txt @@ -0,0 +1,74 @@ +Browser tab: 7, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51425/". +0 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51425/ + 1 container + 2 text Footwork Bench + 3 text SIMULATION + 4 container Jobs + 5 text WORK QUEUE + 6 button (disabled) + New job, ID: new-job + 47 checkbox Missing native receipt stays blocked Paused, Value: 1 + 8 text One bench. Successive jobs. + 9 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 10 container + 11 heading Bench, Value: 1 + 12 text Bench + 48 text Missing native receipt stays blocked + 49 text Paused + 15 button (disabled) Resume, ID: pause + 50 text reply is uncertain; reconcile exact command without resending + 16 container Description: Body connection, ID: network-card + 51 text Body unavailable + 52 text Remote end closed connection without response + 19 button Reconnect, ID: disconnect + 20 button (disabled) Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 53 text Retained observation · body offline + 54 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 55 text Retained · offline · generation 0 + 56 text DISCONNECTED + 57 text The body connection is unavailable. The last camera observation is retained. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 58 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 59 text 15 events + 42 content list activity + 60 container + 61 text 11:01 PM Agent stopped for review Remote end closed connection without response + 62 container + 63 text 11:01 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 64 container + 65 text 11:01 PM Sending a guarded action + 66 container + 67 text 11:01 PM Fresh body check verified The next command uses current body evidence. + 68 container + 69 text 11:01 PM Action proposal accepted Missing native receipt stays blocked + 70 container + 71 text 11:01 PM Checking the planned action + 72 container + 73 text 11:01 PM Fresh body check verified The next command uses current body evidence. + 74 container + 75 text 11:00 PM AI planning requested Missing native receipt stays blocked + 76 container + 77 text 11:00 PM Camera observation received Missing native receipt stays blocked + 78 container + 79 text 11:00 PM Requesting camera observation + 80 container + 81 text 11:00 PM Fresh body check verified The next command uses current body evidence. + 82 container + 83 text 11:00 PM Inspection started Missing native receipt stays blocked + 45 button (collapsed) Session details, Secondary Actions: Expand + 46 text Session details + 84 text 1 model requests · Reported tokens: 12,607 input / 194 output + +The focused UI element is 0 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51425/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-restored.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-restored.ledger.txt new file mode 100644 index 0000000..3f437f4 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-restored.ledger.txt @@ -0,0 +1,40 @@ +Browser tab: 7, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51425/". +0 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51425/ + 1 container + 2 text Footwork Bench + 3 text SIMULATION + 4 container Jobs + 5 text WORK QUEUE + 6 button (disabled) + New job, ID: new-job + 55 checkbox Missing native receipt stays blocked Needs attention, Value: 1 + 8 text One bench. Successive jobs. + 9 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 10 container + 11 heading Bench, Value: 1 + 12 text Bench + 56 text Missing native receipt stays blocked + 57 text Needs attention + 15 button (disabled) Pause, ID: pause + 58 text Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 16 container Description: Body connection, ID: network-card + 59 text Body connected + 60 text Independent body has unresolved effects + 19 button Disconnect, ID: disconnect + 20 button Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 61 text Last observation · Missing native receipt stays blocked + 54 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 62 text Captured · generation 0 + 63 text OUTCOME UNCERTAIN + 64 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 65 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-restored.png b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-restored.png new file mode 100644 index 0000000..9c9397a Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-restored.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-restored.txt b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-restored.txt new file mode 100644 index 0000000..8ecabcd --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-restored.txt @@ -0,0 +1,49 @@ +Browser tab: 7, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51425/". +0 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51425/ + 1 container + 2 text Footwork Bench + 3 text SIMULATION + 4 container Jobs + 5 text WORK QUEUE + 6 button (disabled) + New job, ID: new-job + 55 checkbox Missing native receipt stays blocked Needs attention, Value: 1 + 8 text One bench. Successive jobs. + 9 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 10 container + 11 heading Bench, Value: 1 + 12 text Bench + 56 text Missing native receipt stays blocked + 57 text Needs attention + 15 button (disabled) Pause, ID: pause + 58 text Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 16 container Description: Body connection, ID: network-card + 59 text Body connected + 60 text Independent body has unresolved effects + 19 button Disconnect, ID: disconnect + 20 button Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 61 text Last observation · Missing native receipt stays blocked + 54 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 62 text Captured · generation 0 + 63 text OUTCOME UNCERTAIN + 64 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 65 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 66 text 17 events + 45 button (collapsed) Session details, Secondary Actions: Expand + 46 text Session details + 67 text 1 model requests · Reported tokens: 12,607 input / 194 output + +The focused UI element is 0 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51425/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-still-blocked.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-still-blocked.ledger.txt new file mode 100644 index 0000000..d3c996b --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-still-blocked.ledger.txt @@ -0,0 +1,40 @@ +Browser tab: 7, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51425/". +0 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51425/ + 1 container + 2 text Footwork Bench + 3 text SIMULATION + 4 container Jobs + 5 text WORK QUEUE + 6 button (disabled) + New job, ID: new-job + 68 checkbox Missing native receipt stays blocked Needs attention, Value: 1 + 8 text One bench. Successive jobs. + 9 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 10 container + 11 heading Bench, Value: 1 + 12 text Bench + 69 text Missing native receipt stays blocked + 70 text Needs attention + 15 button (disabled) Pause, ID: pause + 71 text accepted intent has no verified terminal proof + 16 container Description: Body connection, ID: network-card + 72 text Body connected + 73 text Independent body has unresolved effects + 19 button Disconnect, ID: disconnect + 20 button Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 74 text Last observation · Missing native receipt stays blocked + 54 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 75 text Captured · generation 0 + 76 text OUTCOME UNCERTAIN + 77 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 78 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-still-blocked.png b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-still-blocked.png new file mode 100644 index 0000000..d795f2f Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-still-blocked.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-still-blocked.txt b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-still-blocked.txt new file mode 100644 index 0000000..81dfd00 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/native-gap/ui/crash-still-blocked.txt @@ -0,0 +1,74 @@ +Browser tab: 7, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51425/". +0 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51425/ + 1 container + 2 text Footwork Bench + 3 text SIMULATION + 4 container Jobs + 5 text WORK QUEUE + 6 button (disabled) + New job, ID: new-job + 68 checkbox Missing native receipt stays blocked Needs attention, Value: 1 + 8 text One bench. Successive jobs. + 9 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 10 container + 11 heading Bench, Value: 1 + 12 text Bench + 69 text Missing native receipt stays blocked + 70 text Needs attention + 15 button (disabled) Pause, ID: pause + 71 text accepted intent has no verified terminal proof + 16 container Description: Body connection, ID: network-card + 72 text Body connected + 73 text Independent body has unresolved effects + 19 button Disconnect, ID: disconnect + 20 button Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 74 text Last observation · Missing native receipt stays blocked + 54 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 75 text Captured · generation 0 + 76 text OUTCOME UNCERTAIN + 77 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 78 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 79 text 18 events + 67 content list activity + 80 container + 81 text 11:08 PM Agent stopped for review accepted intent has no verified terminal proof + 82 container + 83 text 11:02 PM Body restarted 1 -> 2 + 84 container + 85 text 11:02 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 86 container + 87 text 11:01 PM Agent stopped for review Remote end closed connection without response + 88 container + 89 text 11:01 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 90 container + 91 text 11:01 PM Sending a guarded action + 92 container + 93 text 11:01 PM Fresh body check verified The next command uses current body evidence. + 94 container + 95 text 11:01 PM Action proposal accepted Missing native receipt stays blocked + 96 container + 97 text 11:01 PM Checking the planned action + 98 container + 99 text 11:01 PM Fresh body check verified The next command uses current body evidence. + 100 container + 101 text 11:00 PM AI planning requested Missing native receipt stays blocked + 102 container + 103 text 11:00 PM Camera observation received Missing native receipt stays blocked + 45 button (collapsed) Session details, Secondary Actions: Expand + 46 text Session details + 104 text 1 model requests · Reported tokens: 12,607 input / 194 output + +The focused UI element is 0 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51425/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/crash-ledger.json b/jevdual/results/reusable-body-recovery-20261012/planning/crash-ledger.json new file mode 100644 index 0000000..5719145 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/crash-ledger.json @@ -0,0 +1,194 @@ +{ + "boundaries": [ + { + "case": "host-planning", + "command_id": null, + "finished_snapshot": "000004-finished.json", + "gate": { + "barrier": "actual_codex_planning_with_live_guardian", + "call_directory": "0bc391fc-81d4-4d6a-a267-965b153f8cab-01", + "captured_descendants": [ + { + "command": "/Library/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python", + "group": 50189, + "parent": 49761, + "pid": 50189, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + { + "command": "node", + "group": 50191, + "parent": 50189, + "pid": 50191, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + { + "command": "/Users/thomasmain/.nvm/versions/node/v22.17.1/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/bin/codex", + "group": 50191, + "parent": 50191, + "pid": 50194, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + { + "command": "node", + "group": 50226, + "parent": 50194, + "pid": 50226, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 501 + }, + { + "command": "(git)", + "group": 50311, + "parent": 50194, + "pid": 50311, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 501 + }, + { + "command": "(ps)", + "group": 50189, + "parent": 50189, + "pid": 50312, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 0 + }, + { + "command": "(git)", + "group": 50311, + "parent": 50311, + "pid": 50313, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 501 + } + ], + "command_id": null, + "diagnostic_sha256": "a97bd24b6320995f5ef2ef1b4e06ec1392f3f51987b551c45759612efa874a18", + "pid": 49761, + "planner": { + "group_closed": false, + "guardian_pid": 50189, + "host_pid": 49761, + "owner": "f23b991cbd0a42f68913ce99df3a6f22", + "planner_pgid": 50191, + "planner_pid": 50191, + "process_exit_observed": false, + "request_sha256": "007700010812b697c0d21776587eada6f6dbc91c689139148e39073ac0a787af" + }, + "process_identities": { + "guardian": { + "command": "/Library/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python", + "group": 50189, + "parent": 49761, + "pid": 50189, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + "host": { + "command": "/Library/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python", + "group": 49761, + "parent": 49648, + "pid": 49761, + "started": "Mon Oct 12 22:55:34 2026", + "uid": 501 + }, + "planner": { + "command": "node", + "group": 50191, + "parent": 50189, + "pid": 50191, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + } + }, + "request_sha256": "007700010812b697c0d21776587eada6f6dbc91c689139148e39073ac0a787af", + "time": 1791860179.310709, + "version": 1 + }, + "new_epoch": 1, + "offline_snapshot": "000002-offline.json", + "old_epoch": 1, + "pre_snapshot": "000001-before.json", + "process": { + "boot": 1, + "children_before": [ + { + "command": "/Library/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python", + "group": 50189, + "parent": 49761, + "pid": 50189, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + { + "command": "node", + "group": 50191, + "parent": 50189, + "pid": 50191, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + { + "command": "/Users/thomasmain/.nvm/versions/node/v22.17.1/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/bin/codex", + "group": 50191, + "parent": 50191, + "pid": 50194, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + { + "command": "node", + "group": 50226, + "parent": 50194, + "pid": 50226, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 501 + }, + { + "command": "(git)", + "group": 50311, + "parent": 50194, + "pid": 50311, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 501 + }, + { + "command": "(ps)", + "group": 50189, + "parent": 50189, + "pid": 50312, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 0 + }, + { + "command": "(git)", + "group": 50311, + "parent": 50311, + "pid": 50313, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 501 + } + ], + "exit_observed": true, + "group": 49761, + "initialized": false, + "log": "workbench-01.log", + "pid": 49761, + "port": 51419, + "returncode": -9, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/operator", + "signal": "SIGKILL", + "signal_target": "host_pid_only", + "stopped_time": 1791860179.482004 + }, + "request_sha256": "007700010812b697c0d21776587eada6f6dbc91c689139148e39073ac0a787af", + "restored_snapshot": "000003-restored.json", + "role": "workbench" + } + ], + "case": "host-planning" +} diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/final-public.json b/jevdual/results/reusable-body-recovery-20261012/planning/final-public.json new file mode 100644 index 0000000..1952862 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/final-public.json @@ -0,0 +1,290 @@ +{ + "active_job_id": null, + "activity": [ + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "propose: command-3f9612fde2f84937923d289833d1cd95", + "seq": 131, + "time": 1791860859.06847 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "9b96fd8f2ab49f4a8b8ec92a9b8651061a73355c22526098ce3ca044ed4dd440" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 132, + "time": 1791860859.122879 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 133, + "time": 1791860859.162477 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "dispatch: command-eca41d65274f4d50a46869065ea7bad5", + "seq": 134, + "time": 1791860859.371604 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "d291139d0c6ba08c36f28acac8990f595344de8d206c27712639b5b5af8b1eb9" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 135, + "time": 1791860859.427273 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 136, + "time": 1791860859.468482 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "observe: command-65e5701648ce496aa2a1433078aabc7f", + "seq": 137, + "time": 1791860859.645217 + }, + { + "intent": "Retire planner and recover reply", + "kind": "inference", + "outcome": "14", + "seq": 138, + "time": 1791860859.646932 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "94bb8b8563324b4d327c8070a8e1cf99c5a133b5a104fd99fd3c2942c9d8e71a" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 139, + "time": 1791860867.169062 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 140, + "time": 1791860867.212686 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "propose: command-96098171ac4447cf9cb6a7b2facb798d", + "seq": 141, + "time": 1791860867.295264 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "6fed8287d24fddf7219a4010d559602cf9635bb8478a086949e4e3b3ce1676cd" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 142, + "time": 1791860867.361056 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 143, + "time": 1791860867.4049459 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "dispatch: command-3d83235444f841cdbba5d555c9d5246d", + "seq": 144, + "time": 1791860867.637874 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "510775598cb2b10ed20272d4daadd242ac0e590c38dd52fbfddf9c5381986ecc" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 145, + "time": 1791860867.700633 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 146, + "time": 1791860867.7447581 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "observe: command-047b4fe7d49c425c84abf5b3840b8f46", + "seq": 147, + "time": 1791860867.9181042 + }, + { + "intent": "Retire planner and recover reply", + "kind": "inference", + "outcome": "15", + "seq": 148, + "time": 1791860867.9198852 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "d96a40d00cc5f53d52e520d2589d9974be0c73d045b8b23e49d9e8faddb12074" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 149, + "time": 1791860874.962473 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 150, + "time": 1791860875.005901 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "propose: command-4f154fdba3aa4493846a014f77143055", + "seq": 151, + "time": 1791860875.085974 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "357b8fa432c119b488a0055f0626a482438a04263bc4c3849910d503196533c3" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 152, + "time": 1791860875.1431081 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 153, + "time": 1791860875.183298 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "dispatch: command-8ef12068ba8a4f199cd3e55144180692", + "seq": 154, + "time": 1791860875.4448261 + } + ], + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "connection": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 11, + "image_url": "/api/frame?generation=11&sha256=970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f" + }, + "jobs": [ + { + "call_count": 15, + "disturbance_test": false, + "error": null, + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "label": "Retire planner and recover reply", + "last_intent": "Finish reset now that the block is centered in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "d2820395d03d485492e1225f1972a12d", + "block_home_verified": true, + "generation": 11, + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "kind": "finish_reset", + "native_time_s": 22.94799999999816, + "operation_id": "agent-e8c188b67ec74303916d4a975c30c34e", + "pusher_home_verified": true, + "reset_contact_samples": 3132, + "reset_native_steps": 8206, + "state_sha256": "31f1e85639b1f4712ff98a8b2c3158de32f0e08e3204141921cfac1648fe0b6e" + }, + "status": "completed", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": true, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 15, + "input_tokens": 183745, + "output_tokens": 2157, + "reported_calls": 14, + "token_usage_complete": false + } +} diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/planner-retirement.json b/jevdual/results/reusable-body-recovery-20261012/planning/planner-retirement.json new file mode 100644 index 0000000..8f6581a --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/planner-retirement.json @@ -0,0 +1,115 @@ +{ + "captured_descendants": [ + { + "command": "/Library/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python", + "group": 50189, + "parent": 49761, + "pid": 50189, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + { + "command": "node", + "group": 50191, + "parent": 50189, + "pid": 50191, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + { + "command": "/Users/thomasmain/.nvm/versions/node/v22.17.1/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/bin/codex", + "group": 50191, + "parent": 50191, + "pid": 50194, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + { + "command": "node", + "group": 50226, + "parent": 50194, + "pid": 50226, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 501 + }, + { + "command": "(git)", + "group": 50311, + "parent": 50194, + "pid": 50311, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 501 + }, + { + "command": "(ps)", + "group": 50189, + "parent": 50189, + "pid": 50312, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 0 + }, + { + "command": "(git)", + "group": 50311, + "parent": 50311, + "pid": 50313, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 501 + } + ], + "captured_group_current_rows": [], + "captured_groups": [ + 50189, + 50191, + 50226, + 50311 + ], + "every_captured_group_absent": true, + "new_owner": "83ffafef07084b55a7dee3512d706ca7", + "old_call_files": { + "events.jsonl": "eff43b3f594340d62a5026a10d7059bdb58da361432160e98aa7814d33bfb8d7", + "input.png": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "instructions.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "planner-closure.json": "0e2f3623798d4847ebf4a3bc28acfc3d7cff30e8912183917547d7788b42bf06", + "planner-descendants.json": "558f48d23ab8f4357d9a0a87eb72ac37b5df74879302f72d7ff37e31b3830c2f", + "planner-intent.json": "dc2fb42e4dbf95f5ec8e77ac0a45f0b3c6514ad7e117b70ee8d69fd77d640974", + "planner-process.json": "ad54912fb9ae054f1823dfde4fea89852be5e5fa284be52af04f31f3a1f8f01e", + "prompt.txt": "0ab8175c79ce54ede5d33cc8d0686ac0c2a9a31efd6495a0641048aeb573b99b", + "request.json": "735acdca6b52f1ed2ff42d5114f34bd6dc75191c9ffe43eb4a69b6e5e294ac12", + "reservation.json": "baad3bed92c8b281fcd9a822a446fc5ad24dda723315cc1f2223295d8ce2f2c9", + "schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "stderr.log": "a44e0332e32d0900d6146d67d9133d23957e04544026b0237cea776e885f9fcd" + }, + "old_group_absent": true, + "old_identities": { + "guardian": { + "command": "/Library/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python", + "group": 50189, + "parent": 49761, + "pid": 50189, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + "host": { + "command": "/Library/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python", + "group": 49761, + "parent": 49648, + "pid": 49761, + "started": "Mon Oct 12 22:55:34 2026", + "uid": 501 + }, + "planner": { + "command": "node", + "group": 50191, + "parent": 50189, + "pid": 50191, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + } + }, + "old_owner": "f23b991cbd0a42f68913ce99df3a6f22", + "old_processes_absent": true, + "same_group_current_rows": [], + "same_pid_current_rows": [], + "time": 1791860318.171405 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/process-ledger.json b/jevdual/results/reusable-body-recovery-20261012/planning/process-ledger.json new file mode 100644 index 0000000..8d18177 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/process-ledger.json @@ -0,0 +1,120 @@ +{ + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "body_id": "d2820395d03d485492e1225f1972a12d", + "body_origin": "http://127.0.0.1:51417", + "browser_origin": "http://127.0.0.1:51419", + "case": "host-planning", + "owned_processes_exited": true, + "planner": "actual_codex_cli_images_over_TCP", + "processes": [ + { + "boot": 1, + "exit_observed": true, + "group": 49738, + "initialized": true, + "log": "body-01.log", + "pid": 49738, + "port": 51417, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/body", + "stopped_time": 1791861055.0644631 + }, + { + "exit_observed": true, + "group": 49752, + "pid": 49752, + "returncode": 0, + "role": "initialization" + }, + { + "boot": 1, + "children_before": [ + { + "command": "/Library/Frameworks/Python.framework/Versions/3.12/Resources/Python.app/Contents/MacOS/Python", + "group": 50189, + "parent": 49761, + "pid": 50189, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + { + "command": "node", + "group": 50191, + "parent": 50189, + "pid": 50191, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + { + "command": "/Users/thomasmain/.nvm/versions/node/v22.17.1/lib/node_modules/@openai/codex/node_modules/@openai/codex-darwin-arm64/vendor/aarch64-apple-darwin/bin/codex", + "group": 50191, + "parent": 50191, + "pid": 50194, + "started": "Mon Oct 12 22:56:18 2026", + "uid": 501 + }, + { + "command": "node", + "group": 50226, + "parent": 50194, + "pid": 50226, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 501 + }, + { + "command": "(git)", + "group": 50311, + "parent": 50194, + "pid": 50311, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 501 + }, + { + "command": "(ps)", + "group": 50189, + "parent": 50189, + "pid": 50312, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 0 + }, + { + "command": "(git)", + "group": 50311, + "parent": 50311, + "pid": 50313, + "started": "Mon Oct 12 22:56:19 2026", + "uid": 501 + } + ], + "exit_observed": true, + "group": 49761, + "initialized": false, + "log": "workbench-01.log", + "pid": 49761, + "port": 51419, + "returncode": -9, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/operator", + "signal": "SIGKILL", + "signal_target": "host_pid_only", + "stopped_time": 1791860179.482004 + }, + { + "boot": 2, + "exit_observed": true, + "group": 56155, + "initialized": false, + "log": "workbench-02.log", + "pid": 56155, + "port": 51419, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/operator", + "stopped_time": 1791861054.924713 + } + ], + "ready": true, + "simulation": true, + "source_commit": "c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54" +} diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/recovery-score.json b/jevdual/results/reusable-body-recovery-20261012/planning/recovery-score.json new file mode 100644 index 0000000..6b42944 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/recovery-score.json @@ -0,0 +1,583 @@ +{ + "admissions": [ + { + "command_id": "command-6e87e72b25954bf8ae8571cb370485ca", + "passed": true, + "proof_event": 2 + }, + { + "command_id": "command-3f6a2226f09f40d3a50d6b5c8d4a5f7c", + "passed": true, + "proof_event": 5 + }, + { + "command_id": "command-689742f4520c4c6abb4c674eb93aa7aa", + "passed": true, + "proof_event": 11 + }, + { + "command_id": "command-03a14ca1a1ef4804a6e4b12eea2cd182", + "passed": true, + "proof_event": 15 + }, + { + "command_id": "command-78798af6e0bb4e05b36d9c5a7ab38eeb", + "passed": true, + "proof_event": 18 + }, + { + "command_id": "command-4264ce8fdc0f4762900cc9d40e33a0b0", + "passed": true, + "proof_event": 21 + }, + { + "command_id": "command-e64acab86d6d4ef889fda2e1532edac2", + "passed": true, + "proof_event": 25 + }, + { + "command_id": "command-384125e4ec1a4a92bb2af0f113d51d91", + "passed": true, + "proof_event": 28 + }, + { + "command_id": "command-98ae2724fb4c4defb962a9d3ee33a32a", + "passed": true, + "proof_event": 31 + }, + { + "command_id": "command-082b9bbe08cc43e8b3e31d5e2b81c3b0", + "passed": true, + "proof_event": 35 + }, + { + "command_id": "command-385bfa9f62af41159e1b8c565679ffca", + "passed": true, + "proof_event": 38 + }, + { + "command_id": "command-fcc2df15c8b948a29a01ca1c8f723a19", + "passed": true, + "proof_event": 41 + }, + { + "command_id": "command-47d5a88d55ed4ff39b76af5cb295fdb4", + "passed": true, + "proof_event": 46 + }, + { + "command_id": "command-b0e991c0c3e4431c9e13603a27769d1f", + "passed": true, + "proof_event": 49 + }, + { + "command_id": "command-d55505df042941caab8fde61decfc140", + "passed": true, + "proof_event": 55 + }, + { + "command_id": "command-70dd7efe587f4813bfc5ef426de0a21d", + "passed": true, + "proof_event": 59 + }, + { + "command_id": "command-79f37126007249b5978bf0fc266cc1a4", + "passed": true, + "proof_event": 62 + }, + { + "command_id": "command-c41d875cd39c440599ba1bc4f4530fbf", + "passed": true, + "proof_event": 65 + }, + { + "command_id": "command-7bacb0429d414bc69c28a0af49c45bdb", + "passed": true, + "proof_event": 69 + }, + { + "command_id": "command-0e35e264738a4e009107a506b92c91a7", + "passed": true, + "proof_event": 72 + }, + { + "command_id": "command-0348a03b292645caa7ab7bbc9b0f4b9d", + "passed": true, + "proof_event": 75 + }, + { + "command_id": "command-bc99fd2e782a41e4bbb14592b557bc8b", + "passed": true, + "proof_event": 79 + }, + { + "command_id": "command-4e66c543be0f422b9df61de5663c4c8e", + "passed": true, + "proof_event": 82 + }, + { + "command_id": "command-216a23fd106c4c808cf8845276bfbffe", + "passed": true, + "proof_event": 85 + }, + { + "command_id": "command-d16da3a2381e44188165e2a8f220b3cf", + "passed": true, + "proof_event": 89 + }, + { + "command_id": "command-9475473dbb394debb8016fef9a406bcd", + "passed": true, + "proof_event": 92 + }, + { + "command_id": "command-e7659a3f8a1c46e38d8f7ba91344e4f1", + "passed": true, + "proof_event": 95 + }, + { + "command_id": "command-a52231151c274b47a909105357d1d095", + "passed": true, + "proof_event": 99 + }, + { + "command_id": "command-355fcf7a02ad4b9bbaf14cc8ba2934f4", + "passed": true, + "proof_event": 102 + }, + { + "command_id": "command-a5559c21051e40b89e77c2f0c8c3a457", + "passed": true, + "proof_event": 105 + }, + { + "command_id": "command-34042f5920b44c0f95154cf9d660256e", + "passed": true, + "proof_event": 109 + }, + { + "command_id": "command-2f22e759a1954eeabbc9940b75b07305", + "passed": true, + "proof_event": 112 + }, + { + "command_id": "command-606bcd1f8465444eba5e4196c237e89b", + "passed": true, + "proof_event": 115 + }, + { + "command_id": "command-b4aee31dc5994772a0c25ecdc773e084", + "passed": true, + "proof_event": 119 + }, + { + "command_id": "command-60d821af77f840a4ab884186f847060e", + "passed": true, + "proof_event": 122 + }, + { + "command_id": "command-5dd7dd1bc31b424a81eddcd302d5fb14", + "passed": true, + "proof_event": 125 + }, + { + "command_id": "command-3f9612fde2f84937923d289833d1cd95", + "passed": true, + "proof_event": 129 + }, + { + "command_id": "command-eca41d65274f4d50a46869065ea7bad5", + "passed": true, + "proof_event": 132 + }, + { + "command_id": "command-65e5701648ce496aa2a1433078aabc7f", + "passed": true, + "proof_event": 135 + }, + { + "command_id": "command-96098171ac4447cf9cb6a7b2facb798d", + "passed": true, + "proof_event": 139 + }, + { + "command_id": "command-3d83235444f841cdbba5d555c9d5246d", + "passed": true, + "proof_event": 142 + }, + { + "command_id": "command-047b4fe7d49c425c84abf5b3840b8f46", + "passed": true, + "proof_event": 145 + }, + { + "command_id": "command-4f154fdba3aa4493846a014f77143055", + "passed": true, + "proof_event": 149 + }, + { + "command_id": "command-8ef12068ba8a4f199cd3e55144180692", + "passed": true, + "proof_event": 152 + } + ], + "browser": { + "offline_then_paused": true, + "passed": true, + "steps": [ + "crash-offline", + "restored", + "resumed", + "network-uncertain", + "network-recovered", + "network-resumed", + "completed" + ] + }, + "calls": [ + { + "call": 1, + "camera_generation": 0, + "cancelled": true, + "challenge_id": "challenge-ba75092e251246d5a1a4fb7262a8a6ea", + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 50191, + "passed": true + }, + { + "action": "move", + "call": 2, + "camera_generation": 0, + "challenge_id": "challenge-ecba0f598dd449bc916feeaad2ffb3c7", + "command_id": "command-78798af6e0bb4e05b36d9c5a7ab38eeb", + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 57095, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-2aa1922cc75b43f7bfe7ca517b8dfae1" + }, + { + "action": "move", + "call": 3, + "camera_generation": 1, + "challenge_id": "challenge-a7fd32262d7644f1b93d9e8556275ee5", + "command_id": "command-384125e4ec1a4a92bb2af0f113d51d91", + "image_sha256": "a831bd4576c7fa2d1ccf8d5369de1a242e0292a976ad40da9c39304dd00d96e5", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 57526, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-4d49482125014d9f8fcdb676b18e5327" + }, + { + "action": "move", + "call": 4, + "camera_generation": 2, + "challenge_id": "challenge-9424010599ab484abcd567b40757e0e9", + "command_id": "command-385bfa9f62af41159e1b8c565679ffca", + "image_sha256": "42819fa0bc8837bbd91f1098939426208f093b3ed7e6a2e198d725dd1e53787e", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 58135, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-cac8e089cb4641bfb00485d6c536360b" + }, + { + "action": "measure", + "call": 5, + "camera_generation": 3, + "challenge_id": "challenge-9f9c487ec78e404692a31fd350f6109e", + "command_id": "command-b0e991c0c3e4431c9e13603a27769d1f", + "image_sha256": "b747f88e802c99f70a2caed1a1223c17887647bff6f5ab30b81c310055f20e0a", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 58551, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-1f718cced1634e7fadc18c52807d87bb" + }, + { + "action": "begin_reset", + "call": 6, + "camera_generation": 3, + "challenge_id": "challenge-f775a34c8903495e8113528369a6d0ed", + "command_id": "command-79f37126007249b5978bf0fc266cc1a4", + "image_sha256": "b747f88e802c99f70a2caed1a1223c17887647bff6f5ab30b81c310055f20e0a", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 69932, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-07157b6cf7604949b122db05c454b7ff" + }, + { + "action": "move", + "call": 7, + "camera_generation": 3, + "challenge_id": "challenge-5c51494846e546a79d45bbc69fb31a83", + "command_id": "command-0e35e264738a4e009107a506b92c91a7", + "image_sha256": "b747f88e802c99f70a2caed1a1223c17887647bff6f5ab30b81c310055f20e0a", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 70155, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-8b07c518ea61464e8916765e1aa94105" + }, + { + "action": "move", + "call": 8, + "camera_generation": 4, + "challenge_id": "challenge-11a1857c3561480fb3418cddc4d2500b", + "command_id": "command-4e66c543be0f422b9df61de5663c4c8e", + "image_sha256": "ca9c98a842818b470d65694e6d7161063ef45559683d673656aad7c787cd25ff", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 70426, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-49c8f04def1446e3b88d3fee479238f3" + }, + { + "action": "move", + "call": 9, + "camera_generation": 5, + "challenge_id": "challenge-7776216b6d18462a96e86651161d97a4", + "command_id": "command-9475473dbb394debb8016fef9a406bcd", + "image_sha256": "6190b019f225c3b7dbaa4974798da798787bbba6ade79b197f6fd8c5e3547e46", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 70720, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-110797a0a2824bc4b19aeff6c200288d" + }, + { + "action": "move", + "call": 10, + "camera_generation": 6, + "challenge_id": "challenge-f382bcabe663459089d5cc42a6d23d75", + "command_id": "command-355fcf7a02ad4b9bbaf14cc8ba2934f4", + "image_sha256": "bc7ea7cf98607fc2dcc490c5d234b59ad266652809ed972d188650a5a1a66d97", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 71023, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-80798459929a4118a49f4e48f9083eb8" + }, + { + "action": "move", + "call": 11, + "camera_generation": 7, + "challenge_id": "challenge-9b710e3a512b4ff8bd72f93c80b23553", + "command_id": "command-2f22e759a1954eeabbc9940b75b07305", + "image_sha256": "c4955a67bf00f4aedca9f61177cc50e16550508bb1469aaee9dd3cec6a3f3668", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 71288, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-7988174b4a90466a85faa9a327760389" + }, + { + "action": "move", + "call": 12, + "camera_generation": 8, + "challenge_id": "challenge-8583584b64d44f4eb2d705d20aad0ffc", + "command_id": "command-60d821af77f840a4ab884186f847060e", + "image_sha256": "fda5ca77879af6ad557e638569e8d7ba8d677c36f5d53600612401a82a1ed0d8", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 71562, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-dc4b91b0cd014d548d7bba4dd2c77951" + }, + { + "action": "move", + "call": 13, + "camera_generation": 9, + "challenge_id": "challenge-4321cf322a1040d1bcc601bcd1015555", + "command_id": "command-eca41d65274f4d50a46869065ea7bad5", + "image_sha256": "93e762ee1356542f0a026394189aa27d0149478433ff0809eec1d7148f682a9a", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 71977, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-4d758e9db26b4282b268ff2de543aae1" + }, + { + "action": "move", + "call": 14, + "camera_generation": 10, + "challenge_id": "challenge-a4b6472d2af149c4938299d6285ccc0f", + "command_id": "command-3d83235444f841cdbba5d555c9d5246d", + "image_sha256": "3a0658fbcc98db2233e01c3cf4d4dcf31eaaca13ff25f778165da8d6bb180be0", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 72280, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-a04263ab4f284fa6837ea88eebe23c5c" + }, + { + "action": "finish_reset", + "call": 15, + "camera_generation": 11, + "challenge_id": "challenge-73ea6cac4a2447b79306436730aabfe7", + "command_id": "command-8ef12068ba8a4f199cd3e55144180692", + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "model_pid": 74506, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-5eff7045e6034329813e89f53ba2d486" + } + ], + "cancelled_calls": 1, + "case": "host-planning", + "completed_cycles": 1, + "crash": { + "fresh_challenge": "challenge-ecba0f598dd449bc916feeaad2ffb3c7", + "host_pid_only": true, + "original_commands_preserved": true, + "passed": true, + "resume_sequence": 10 + }, + "event_cutoff": 154, + "native": { + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "measurement_receipt": { + "bench_id": "d2820395d03d485492e1225f1972a12d", + "generation": 3, + "image_sha256": "b747f88e802c99f70a2caed1a1223c17887647bff6f5ab30b81c310055f20e0a", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 6.535999999999502, + "operation_id": "agent-5152e9c248aa46bca03709a0de929e28", + "source_kind": "mujoco_simulation", + "state_sha256": "e3c670edb30212850ba6564011f4e07838e1ee8785a2d314fe79d628051de929", + "version": 1, + "worker_pid": 59003 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06580393724193996, + -1.5294247501940597e-10, + 0.034995730455369786 + ], + "external_force_cleared": true, + "generation": 3, + "native_time_s": 6.535999999999502, + "pusher_home": false, + "pusher_xyz": [ + -0.04999996940907919, + 1.8429965196805716e-26, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "e3c670edb30212850ba6564011f4e07838e1ee8785a2d314fe79d628051de929" + }, + "model_calls": 15, + "passed": true, + "reset_receipt": { + "bench_id": "d2820395d03d485492e1225f1972a12d", + "block_home_verified": true, + "generation": 11, + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "kind": "finish_reset", + "native_time_s": 22.94799999999816, + "operation_id": "agent-e8c188b67ec74303916d4a975c30c34e", + "pusher_home_verified": true, + "reset_contact_samples": 3132, + "reset_native_steps": 8206, + "state_sha256": "31f1e85639b1f4712ff98a8b2c3158de32f0e08e3204141921cfac1648fe0b6e" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11576632483668478, + -2.4280269049159656e-09, + 0.03499573045536984 + ], + "external_force_cleared": true, + "generation": 11, + "native_time_s": 22.94799999999816, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "31f1e85639b1f4712ff98a8b2c3158de32f0e08e3204141921cfac1648fe0b6e" + } + }, + "owned_processes_exited": true, + "passed": true, + "scope": "selected event publication and active-planning host SIGKILL; loopback simulation", + "single_wire_post_every_command": true, + "source_commit": "c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54", + "source_unchanged": true, + "sqlite": { + "count": 1, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "d2820395d03d485492e1225f1972a12d", + "generation": 3, + "image_sha256": "b747f88e802c99f70a2caed1a1223c17887647bff6f5ab30b81c310055f20e0a", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "operation_id": "agent-5152e9c248aa46bca03709a0de929e28", + "receipt": { + "bench_id": "d2820395d03d485492e1225f1972a12d", + "generation": 3, + "image_sha256": "b747f88e802c99f70a2caed1a1223c17887647bff6f5ab30b81c310055f20e0a", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 6.535999999999502, + "operation_id": "agent-5152e9c248aa46bca03709a0de929e28", + "source_kind": "mujoco_simulation", + "state_sha256": "e3c670edb30212850ba6564011f4e07838e1ee8785a2d314fe79d628051de929", + "version": 1, + "worker_pid": 59003 + }, + "state_sha256": "e3c670edb30212850ba6564011f4e07838e1ee8785a2d314fe79d628051de929" + } + ] + }, + "transport_command_count": 44, + "version": 1 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/snapshots/000001-before.json b/jevdual/results/reusable-body-recovery-20261012/planning/snapshots/000001-before.json new file mode 100644 index 0000000..fe6fc45 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/snapshots/000001-before.json @@ -0,0 +1,142 @@ +{ + "body_command": null, + "body_identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "moves": 0, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "d2820395d03d485492e1225f1972a12d", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-7d500c79f03142ecb999bacab4d5a0a2", + "epoch": 1, + "expires_in_s": 28.932480374816805, + "lease_id": "9f787a7440f84c489233400d2f929518" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-3f6a2226f09f40d3a50d6b5c8d4a5f7c": { + "operation": "observe", + "request_sha256": "aca734b224de4e8d8632c57c911cb12abac39464d0fef4eb6f3f879eb1e390c9", + "send_count": 1, + "state": "completed" + }, + "command-6e87e72b25954bf8ae8571cb370485ca": { + "operation": "start_job", + "request_sha256": "824c68663aad317b4329f1c6a6224342440c9f9361f12bda6c8c50988da641ab", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-7d500c79f03142ecb999bacab4d5a0a2", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "9f787a7440f84c489233400d2f929518" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "body_identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/operator/client/images/challenge-ba75092e251246d5a1a4fb7262a8a6ea-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "planner_owner": "f23b991cbd0a42f68913ce99df3a6f22", + "runner_status": "running", + "sequence": 8, + "stage": null, + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0 + } + }, + "label": "before", + "posts": [ + { + "command_id": "command-6e87e72b25954bf8ae8571cb370485ca", + "payload_sha256": "824c68663aad317b4329f1c6a6224342440c9f9361f12bda6c8c50988da641ab", + "sequence": 8 + }, + { + "command_id": "command-3f6a2226f09f40d3a50d6b5c8d4a5f7c", + "payload_sha256": "aca734b224de4e8d8632c57c911cb12abac39464d0fef4eb6f3f879eb1e390c9", + "sequence": 16 + } + ], + "processes": { + "body": 49738, + "workbench": 49761 + }, + "public": null, + "time": 1791860179.3760471 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/snapshots/000002-offline.json b/jevdual/results/reusable-body-recovery-20261012/planning/snapshots/000002-offline.json new file mode 100644 index 0000000..efda405 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/snapshots/000002-offline.json @@ -0,0 +1,142 @@ +{ + "body_command": null, + "body_identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "moves": 0, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "d2820395d03d485492e1225f1972a12d", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-7d500c79f03142ecb999bacab4d5a0a2", + "epoch": 1, + "expires_in_s": 28.826274332823232, + "lease_id": "9f787a7440f84c489233400d2f929518" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-3f6a2226f09f40d3a50d6b5c8d4a5f7c": { + "operation": "observe", + "request_sha256": "aca734b224de4e8d8632c57c911cb12abac39464d0fef4eb6f3f879eb1e390c9", + "send_count": 1, + "state": "completed" + }, + "command-6e87e72b25954bf8ae8571cb370485ca": { + "operation": "start_job", + "request_sha256": "824c68663aad317b4329f1c6a6224342440c9f9361f12bda6c8c50988da641ab", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-7d500c79f03142ecb999bacab4d5a0a2", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "9f787a7440f84c489233400d2f929518" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "body_identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/operator/client/images/challenge-ba75092e251246d5a1a4fb7262a8a6ea-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "planner_owner": "f23b991cbd0a42f68913ce99df3a6f22", + "runner_status": "running", + "sequence": 8, + "stage": null, + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0 + } + }, + "label": "offline", + "posts": [ + { + "command_id": "command-6e87e72b25954bf8ae8571cb370485ca", + "payload_sha256": "824c68663aad317b4329f1c6a6224342440c9f9361f12bda6c8c50988da641ab", + "sequence": 8 + }, + { + "command_id": "command-3f6a2226f09f40d3a50d6b5c8d4a5f7c", + "payload_sha256": "aca734b224de4e8d8632c57c911cb12abac39464d0fef4eb6f3f879eb1e390c9", + "sequence": 16 + } + ], + "processes": { + "body": 49738, + "workbench": null + }, + "public": null, + "time": 1791860179.482015 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/snapshots/000003-restored.json b/jevdual/results/reusable-body-recovery-20261012/planning/snapshots/000003-restored.json new file mode 100644 index 0000000..51d4b70 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/snapshots/000003-restored.json @@ -0,0 +1,282 @@ +{ + "body_command": null, + "body_identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "moves": 0, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "d2820395d03d485492e1225f1972a12d", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-7d500c79f03142ecb999bacab4d5a0a2", + "epoch": 1, + "expires_in_s": 0, + "lease_id": "9f787a7440f84c489233400d2f929518" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-3f6a2226f09f40d3a50d6b5c8d4a5f7c": { + "operation": "observe", + "request_sha256": "aca734b224de4e8d8632c57c911cb12abac39464d0fef4eb6f3f879eb1e390c9", + "send_count": 1, + "state": "completed" + }, + "command-6e87e72b25954bf8ae8571cb370485ca": { + "operation": "start_job", + "request_sha256": "824c68663aad317b4329f1c6a6224342440c9f9361f12bda6c8c50988da641ab", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-7d500c79f03142ecb999bacab4d5a0a2", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "9f787a7440f84c489233400d2f929518" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "body_identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/operator/client/images/challenge-ba75092e251246d5a1a4fb7262a8a6ea-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "planner_owner": "83ffafef07084b55a7dee3512d706ca7", + "runner_status": "paused", + "sequence": 9, + "stage": null, + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0 + } + }, + "label": "restored", + "posts": [ + { + "command_id": "command-6e87e72b25954bf8ae8571cb370485ca", + "payload_sha256": "824c68663aad317b4329f1c6a6224342440c9f9361f12bda6c8c50988da641ab", + "sequence": 8 + }, + { + "command_id": "command-3f6a2226f09f40d3a50d6b5c8d4a5f7c", + "payload_sha256": "aca734b224de4e8d8632c57c911cb12abac39464d0fef4eb6f3f879eb1e390c9", + "sequence": 16 + } + ], + "processes": { + "body": 49738, + "workbench": 56155 + }, + "public": { + "active_job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "activity": [ + { + "intent": "Retire planner and recover reply", + "kind": "queued", + "outcome": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "seq": 1, + "time": 1791860178.2600389 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "9f2ea4a53b7fb698ae92d96b09a8bca23606c20f2937a470dbd6f205534216e1" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "start_job", + "seq": 2, + "time": 1791860178.269863 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 3, + "time": 1791860178.281921 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "start_job: command-6e87e72b25954bf8ae8571cb370485ca", + "seq": 4, + "time": 1791860178.295229 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "701dd8e778fe5bb6994985e7c2f28e3fb68a48851aaad24d968b795811594e44" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 5, + "time": 1791860178.30598 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 6, + "time": 1791860178.3193002 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "observe: command-3f6a2226f09f40d3a50d6b5c8d4a5f7c", + "seq": 7, + "time": 1791860178.5038688 + }, + { + "intent": "Retire planner and recover reply", + "kind": "inference", + "outcome": "1", + "seq": 8, + "time": 1791860178.505256 + }, + { + "evidence": { + "calls": [ + { + "call": "0bc391fc-81d4-4d6a-a267-965b153f8cab-01", + "reason": "host liveness pipe closed" + } + ] + }, + "intent": "", + "kind": "planner_retired", + "outcome": "Prior planner retired; explicit Resume obtains a fresh image", + "seq": 9, + "time": 1791860318.091291 + } + ], + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "connection": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Prior planner retired; explicit Resume obtains a fresh image", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "label": "Retire planner and recover reply", + "last_intent": "", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": false, + "reason": "Prior planner retired; explicit Resume obtains a fresh image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No uncertain command", + "state": "none" + }, + "runner_status": "paused", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0, + "token_usage_complete": false + } + }, + "time": 1791860318.1755378 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/snapshots/000004-finished.json b/jevdual/results/reusable-body-recovery-20261012/planning/snapshots/000004-finished.json new file mode 100644 index 0000000..a4df70d --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/snapshots/000004-finished.json @@ -0,0 +1,930 @@ +{ + "body_command": null, + "body_identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "body_state": { + "bench": { + "active_job": null, + "bench_id": "d2820395d03d485492e1225f1972a12d", + "diagnostic_enabled": true, + "generation": 11, + "jobs": { + "0bc391fc-81d4-4d6a-a267-965b153f8cab": { + "completed_generation": 11, + "completed_native_time_s": 22.94799999999816, + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "measurement_operation_id": "agent-5152e9c248aa46bca03709a0de929e28", + "measurement_receipt": { + "bench_id": "d2820395d03d485492e1225f1972a12d", + "generation": 3, + "image_sha256": "b747f88e802c99f70a2caed1a1223c17887647bff6f5ab30b81c310055f20e0a", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 6.535999999999502, + "operation_id": "agent-5152e9c248aa46bca03709a0de929e28", + "source_kind": "mujoco_simulation", + "state_sha256": "e3c670edb30212850ba6564011f4e07838e1ee8785a2d314fe79d628051de929", + "version": 1, + "worker_pid": 59003 + }, + "moves": 11, + "phase": "completed", + "reset_receipt": { + "bench_id": "d2820395d03d485492e1225f1972a12d", + "block_home_verified": true, + "generation": 11, + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "kind": "finish_reset", + "native_time_s": 22.94799999999816, + "operation_id": "agent-e8c188b67ec74303916d4a975c30c34e", + "pusher_home_verified": true, + "reset_contact_samples": 3132, + "reset_native_steps": 8206, + "state_sha256": "31f1e85639b1f4712ff98a8b2c3158de32f0e08e3204141921cfac1648fe0b6e" + }, + "reset_started_generation": 3, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 22.94799999999816, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-7d500c79f03142ecb999bacab4d5a0a2", + "epoch": 1, + "expires_in_s": 0, + "lease_id": "e514cf3d683e44748e0d24ead51c8391" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-0348a03b292645caa7ab7bbc9b0f4b9d": { + "operation": "observe", + "request_sha256": "43f706b4a6af79d86ceda9b79a6fc25b2fea13ec3586942f053e70b2c6af3981", + "send_count": 1, + "state": "completed" + }, + "command-03a14ca1a1ef4804a6e4b12eea2cd182": { + "operation": "propose", + "request_sha256": "e885babfcb6b85b874f5c035b336901648da1c21d838050829e6cccd0bae6dbb", + "send_count": 1, + "state": "completed" + }, + "command-047b4fe7d49c425c84abf5b3840b8f46": { + "operation": "observe", + "request_sha256": "e9d677de972e1735e2aa9ccf280c05e1b3d546ef5ed0167afcbc4471df273654", + "send_count": 1, + "state": "completed" + }, + "command-082b9bbe08cc43e8b3e31d5e2b81c3b0": { + "operation": "propose", + "request_sha256": "aa75eccbb846bede9149a5902a9013604f19c354ea2a0a81f4d7537bd7a94da2", + "send_count": 1, + "state": "completed" + }, + "command-0e35e264738a4e009107a506b92c91a7": { + "operation": "dispatch", + "request_sha256": "a8b1102a3fd567650c4e36a12c058106d8ad44194bc4682868b35ba02e470da8", + "send_count": 1, + "state": "completed" + }, + "command-216a23fd106c4c808cf8845276bfbffe": { + "operation": "observe", + "request_sha256": "c5c1f8d4bbccb0b799a95e8eb9260f6b1ceed7429d0b7000155d7b428118ac5c", + "send_count": 1, + "state": "completed" + }, + "command-2f22e759a1954eeabbc9940b75b07305": { + "operation": "dispatch", + "request_sha256": "4058646bc3cc2e8634383e11bdbe861ece2196884816a9b69a8efdec299a0184", + "send_count": 1, + "state": "completed" + }, + "command-34042f5920b44c0f95154cf9d660256e": { + "operation": "propose", + "request_sha256": "912d826357d97fa2901156d814009a49730c869b5d02c095c26fd0da9b245ecb", + "send_count": 1, + "state": "completed" + }, + "command-355fcf7a02ad4b9bbaf14cc8ba2934f4": { + "operation": "dispatch", + "request_sha256": "aa7db5c19e3567b96e181a9791d1f4e7ce201b62c65bd69ddffb979cbafd5ed9", + "send_count": 1, + "state": "completed" + }, + "command-384125e4ec1a4a92bb2af0f113d51d91": { + "operation": "dispatch", + "request_sha256": "ca8a69a3f1ce77ca40240559a7b376afb1a671119dd9db5fb80c548811b978b4", + "send_count": 1, + "state": "completed" + }, + "command-385bfa9f62af41159e1b8c565679ffca": { + "operation": "dispatch", + "request_sha256": "7f45ddd193dc9440ad1edd6b2b56d543cb3adef69bf75aa90806ed12fb0ff4ac", + "send_count": 1, + "state": "completed" + }, + "command-3d83235444f841cdbba5d555c9d5246d": { + "operation": "dispatch", + "request_sha256": "d1533a9abf9c13845c1fd816c89652d19d4cdf41f6d17b444b03b7576da5a95d", + "send_count": 1, + "state": "completed" + }, + "command-3f6a2226f09f40d3a50d6b5c8d4a5f7c": { + "operation": "observe", + "request_sha256": "aca734b224de4e8d8632c57c911cb12abac39464d0fef4eb6f3f879eb1e390c9", + "send_count": 1, + "state": "completed" + }, + "command-3f9612fde2f84937923d289833d1cd95": { + "operation": "propose", + "request_sha256": "f94bf0ed61b3f7f921f73170767edbab226db703fd83f4f89ff16a105db10f8c", + "send_count": 1, + "state": "completed" + }, + "command-4264ce8fdc0f4762900cc9d40e33a0b0": { + "operation": "observe", + "request_sha256": "c97b3ffd16490bd0de61bb2b73d9ef5c1df71bb4b9c12ee8836a05d59b3cefaf", + "send_count": 1, + "state": "completed" + }, + "command-47d5a88d55ed4ff39b76af5cb295fdb4": { + "operation": "propose", + "request_sha256": "0c9da6bd680f951169cc2ef77f14a76f7509f8615e8e53c74a009461c981115a", + "send_count": 1, + "state": "completed" + }, + "command-4e66c543be0f422b9df61de5663c4c8e": { + "operation": "dispatch", + "request_sha256": "bc5ab7b04c58004c4712aee2e742ef303856d3d7ada81fc59051445ffe151efc", + "send_count": 1, + "state": "completed" + }, + "command-4f154fdba3aa4493846a014f77143055": { + "operation": "propose", + "request_sha256": "335d1e14f37a1dc3ccbcc9c7a2847b4c692ce4c36cd0524944f3f6cf2c04efa5", + "send_count": 1, + "state": "completed" + }, + "command-5dd7dd1bc31b424a81eddcd302d5fb14": { + "operation": "observe", + "request_sha256": "d52264dcfb3c2de26acd7edee81cd24ac54dad76c1bba6c88576e0b5f71caf3a", + "send_count": 1, + "state": "completed" + }, + "command-606bcd1f8465444eba5e4196c237e89b": { + "operation": "observe", + "request_sha256": "65937dd2b8bb80067bccc2cb0889e07500d349eec36bd855ca0290519b490549", + "send_count": 1, + "state": "completed" + }, + "command-60d821af77f840a4ab884186f847060e": { + "operation": "dispatch", + "request_sha256": "af1aad2341c554fa1fb5dd2f3251a29b246b838e3ef116c87ff56d464d8f5562", + "send_count": 1, + "state": "completed" + }, + "command-65e5701648ce496aa2a1433078aabc7f": { + "operation": "observe", + "request_sha256": "d5628961581aaf2c1196208155ddb6d33a31cd264db97f99aac1735d88c54dde", + "send_count": 1, + "state": "completed" + }, + "command-689742f4520c4c6abb4c674eb93aa7aa": { + "operation": "observe", + "request_sha256": "1c91b923267ebec498c6e98007b7d3caf43529162a8a4a10ab83e32d68140a2a", + "send_count": 1, + "state": "completed" + }, + "command-6e87e72b25954bf8ae8571cb370485ca": { + "operation": "start_job", + "request_sha256": "824c68663aad317b4329f1c6a6224342440c9f9361f12bda6c8c50988da641ab", + "send_count": 1, + "state": "completed" + }, + "command-70dd7efe587f4813bfc5ef426de0a21d": { + "operation": "propose", + "request_sha256": "45ce03d0c45782c9fd8675ba37854802bd86a911c34de18ccf3845c4162a5159", + "send_count": 1, + "state": "completed" + }, + "command-78798af6e0bb4e05b36d9c5a7ab38eeb": { + "operation": "dispatch", + "request_sha256": "4cea247d772d8911b53ebc42d8740f8346a7ca5976d038057c628c771fa19156", + "send_count": 1, + "state": "completed" + }, + "command-79f37126007249b5978bf0fc266cc1a4": { + "operation": "dispatch", + "request_sha256": "96a530ac76395468d01068b870953fc38c8c36bd60f61da318e4fcfd2a3b775a", + "send_count": 1, + "state": "completed" + }, + "command-7bacb0429d414bc69c28a0af49c45bdb": { + "operation": "propose", + "request_sha256": "8492e5485e3dfa2d2733df2215b989b3e3acc0587be73916c32d71dc1955daba", + "send_count": 1, + "state": "completed" + }, + "command-8ef12068ba8a4f199cd3e55144180692": { + "operation": "dispatch", + "request_sha256": "627bc712c98b5427e01d8e40bed7d8eb87b2f423ff252aaac6cb1b6b7bd6286b", + "send_count": 1, + "state": "completed" + }, + "command-9475473dbb394debb8016fef9a406bcd": { + "operation": "dispatch", + "request_sha256": "178d25203bce834fdf4391e48cb9a64a4e2467fbd7f6f568e52b204f3dd5db7e", + "send_count": 1, + "state": "completed" + }, + "command-96098171ac4447cf9cb6a7b2facb798d": { + "operation": "propose", + "request_sha256": "cc7d27b672ab5b7ddda0f042585865baf86b0ab9bdc2b7084526b8d77819c1de", + "send_count": 1, + "state": "completed" + }, + "command-98ae2724fb4c4defb962a9d3ee33a32a": { + "operation": "observe", + "request_sha256": "79d40a7201ba54ed4d55777ec4bd8a12c77d05ec857b8b74b9a04858a401682c", + "send_count": 1, + "state": "completed" + }, + "command-a52231151c274b47a909105357d1d095": { + "operation": "propose", + "request_sha256": "525be1bea435fafb7f99e708c8fb406afef3f08e8d421d5ffb5f00b09b5a78f7", + "send_count": 1, + "state": "completed" + }, + "command-a5559c21051e40b89e77c2f0c8c3a457": { + "operation": "observe", + "request_sha256": "c5ce286f0732bfe4166c208a0ff3d8622d7fd8e8dac1c785aef4520e572f38be", + "send_count": 1, + "state": "completed" + }, + "command-b0e991c0c3e4431c9e13603a27769d1f": { + "operation": "dispatch", + "request_sha256": "02a72a627b92673a18103314a4773d3c0772b504787fc9ed9d6d9316766f00ee", + "send_count": 1, + "state": "completed" + }, + "command-b4aee31dc5994772a0c25ecdc773e084": { + "operation": "propose", + "request_sha256": "3cc10c9700768ce974b8c8c67f0649e37f565c8e8a624412c61903711e715bec", + "send_count": 1, + "state": "completed" + }, + "command-bc99fd2e782a41e4bbb14592b557bc8b": { + "operation": "propose", + "request_sha256": "7764506d594a7ab402f275d04e8bfd47225875052b1300ec6fa0964b58a58bab", + "send_count": 1, + "state": "completed" + }, + "command-c41d875cd39c440599ba1bc4f4530fbf": { + "operation": "observe", + "request_sha256": "84cd59e835170a636e8b4feb0b9df3983d83dc5ff9b0f7d94a615f9c8f7c67f3", + "send_count": 1, + "state": "completed" + }, + "command-d16da3a2381e44188165e2a8f220b3cf": { + "operation": "propose", + "request_sha256": "1b85e7707a60e63c6f7f34633efce9803bac9f8b1dbc32f9f659b35ad77f4cb6", + "send_count": 1, + "state": "completed" + }, + "command-d55505df042941caab8fde61decfc140": { + "operation": "observe", + "request_sha256": "4f30386918651a35dfc28bc7b8dca60a877d6191125e099013062eb9caab9752", + "send_count": 1, + "state": "completed" + }, + "command-e64acab86d6d4ef889fda2e1532edac2": { + "operation": "propose", + "request_sha256": "31f9afe60db97dde6424074d43288c44a4a79dd91a94800643d539136d2f56e6", + "send_count": 1, + "state": "completed" + }, + "command-e7659a3f8a1c46e38d8f7ba91344e4f1": { + "operation": "observe", + "request_sha256": "6fb2b7ca364f65f8a63ff594a143118381629e0845f74040cc9c2ef2d7a25647", + "send_count": 1, + "state": "completed" + }, + "command-eca41d65274f4d50a46869065ea7bad5": { + "operation": "dispatch", + "request_sha256": "9272a39f361a43c74de3f323836af7f6b5d2a2c8f163ec7756fe5db5634deff4", + "send_count": 1, + "state": "completed" + }, + "command-fcc2df15c8b948a29a01ca1c8f723a19": { + "operation": "observe", + "request_sha256": "31a77f354a376086ab3f227d7a7688b19d628284ff696b1076ef44863bec3ef4", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-7d500c79f03142ecb999bacab4d5a0a2", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "e514cf3d683e44748e0d24ead51c8391" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "body_identity": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "frame": { + "generation": 11, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/operator/client/images/challenge-73ea6cac4a2447b79306436730aabfe7-970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f.png", + "sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f" + }, + "planner_owner": "83ffafef07084b55a7dee3512d706ca7", + "runner_status": "running", + "sequence": 154, + "stage": null, + "usage": { + "calls": 15, + "input_tokens": 183745, + "output_tokens": 2157, + "reported_calls": 14 + } + }, + "label": "finished", + "posts": [ + { + "command_id": "command-6e87e72b25954bf8ae8571cb370485ca", + "payload_sha256": "824c68663aad317b4329f1c6a6224342440c9f9361f12bda6c8c50988da641ab", + "sequence": 8 + }, + { + "command_id": "command-3f6a2226f09f40d3a50d6b5c8d4a5f7c", + "payload_sha256": "aca734b224de4e8d8632c57c911cb12abac39464d0fef4eb6f3f879eb1e390c9", + "sequence": 16 + }, + { + "command_id": "command-689742f4520c4c6abb4c674eb93aa7aa", + "payload_sha256": "1c91b923267ebec498c6e98007b7d3caf43529162a8a4a10ab83e32d68140a2a", + "sequence": 29 + }, + { + "command_id": "command-03a14ca1a1ef4804a6e4b12eea2cd182", + "payload_sha256": "e885babfcb6b85b874f5c035b336901648da1c21d838050829e6cccd0bae6dbb", + "sequence": 41 + }, + { + "command_id": "command-78798af6e0bb4e05b36d9c5a7ab38eeb", + "payload_sha256": "4cea247d772d8911b53ebc42d8740f8346a7ca5976d038057c628c771fa19156", + "sequence": 49 + }, + { + "command_id": "command-4264ce8fdc0f4762900cc9d40e33a0b0", + "payload_sha256": "c97b3ffd16490bd0de61bb2b73d9ef5c1df71bb4b9c12ee8836a05d59b3cefaf", + "sequence": 57 + }, + { + "command_id": "command-e64acab86d6d4ef889fda2e1532edac2", + "payload_sha256": "31f9afe60db97dde6424074d43288c44a4a79dd91a94800643d539136d2f56e6", + "sequence": 69 + }, + { + "command_id": "command-384125e4ec1a4a92bb2af0f113d51d91", + "payload_sha256": "ca8a69a3f1ce77ca40240559a7b376afb1a671119dd9db5fb80c548811b978b4", + "sequence": 77 + }, + { + "command_id": "command-98ae2724fb4c4defb962a9d3ee33a32a", + "payload_sha256": "79d40a7201ba54ed4d55777ec4bd8a12c77d05ec857b8b74b9a04858a401682c", + "sequence": 85 + }, + { + "command_id": "command-082b9bbe08cc43e8b3e31d5e2b81c3b0", + "payload_sha256": "aa75eccbb846bede9149a5902a9013604f19c354ea2a0a81f4d7537bd7a94da2", + "sequence": 97 + }, + { + "command_id": "command-385bfa9f62af41159e1b8c565679ffca", + "payload_sha256": "7f45ddd193dc9440ad1edd6b2b56d543cb3adef69bf75aa90806ed12fb0ff4ac", + "sequence": 105 + }, + { + "command_id": "command-fcc2df15c8b948a29a01ca1c8f723a19", + "payload_sha256": "31a77f354a376086ab3f227d7a7688b19d628284ff696b1076ef44863bec3ef4", + "sequence": 113 + }, + { + "command_id": "command-47d5a88d55ed4ff39b76af5cb295fdb4", + "payload_sha256": "0c9da6bd680f951169cc2ef77f14a76f7509f8615e8e53c74a009461c981115a", + "sequence": 126 + }, + { + "command_id": "command-b0e991c0c3e4431c9e13603a27769d1f", + "payload_sha256": "02a72a627b92673a18103314a4773d3c0772b504787fc9ed9d6d9316766f00ee", + "sequence": 134 + }, + { + "command_id": "command-d55505df042941caab8fde61decfc140", + "payload_sha256": "4f30386918651a35dfc28bc7b8dca60a877d6191125e099013062eb9caab9752", + "sequence": 146 + }, + { + "command_id": "command-70dd7efe587f4813bfc5ef426de0a21d", + "payload_sha256": "45ce03d0c45782c9fd8675ba37854802bd86a911c34de18ccf3845c4162a5159", + "sequence": 158 + }, + { + "command_id": "command-79f37126007249b5978bf0fc266cc1a4", + "payload_sha256": "96a530ac76395468d01068b870953fc38c8c36bd60f61da318e4fcfd2a3b775a", + "sequence": 166 + }, + { + "command_id": "command-c41d875cd39c440599ba1bc4f4530fbf", + "payload_sha256": "84cd59e835170a636e8b4feb0b9df3983d83dc5ff9b0f7d94a615f9c8f7c67f3", + "sequence": 174 + }, + { + "command_id": "command-7bacb0429d414bc69c28a0af49c45bdb", + "payload_sha256": "8492e5485e3dfa2d2733df2215b989b3e3acc0587be73916c32d71dc1955daba", + "sequence": 186 + }, + { + "command_id": "command-0e35e264738a4e009107a506b92c91a7", + "payload_sha256": "a8b1102a3fd567650c4e36a12c058106d8ad44194bc4682868b35ba02e470da8", + "sequence": 194 + }, + { + "command_id": "command-0348a03b292645caa7ab7bbc9b0f4b9d", + "payload_sha256": "43f706b4a6af79d86ceda9b79a6fc25b2fea13ec3586942f053e70b2c6af3981", + "sequence": 202 + }, + { + "command_id": "command-bc99fd2e782a41e4bbb14592b557bc8b", + "payload_sha256": "7764506d594a7ab402f275d04e8bfd47225875052b1300ec6fa0964b58a58bab", + "sequence": 214 + }, + { + "command_id": "command-4e66c543be0f422b9df61de5663c4c8e", + "payload_sha256": "bc5ab7b04c58004c4712aee2e742ef303856d3d7ada81fc59051445ffe151efc", + "sequence": 222 + }, + { + "command_id": "command-216a23fd106c4c808cf8845276bfbffe", + "payload_sha256": "c5c1f8d4bbccb0b799a95e8eb9260f6b1ceed7429d0b7000155d7b428118ac5c", + "sequence": 230 + }, + { + "command_id": "command-d16da3a2381e44188165e2a8f220b3cf", + "payload_sha256": "1b85e7707a60e63c6f7f34633efce9803bac9f8b1dbc32f9f659b35ad77f4cb6", + "sequence": 242 + }, + { + "command_id": "command-9475473dbb394debb8016fef9a406bcd", + "payload_sha256": "178d25203bce834fdf4391e48cb9a64a4e2467fbd7f6f568e52b204f3dd5db7e", + "sequence": 250 + }, + { + "command_id": "command-e7659a3f8a1c46e38d8f7ba91344e4f1", + "payload_sha256": "6fb2b7ca364f65f8a63ff594a143118381629e0845f74040cc9c2ef2d7a25647", + "sequence": 258 + }, + { + "command_id": "command-a52231151c274b47a909105357d1d095", + "payload_sha256": "525be1bea435fafb7f99e708c8fb406afef3f08e8d421d5ffb5f00b09b5a78f7", + "sequence": 270 + }, + { + "command_id": "command-355fcf7a02ad4b9bbaf14cc8ba2934f4", + "payload_sha256": "aa7db5c19e3567b96e181a9791d1f4e7ce201b62c65bd69ddffb979cbafd5ed9", + "sequence": 278 + }, + { + "command_id": "command-a5559c21051e40b89e77c2f0c8c3a457", + "payload_sha256": "c5ce286f0732bfe4166c208a0ff3d8622d7fd8e8dac1c785aef4520e572f38be", + "sequence": 286 + }, + { + "command_id": "command-34042f5920b44c0f95154cf9d660256e", + "payload_sha256": "912d826357d97fa2901156d814009a49730c869b5d02c095c26fd0da9b245ecb", + "sequence": 298 + }, + { + "command_id": "command-2f22e759a1954eeabbc9940b75b07305", + "payload_sha256": "4058646bc3cc2e8634383e11bdbe861ece2196884816a9b69a8efdec299a0184", + "sequence": 306 + }, + { + "command_id": "command-606bcd1f8465444eba5e4196c237e89b", + "payload_sha256": "65937dd2b8bb80067bccc2cb0889e07500d349eec36bd855ca0290519b490549", + "sequence": 314 + }, + { + "command_id": "command-b4aee31dc5994772a0c25ecdc773e084", + "payload_sha256": "3cc10c9700768ce974b8c8c67f0649e37f565c8e8a624412c61903711e715bec", + "sequence": 326 + }, + { + "command_id": "command-60d821af77f840a4ab884186f847060e", + "payload_sha256": "af1aad2341c554fa1fb5dd2f3251a29b246b838e3ef116c87ff56d464d8f5562", + "sequence": 334 + }, + { + "command_id": "command-5dd7dd1bc31b424a81eddcd302d5fb14", + "payload_sha256": "d52264dcfb3c2de26acd7edee81cd24ac54dad76c1bba6c88576e0b5f71caf3a", + "sequence": 342 + }, + { + "command_id": "command-3f9612fde2f84937923d289833d1cd95", + "payload_sha256": "f94bf0ed61b3f7f921f73170767edbab226db703fd83f4f89ff16a105db10f8c", + "sequence": 354 + }, + { + "command_id": "command-eca41d65274f4d50a46869065ea7bad5", + "payload_sha256": "9272a39f361a43c74de3f323836af7f6b5d2a2c8f163ec7756fe5db5634deff4", + "sequence": 362 + }, + { + "command_id": "command-65e5701648ce496aa2a1433078aabc7f", + "payload_sha256": "d5628961581aaf2c1196208155ddb6d33a31cd264db97f99aac1735d88c54dde", + "sequence": 370 + }, + { + "command_id": "command-96098171ac4447cf9cb6a7b2facb798d", + "payload_sha256": "cc7d27b672ab5b7ddda0f042585865baf86b0ab9bdc2b7084526b8d77819c1de", + "sequence": 382 + }, + { + "command_id": "command-3d83235444f841cdbba5d555c9d5246d", + "payload_sha256": "d1533a9abf9c13845c1fd816c89652d19d4cdf41f6d17b444b03b7576da5a95d", + "sequence": 390 + }, + { + "command_id": "command-047b4fe7d49c425c84abf5b3840b8f46", + "payload_sha256": "e9d677de972e1735e2aa9ccf280c05e1b3d546ef5ed0167afcbc4471df273654", + "sequence": 398 + }, + { + "command_id": "command-4f154fdba3aa4493846a014f77143055", + "payload_sha256": "335d1e14f37a1dc3ccbcc9c7a2847b4c692ce4c36cd0524944f3f6cf2c04efa5", + "sequence": 410 + }, + { + "command_id": "command-8ef12068ba8a4f199cd3e55144180692", + "payload_sha256": "627bc712c98b5427e01d8e40bed7d8eb87b2f423ff252aaac6cb1b6b7bd6286b", + "sequence": 418 + } + ], + "processes": { + "body": 49738, + "workbench": 56155 + }, + "public": { + "active_job_id": null, + "activity": [ + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "propose: command-3f9612fde2f84937923d289833d1cd95", + "seq": 131, + "time": 1791860859.06847 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "9b96fd8f2ab49f4a8b8ec92a9b8651061a73355c22526098ce3ca044ed4dd440" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 132, + "time": 1791860859.122879 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 133, + "time": 1791860859.162477 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "dispatch: command-eca41d65274f4d50a46869065ea7bad5", + "seq": 134, + "time": 1791860859.371604 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "d291139d0c6ba08c36f28acac8990f595344de8d206c27712639b5b5af8b1eb9" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 135, + "time": 1791860859.427273 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 136, + "time": 1791860859.468482 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "observe: command-65e5701648ce496aa2a1433078aabc7f", + "seq": 137, + "time": 1791860859.645217 + }, + { + "intent": "Retire planner and recover reply", + "kind": "inference", + "outcome": "14", + "seq": 138, + "time": 1791860859.646932 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "94bb8b8563324b4d327c8070a8e1cf99c5a133b5a104fd99fd3c2942c9d8e71a" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 139, + "time": 1791860867.169062 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 140, + "time": 1791860867.212686 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "propose: command-96098171ac4447cf9cb6a7b2facb798d", + "seq": 141, + "time": 1791860867.295264 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "6fed8287d24fddf7219a4010d559602cf9635bb8478a086949e4e3b3ce1676cd" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 142, + "time": 1791860867.361056 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 143, + "time": 1791860867.4049459 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "dispatch: command-3d83235444f841cdbba5d555c9d5246d", + "seq": 144, + "time": 1791860867.637874 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "510775598cb2b10ed20272d4daadd242ac0e590c38dd52fbfddf9c5381986ecc" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 145, + "time": 1791860867.700633 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 146, + "time": 1791860867.7447581 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "observe: command-047b4fe7d49c425c84abf5b3840b8f46", + "seq": 147, + "time": 1791860867.9181042 + }, + { + "intent": "Retire planner and recover reply", + "kind": "inference", + "outcome": "15", + "seq": 148, + "time": 1791860867.9198852 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "d96a40d00cc5f53d52e520d2589d9974be0c73d045b8b23e49d9e8faddb12074" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 149, + "time": 1791860874.962473 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 150, + "time": 1791860875.005901 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "propose: command-4f154fdba3aa4493846a014f77143055", + "seq": 151, + "time": 1791860875.085974 + }, + { + "evidence": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "357b8fa432c119b488a0055f0626a482438a04263bc4c3849910d503196533c3" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 152, + "time": 1791860875.1431081 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 153, + "time": 1791860875.183298 + }, + { + "intent": "Retire planner and recover reply", + "kind": "rpc_applied", + "outcome": "dispatch: command-8ef12068ba8a4f199cd3e55144180692", + "seq": 154, + "time": 1791860875.4448261 + } + ], + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "connection": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 11, + "image_url": "/api/frame?generation=11&sha256=970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f" + }, + "jobs": [ + { + "call_count": 15, + "disturbance_test": false, + "error": null, + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "label": "Retire planner and recover reply", + "last_intent": "Finish reset now that the block is centered in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "d2820395d03d485492e1225f1972a12d", + "block_home_verified": true, + "generation": 11, + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "kind": "finish_reset", + "native_time_s": 22.94799999999816, + "operation_id": "agent-e8c188b67ec74303916d4a975c30c34e", + "pusher_home_verified": true, + "reset_contact_samples": 3132, + "reset_native_steps": 8206, + "state_sha256": "31f1e85639b1f4712ff98a8b2c3158de32f0e08e3204141921cfac1648fe0b6e" + }, + "status": "completed", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": true, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 15, + "input_tokens": 183745, + "output_tokens": 2157, + "reported_calls": 14, + "token_usage_complete": false + } + }, + "time": 1791861054.3815832 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/source-freeze.json b/jevdual/results/reusable-body-recovery-20261012/planning/source-freeze.json new file mode 100644 index 0000000..11dbaf8 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/source-freeze.json @@ -0,0 +1,45 @@ +{ + "commit": "c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/bench_body_contract.py": "a4dd11d946a3226c6bc32f135b5d4484e86ac684ac3aa941d52d093062933a2b", + "tools/physical-workcell/body_client.py": "3ff0449f0865136d8c2d0913f34ca1dc8d9f5f25216ccbdcd1bf6530dcfb6fd1", + "tools/physical-workcell/body_contract.py": "3a8ba3f44d5f8ab9b5ab30e53642a78b176608fdab7719a68d725dc03d6974bd", + "tools/physical-workcell/body_network.py": "5dbb4480765ebb75bcdbafc870b1d6a468f56d4f89d924d8cd4ec851c4c02d0a", + "tools/physical-workcell/body_protocol.py": "301fca90dc215694468f5c6989080cf796ee0c205ff045f5e18e4968a2ca6bdf", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "d7ba2e5f3a520a4679f8309b866e45ba3abd1d22e4f8849b970302159af134a3", + "tools/physical-workcell/crash_body.py": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "tools/physical-workcell/crash_operator.py": "a97bd24b6320995f5ef2ef1b4e06ec1392f3f51987b551c45759612efa874a18", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/network_bench_operator.html": "10efef540879da4a710cee2aafe6712213ad645c8fd7890382da1048f96a6686", + "tools/physical-workcell/network_bench_operator.py": "9079729ea4e8b56ccdf2a6d187b0c5d79a23a33688748fe55d92d10922500b40", + "tools/physical-workcell/network_fault_proxy.py": "44b0b52f7c213c0f5f93b8997ba72be4e750bc9f1a4f7f6d7b2a8c4f1233b595", + "tools/physical-workcell/network_operator_runtime.py": "530666b4d4e54cc8e67a6180ac38821590bc42acd8a460c3d9066ad02542e229", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/persistent_body_client.py": "57ba866da8bcb53995ea9d93fbefbfd0556e53a2bde2b445deb4f427cd3d2a14", + "tools/physical-workcell/planner_guard.py": "52eeb806b824d62fc78d500d3bf285fcf8c78facedec8c17721eabae0e7c87da", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/qualify_network_body.py": "769c2514277a6f3101d8ff237041039c1e4ca85a5a76ec453dd9c9f87997a09c", + "tools/physical-workcell/qualify_network_workbench.py": "e5c72acda2ea8fae3658faba907448765df2f1e34e05749fbe85faed28ef003b", + "tools/physical-workcell/qualify_workbench_crashes.py": "da971a4190389414dd2114b86d2b7dd40adb2dba2c16e304782eab362244932e", + "tools/physical-workcell/qualify_workbench_recovery.py": "acdd708c879582cadefbdbe734da855143ad71242551d55625a0abacff8b26eb", + "tools/physical-workcell/qualify_workbench_restarts.py": "2bc3edc5ea39b69f3372d4274b757f06472409407a22cd3149dfa52fbf314dcb", + "tools/physical-workcell/record_network_workbench.py": "7604865caa309c7002c01a2384da84eb62d8b22dcd63ab256618c536bc5fedc4", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_network_body.py": "d1f7150a5ff23775664b5656fd21379502ba71175487fd7aadb2b51bffcdaf8c", + "tools/physical-workcell/score_network_workbench.py": "67a53586081fe425bd14b0a0fbbf3d7f0fc1e66daccda06765bb88f3d9d4d84c", + "tools/physical-workcell/score_workbench_crashes.py": "10bec02c29afdb9963006884fd9ce310a52be8ba0995698cc11f5d6e4475b998", + "tools/physical-workcell/score_workbench_recovery.py": "8bac7d17ca63f54f70c0ad9321c9d399f0e55f17f22de61a2783d3535a443135", + "tools/physical-workcell/score_workbench_restarts.py": "181cbf4ee1bbd5483f38edc4a84244fc3efa98dfd21b5c9751d43edfe8f9f5a5", + "tools/physical-workcell/test_workbench_crash_harness.py": "b5a1398f9ccec220731f30c9f082260e378743f6fa6c3d954f008c2e79aeaf6e", + "tools/physical-workcell/test_workbench_recovery_trials.py": "9983772236f3867c1507f90546969fa99bfe6dba99de43bbcf4b18daa60a9b76", + "tools/physical-workcell/workbench_link.py": "79571f62c5a9b5fb843a852bc34a2a509b0dc6dc1a6e091fa9f948ab98fae420", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui-ledger.json b/jevdual/results/reusable-body-recovery-20261012/planning/ui-ledger.json new file mode 100644 index 0000000..2ac2502 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui-ledger.json @@ -0,0 +1,358 @@ +{ + "steps": [ + { + "browser_action": "Observed offline state; cached evidence and disabled controls retained", + "cached_public": null, + "client_commands": 2, + "fetch_error": "[Errno 61] Connection refused", + "name": "crash-offline", + "offline": true, + "pending_command_ids": [], + "pins": { + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "body_id": "d2820395d03d485492e1225f1972a12d" + }, + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/ui/crash-offline.png", + "snapshot_provenance": { + "name": "000002-offline.json", + "sha256": "2fdafd1c57992a71c9d50ecfa7b5c632d65a75232b96dd2812e36d128b29c766" + }, + "state": null, + "time": 1791860317.6027021, + "visible_text": "Browser tab: 6, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51419/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51419/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t58 checkbox Retire planner and recover reply Working, Value: 1\n\t\t9 text One bench. Successive jobs.\n\t\t10 text The AI prepares the part, takes a measurement, and returns it home before the next job.\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t59 text Retire planner and recover reply\n\t\t60 text Disconnected\n\t\t16 button (disabled) Pause, ID: pause\n\t\t61 text The workbench is unavailable. The last camera image and job status are retained.\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t62 text Body connected\n\t\t\t63 text Last known body status. The workbench cannot check it while disconnected.\n\t\t\t20 button (disabled) Disconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t22 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t23 text Test the simulated connection\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t64 text Retained observation \u00b7 workbench offline\n\t\t\t65 text Waiting for the next camera observation.\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t66 text No frame captured\n\t\t67 text WORKBENCH OFFLINE\n\t\t68 text The camera image and job status are from the workbench\u2019s last reply. Reconnect before continuing.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t69 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Reloaded retained root; no automatic continuation or command replay", + "cached_public": null, + "client_commands": 2, + "name": "restored", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/ui/restored.png", + "state": { + "active_job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "connection": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Prior planner retired; explicit Resume obtains a fresh image", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "label": "Retire planner and recover reply", + "last_intent": "", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No uncertain command", + "state": "none" + }, + "runner_status": "paused" + }, + "time": 1791860352.906859, + "visible_text": "Browser tab: 6, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51419/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51419/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 checkbox Retire planner and recover reply Paused, Value: 1\n\t\t9 text One bench. Successive jobs.\n\t\t10 text The AI prepares the part, takes a measurement, and returns it home before the next job.\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t14 text Retire planner and recover reply\n\t\t15 text Paused\n\t\t16 button Resume, ID: pause\n\t\t17 text Prior planner retired; explicit Resume obtains a fresh image\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t19 text Body connected\n\t\t\t20 text Body identity verified by read-only TCP health\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t23 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t24 text Test the simulated connection\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t27 text Last observation \u00b7 Retire planner and recover reply\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t30 text Captured \u00b7 generation 0\n\t\t31 text PAUSED\n\t\t32 text Paused. Resume when you are ready for the next action.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t38 text Pending\n\t\t\t39 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Clicked explicit Resume before any additional reply fault; fresh TCP camera planning resumes", + "cached_public": null, + "client_commands": 6, + "name": "resumed", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/ui/resumed.png", + "state": { + "active_job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "connection": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 1, + "image_url": "/api/frame?generation=1&sha256=a831bd4576c7fa2d1ccf8d5369de1a242e0292a976ad40da9c39304dd00d96e5" + }, + "jobs": [ + { + "call_count": 3, + "disturbance_test": false, + "error": null, + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "label": "Retire planner and recover reply", + "last_intent": "Move the pusher into left-side contact with the block to prepare for a rightward push.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "A" + } + ], + "model_request_pending": true, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791860398.32268, + "visible_text": "Browser tab: 6, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51419/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51419/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t65 checkbox Retire planner and recover reply Paused, Value: 1\n\t\t9 text One bench. Successive jobs.\n\t\t10 text The AI prepares the part, takes a measurement, and returns it home before the next job.\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t66 text Retire planner and recover reply\n\t\t67 text Running\n\t\t16 button Pause, ID: pause\n\t\t68 text Prior planner retired; explicit Resume obtains a fresh image\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t69 text Body connected\n\t\t\t70 text Current body boot verified for explicit Resume\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t23 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t24 text Test the simulated connection\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t71 text Last observation \u00b7 Retire planner and recover reply\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t72 text Captured \u00b7 generation 0\n\t\t73 text AGENT\n\t\t74 text Waiting for the agent\u2019s next observation.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t75 text Pending\n\t\t\t39 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Armed lost reply only after captured Resume; exact dispatch now uncertain and blocks successors", + "cached_public": null, + "client_commands": 14, + "name": "network-uncertain", + "offline": false, + "pending_command_ids": [ + "command-b0e991c0c3e4431c9e13603a27769d1f" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/ui/network-uncertain.png", + "state": { + "active_job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "connection": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 3, + "image_url": "/api/frame?generation=3&sha256=b747f88e802c99f70a2caed1a1223c17887647bff6f5ab30b81c310055f20e0a" + }, + "jobs": [ + { + "call_count": 5, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "label": "Retire planner and recover reply", + "last_intent": "Measure the block now that its center is inside the region and the pusher is clear.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-b0e991c0c3e4431c9e13603a27769d1f", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791860470.984551, + "visible_text": "Browser tab: 6, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51419/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51419/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t77 checkbox Retire planner and recover reply Needs attention, Value: 1\n\t\t9 text One bench. Successive jobs.\n\t\t10 text The AI prepares the part, takes a measurement, and returns it home before the next job.\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t78 text Retire planner and recover reply\n\t\t79 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t80 text reply is uncertain; reconcile exact command without resending\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t81 text Body connected\n\t\t\t82 text Body identity verified by read-only TCP health\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button Check receipt, ID: reconcile\n\t\t\t70 container\n\t\t\t\t71 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t72 text Test the simulated connection\n\t\t\t\t73 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t74 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t75 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t76 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t83 text No reply fault armed.\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t84 text Last observation \u00b7 Retire planner and recover reply\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t85 text Captured \u00b7 generation 3\n\t\t86 text OUTCOME UNCERTAIN\n\t\t87 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t88 text Pending\n\t\t\t39 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Checked exact lost measurement receipt; retained original token and paused, no new commands", + "cached_public": null, + "client_commands": 14, + "name": "network-recovered", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/ui/network-recovered.png", + "state": { + "active_job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "connection": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 3, + "image_url": "/api/frame?generation=3&sha256=b747f88e802c99f70a2caed1a1223c17887647bff6f5ab30b81c310055f20e0a" + }, + "jobs": [ + { + "call_count": 5, + "disturbance_test": false, + "error": null, + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "label": "Retire planner and recover reply", + "last_intent": "Measure the block now that its center is inside the region and the pusher is clear.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "measured", + "reset": null, + "status": "paused", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-b0e991c0c3e4431c9e13603a27769d1f", + "operation": "dispatch", + "reason": "Exact historical stage recovered; explicit Resume observes fresh evidence", + "state": "recovered" + }, + "runner_status": "paused" + }, + "time": 1791860545.1437068, + "visible_text": "Browser tab: 6, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51419/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51419/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t77 checkbox Retire planner and recover reply Paused, Value: 1\n\t\t9 text One bench. Successive jobs.\n\t\t10 text The AI prepares the part, takes a measurement, and returns it home before the next job.\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t78 text Retire planner and recover reply\n\t\t79 text Paused\n\t\t16 button Resume, ID: pause\n\t\t80 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue.\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t81 text Body connected\n\t\t\t82 text Body identity verified by read-only TCP health\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t70 container\n\t\t\t\t71 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t72 text Test the simulated connection\n\t\t\t\t73 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t74 button Lose next reply, ID: fault-drop\n\t\t\t\t75 button Delay next reply, ID: fault-delay\n\t\t\t\t76 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t83 text No reply fault armed.\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t84 text Last observation \u00b7 Retire planner and recover reply\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t85 text Captured \u00b7 generation 3\n\t\t86 text RECEIPT RECOVERED\n\t\t87 text The exact command receipt was recovered. The agent is paused until you resume.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t88 text Recorded\n\t\t\t89 container measurement-result\n\t\t\t\t90 text 1,726\n\t\t\t\t91 text Orange pixels\n\t\t\t\t92 container\n\t\t\t\t\t93 text 4,794.4\n\t\t\t\t\t94 text mm\u00b2\n\t\t\t\t95 text Apparent footprint\n\t\t\t\t96 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment." + }, + { + "browser_action": "Explicit Resume after exact measurement receipt, fresh image for contact reset", + "cached_public": null, + "client_commands": 18, + "name": "network-resumed", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/ui/network-resumed.png", + "state": { + "active_job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "connection": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 3, + "image_url": "/api/frame?generation=3&sha256=b747f88e802c99f70a2caed1a1223c17887647bff6f5ab30b81c310055f20e0a" + }, + "jobs": [ + { + "call_count": 7, + "disturbance_test": false, + "error": null, + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "label": "Retire planner and recover reply", + "last_intent": "Begin the reset sequence after the verified measurement.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "resetting", + "reset": null, + "status": "running", + "target": "A" + } + ], + "model_request_pending": true, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791860804.0481112, + "visible_text": "Browser tab: 6, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51419/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51419/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t97 checkbox Retire planner and recover reply Working, Value: 1\n\t\t9 text One bench. Successive jobs.\n\t\t10 text The AI prepares the part, takes a measurement, and returns it home before the next job.\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t98 text Retire planner and recover reply\n\t\t99 text Running\n\t\t16 button Pause, ID: pause\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t100 text Body connected\n\t\t\t101 text Fresh pinned ready proof verified before command admission\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t70 container\n\t\t\t\t71 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t72 text Test the simulated connection\n\t\t\t\t73 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t74 button Lose next reply, ID: fault-drop\n\t\t\t\t75 button Delay next reply, ID: fault-delay\n\t\t\t\t76 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t102 text No reply fault armed.\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t103 text Last observation \u00b7 Retire planner and recover reply\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t104 text Captured \u00b7 generation 3\n\t\t105 text AI IS PLANNING\n\t\t106 text Planning the next move from the latest camera image.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t107 text Recorded\n\t\t\t89 container measurement-result\n\t\t\t\t108 text 1,726\n\t\t\t\t91 text Orange pixels\n\t\t\t\t92 container\n\t\t\t\t\t109 text 4,794.4\n\t\t\t\t\t94 text mm\u00b2\n\t\t\t\t95 text Apparent footprint\n\t\t\t\t96 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment." + }, + { + "browser_action": "Observed completed measurement and contact reset after planner and lost-reply recovery", + "cached_public": null, + "client_commands": 44, + "name": "completed", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2/ui/completed.png", + "state": { + "active_job_id": null, + "app_id": "3e44f35fead1476292e3976e8a3e6b63", + "connection": { + "adapter_id": "ad2772b543f9415b96d4efde0013d9ab", + "body_id": "d2820395d03d485492e1225f1972a12d", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 11, + "image_url": "/api/frame?generation=11&sha256=970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f" + }, + "jobs": [ + { + "call_count": 15, + "disturbance_test": false, + "error": null, + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "label": "Retire planner and recover reply", + "last_intent": "Finish reset now that the block is centered in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "d2820395d03d485492e1225f1972a12d", + "block_home_verified": true, + "generation": 11, + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "0bc391fc-81d4-4d6a-a267-965b153f8cab", + "kind": "finish_reset", + "native_time_s": 22.94799999999816, + "operation_id": "agent-e8c188b67ec74303916d4a975c30c34e", + "pusher_home_verified": true, + "reset_contact_samples": 3132, + "reset_native_steps": 8206, + "state_sha256": "31f1e85639b1f4712ff98a8b2c3158de32f0e08e3204141921cfac1648fe0b6e" + }, + "status": "completed", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791861054.1896262, + "visible_text": "Browser tab: 6, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51419/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51419/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t97 checkbox Retire planner and recover reply Completed, Value: 1\n\t\t9 text One bench. Successive jobs.\n\t\t10 text The AI prepares the part, takes a measurement, and returns it home before the next job.\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t98 text Retire planner and recover reply\n\t\t99 text Running\n\t\t16 button Pause, ID: pause\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t100 text Body connected\n\t\t\t101 text Body identity verified by read-only TCP health\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t70 container\n\t\t\t\t71 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t72 text Test the simulated connection\n\t\t\t\t73 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t74 button Lose next reply, ID: fault-drop\n\t\t\t\t75 button Delay next reply, ID: fault-delay\n\t\t\t\t76 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t102 text No reply fault armed.\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t103 text Last camera observation\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t104 text Captured \u00b7 generation 11\n\t\t105 text COMPLETED\n\t\t106 text Measurement recorded and reset verified. The bench is ready for another job.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t107 text Historical result\n\t\t\t89 container measurement-result\n\t\t\t\t108 text 1,726\n\t\t\t\t91 text Orange pixels\n\t\t\t\t92 container\n\t\t\t\t\t109 text 4,794.4\n\t\t\t\t\t94 text mm\u00b2\n\t\t\t\t95 text Apparent footprint\n\t\t\t\t96 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment.\n\t\t\t110 button Repeat job, ID: repeat" + } + ] +} diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/completed.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/completed.ledger.txt new file mode 100644 index 0000000..1c4abec --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/completed.ledger.txt @@ -0,0 +1,53 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 97 checkbox Retire planner and recover reply Completed, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 98 text Retire planner and recover reply + 99 text Running + 16 button Pause, ID: pause + 18 container Description: Body connection, ID: network-card + 100 text Body connected + 101 text Body identity verified by read-only TCP health + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 70 container + 71 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 72 text Test the simulated connection + 73 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 74 button Lose next reply, ID: fault-drop + 75 button Delay next reply, ID: fault-delay + 76 button Duplicate next command, ID: fault-duplicate + 102 text No reply fault armed. + 25 container Camera observation + 26 text OVERHEAD CAMERA + 103 text Last camera observation + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 104 text Captured · generation 11 + 105 text COMPLETED + 106 text Measurement recorded and reset verified. The bench is ready for another job. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 107 text Historical result + 89 container measurement-result + 108 text 1,726 + 91 text Orange pixels + 92 container + 109 text 4,794.4 + 94 text mm² + 95 text Apparent footprint + 96 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 110 button Repeat job, ID: repeat \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/completed.png b/jevdual/results/reusable-body-recovery-20261012/planning/ui/completed.png new file mode 100644 index 0000000..dc10ac0 Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/planning/ui/completed.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/completed.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/completed.txt new file mode 100644 index 0000000..53e76ca --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/completed.txt @@ -0,0 +1,87 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 97 checkbox Retire planner and recover reply Completed, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 98 text Retire planner and recover reply + 99 text Running + 16 button Pause, ID: pause + 18 container Description: Body connection, ID: network-card + 100 text Body connected + 101 text Body identity verified by read-only TCP health + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 70 container + 71 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 72 text Test the simulated connection + 73 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 74 button Lose next reply, ID: fault-drop + 75 button Delay next reply, ID: fault-delay + 76 button Duplicate next command, ID: fault-duplicate + 102 text No reply fault armed. + 25 container Camera observation + 26 text OVERHEAD CAMERA + 103 text Last camera observation + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 104 text Captured · generation 11 + 105 text COMPLETED + 106 text Measurement recorded and reset verified. The bench is ready for another job. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 107 text Historical result + 89 container measurement-result + 108 text 1,726 + 91 text Orange pixels + 92 container + 109 text 4,794.4 + 94 text mm² + 95 text Apparent footprint + 96 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 110 button Repeat job, ID: repeat + 40 container Activity + 41 heading Activity, Value: 2, ID: activity-heading + 42 text Activity + 111 text 24 events + 112 content list activity + 113 container + 114 text 11:07 PM Action receipt checked Retire planner and recover reply + 115 container + 116 text 11:07 PM Sending a guarded action + 117 container + 118 text 11:07 PM Fresh body check verified The next command uses current body evidence. + 119 container + 120 text 11:07 PM Action proposal accepted Retire planner and recover reply + 121 container + 122 text 11:07 PM Checking the planned action + 123 container + 124 text 11:07 PM Fresh body check verified The next command uses current body evidence. + 125 container + 126 text 11:07 PM AI planning requested Retire planner and recover reply + 127 container + 128 text 11:07 PM Camera observation received Retire planner and recover reply + 129 container + 130 text 11:07 PM Requesting camera observation + 131 container + 132 text 11:07 PM Fresh body check verified The next command uses current body evidence. + 133 container + 134 text 11:07 PM Action receipt checked Retire planner and recover reply + 135 container + 136 text 11:07 PM Sending a guarded action + 63 button (collapsed) Session details, Secondary Actions: Expand + 64 text Session details + 137 text 15 model requests · Reported tokens: 183,745 input / 2,157 output · Reporting incomplete (14 of 15 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/crash-offline.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/crash-offline.ledger.txt new file mode 100644 index 0000000..3020c40 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/crash-offline.ledger.txt @@ -0,0 +1,40 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 58 checkbox Retire planner and recover reply Working, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 59 text Retire planner and recover reply + 60 text Disconnected + 16 button (disabled) Pause, ID: pause + 61 text The workbench is unavailable. The last camera image and job status are retained. + 17 container Description: Body connection, ID: network-card + 62 text Body connected + 63 text Last known body status. The workbench cannot check it while disconnected. + 20 button (disabled) Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 22 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 23 text Test the simulated connection + 24 container Camera observation + 25 text OVERHEAD CAMERA + 64 text Retained observation · workbench offline + 65 text Waiting for the next camera observation. + 28 text Orange part Blue pusher Green task region + 66 text No frame captured + 67 text WORKBENCH OFFLINE + 68 text The camera image and job status are from the workbench’s last reply. Reconnect before continuing. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 69 text Pending + 38 text The part’s visible footprint will be measured after its placement is verified. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/crash-offline.png b/jevdual/results/reusable-body-recovery-20261012/planning/ui/crash-offline.png new file mode 100644 index 0000000..67d567d Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/planning/ui/crash-offline.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/crash-offline.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/crash-offline.txt new file mode 100644 index 0000000..6bc8dc1 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/crash-offline.txt @@ -0,0 +1,62 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 58 checkbox Retire planner and recover reply Working, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 59 text Retire planner and recover reply + 60 text Disconnected + 16 button (disabled) Pause, ID: pause + 61 text The workbench is unavailable. The last camera image and job status are retained. + 17 container Description: Body connection, ID: network-card + 62 text Body connected + 63 text Last known body status. The workbench cannot check it while disconnected. + 20 button (disabled) Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 22 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 23 text Test the simulated connection + 24 container Camera observation + 25 text OVERHEAD CAMERA + 64 text Retained observation · workbench offline + 65 text Waiting for the next camera observation. + 28 text Orange part Blue pusher Green task region + 66 text No frame captured + 67 text WORKBENCH OFFLINE + 68 text The camera image and job status are from the workbench’s last reply. Reconnect before continuing. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 69 text Pending + 38 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 70 text 6 events + 43 content list activity + 71 container + 72 text 10:56 PM Requesting camera observation + 73 container + 74 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 75 container + 76 text 10:56 PM Inspection started Retire planner and recover reply + 77 container + 78 text 10:56 PM Preparing inspection + 79 container + 80 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 81 container + 82 text 10:56 PM Job queued Retire planner and recover reply + 56 button (collapsed) Session details, Secondary Actions: Expand + 57 text Session details + 83 text Model usage will appear after the first request. + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-recovered.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-recovered.ledger.txt new file mode 100644 index 0000000..a3a7396 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-recovered.ledger.txt @@ -0,0 +1,53 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 77 checkbox Retire planner and recover reply Paused, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 78 text Retire planner and recover reply + 79 text Paused + 16 button Resume, ID: pause + 80 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue. + 18 container Description: Body connection, ID: network-card + 81 text Body connected + 82 text Body identity verified by read-only TCP health + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 70 container + 71 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 72 text Test the simulated connection + 73 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 74 button Lose next reply, ID: fault-drop + 75 button Delay next reply, ID: fault-delay + 76 button Duplicate next command, ID: fault-duplicate + 83 text No reply fault armed. + 25 container Camera observation + 26 text OVERHEAD CAMERA + 84 text Last observation · Retire planner and recover reply + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 85 text Captured · generation 3 + 86 text RECEIPT RECOVERED + 87 text The exact command receipt was recovered. The agent is paused until you resume. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 88 text Recorded + 89 container measurement-result + 90 text 1,726 + 91 text Orange pixels + 92 container + 93 text 4,794.4 + 94 text mm² + 95 text Apparent footprint + 96 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-recovered.png b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-recovered.png new file mode 100644 index 0000000..cef3c23 Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-recovered.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-recovered.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-recovered.txt new file mode 100644 index 0000000..c8f43f0 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-recovered.txt @@ -0,0 +1,87 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 77 checkbox Retire planner and recover reply Paused, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 78 text Retire planner and recover reply + 79 text Paused + 16 button Resume, ID: pause + 80 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue. + 18 container Description: Body connection, ID: network-card + 81 text Body connected + 82 text Body identity verified by read-only TCP health + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 70 container + 71 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 72 text Test the simulated connection + 73 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 74 button Lose next reply, ID: fault-drop + 75 button Delay next reply, ID: fault-delay + 76 button Duplicate next command, ID: fault-duplicate + 83 text No reply fault armed. + 25 container Camera observation + 26 text OVERHEAD CAMERA + 84 text Last observation · Retire planner and recover reply + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 85 text Captured · generation 3 + 86 text RECEIPT RECOVERED + 87 text The exact command receipt was recovered. The agent is paused until you resume. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 88 text Recorded + 89 container measurement-result + 90 text 1,726 + 91 text Orange pixels + 92 container + 93 text 4,794.4 + 94 text mm² + 95 text Apparent footprint + 96 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 40 container Activity + 41 heading Activity, Value: 2, ID: activity-heading + 42 text Activity + 97 text 24 events + 44 content list activity + 98 container + 99 text 11:01 PM Command receipt recovered The agent remains paused until you resume. + 100 container + 101 text 11:01 PM Action receipt checked Retire planner and recover reply + 102 container + 103 text 11:00 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 104 container + 105 text 11:00 PM Sending a guarded action + 106 container + 107 text 11:00 PM Fresh body check verified The next command uses current body evidence. + 108 container + 109 text 11:00 PM Action proposal accepted Retire planner and recover reply + 110 container + 111 text 11:00 PM Checking the planned action + 112 container + 113 text 11:00 PM Fresh body check verified The next command uses current body evidence. + 114 container + 115 text 11:00 PM Reply fault armed The next command reply will be lost. + 116 container + 117 text 11:00 PM AI planning requested Retire planner and recover reply + 118 container + 119 text 11:00 PM Camera observation received Retire planner and recover reply + 120 container + 121 text 11:00 PM Requesting camera observation + 63 button (collapsed) Session details, Secondary Actions: Expand + 64 text Session details + 122 text 5 model requests · Reported tokens: 51,775 input / 638 output · Reporting incomplete (4 of 5 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-resumed.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-resumed.ledger.txt new file mode 100644 index 0000000..a8581b3 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-resumed.ledger.txt @@ -0,0 +1,52 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 97 checkbox Retire planner and recover reply Working, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 98 text Retire planner and recover reply + 99 text Running + 16 button Pause, ID: pause + 18 container Description: Body connection, ID: network-card + 100 text Body connected + 101 text Fresh pinned ready proof verified before command admission + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 70 container + 71 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 72 text Test the simulated connection + 73 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 74 button Lose next reply, ID: fault-drop + 75 button Delay next reply, ID: fault-delay + 76 button Duplicate next command, ID: fault-duplicate + 102 text No reply fault armed. + 25 container Camera observation + 26 text OVERHEAD CAMERA + 103 text Last observation · Retire planner and recover reply + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 104 text Captured · generation 3 + 105 text AI IS PLANNING + 106 text Planning the next move from the latest camera image. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 107 text Recorded + 89 container measurement-result + 108 text 1,726 + 91 text Orange pixels + 92 container + 109 text 4,794.4 + 94 text mm² + 95 text Apparent footprint + 96 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-resumed.png b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-resumed.png new file mode 100644 index 0000000..1de2286 Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-resumed.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-resumed.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-resumed.txt new file mode 100644 index 0000000..161830b --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-resumed.txt @@ -0,0 +1,61 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 97 checkbox Retire planner and recover reply Working, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 98 text Retire planner and recover reply + 99 text Running + 16 button Pause, ID: pause + 18 container Description: Body connection, ID: network-card + 100 text Body connected + 101 text Fresh pinned ready proof verified before command admission + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 70 container + 71 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 72 text Test the simulated connection + 73 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 74 button Lose next reply, ID: fault-drop + 75 button Delay next reply, ID: fault-delay + 76 button Duplicate next command, ID: fault-duplicate + 102 text No reply fault armed. + 25 container Camera observation + 26 text OVERHEAD CAMERA + 103 text Last observation · Retire planner and recover reply + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 104 text Captured · generation 3 + 105 text AI IS PLANNING + 106 text Planning the next move from the latest camera image. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 107 text Recorded + 89 container measurement-result + 108 text 1,726 + 91 text Orange pixels + 92 container + 109 text 4,794.4 + 94 text mm² + 95 text Apparent footprint + 96 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 40 container Activity + 41 heading Activity, Value: 2, ID: activity-heading + 42 text Activity + 110 text 24 events + 63 button (collapsed) Session details, Secondary Actions: Expand + 64 text Session details + 111 text 6 model requests · Reported tokens: 51,775 input / 638 output · Reporting incomplete (4 of 6 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-uncertain.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-uncertain.ledger.txt new file mode 100644 index 0000000..a6faa89 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-uncertain.ledger.txt @@ -0,0 +1,46 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 77 checkbox Retire planner and recover reply Needs attention, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 78 text Retire planner and recover reply + 79 text Needs attention + 16 button (disabled) Pause, ID: pause + 80 text reply is uncertain; reconcile exact command without resending + 18 container Description: Body connection, ID: network-card + 81 text Body connected + 82 text Body identity verified by read-only TCP health + 21 button Disconnect, ID: disconnect + 22 button Check receipt, ID: reconcile + 70 container + 71 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 72 text Test the simulated connection + 73 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 74 button (disabled) Lose next reply, ID: fault-drop + 75 button (disabled) Delay next reply, ID: fault-delay + 76 button (disabled) Duplicate next command, ID: fault-duplicate + 83 text No reply fault armed. + 25 container Camera observation + 26 text OVERHEAD CAMERA + 84 text Last observation · Retire planner and recover reply + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 85 text Captured · generation 3 + 86 text OUTCOME UNCERTAIN + 87 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 88 text Pending + 39 text The part’s visible footprint will be measured after its placement is verified. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-uncertain.png b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-uncertain.png new file mode 100644 index 0000000..f234afe Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-uncertain.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-uncertain.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-uncertain.txt new file mode 100644 index 0000000..653ad87 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/network-uncertain.txt @@ -0,0 +1,80 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 77 checkbox Retire planner and recover reply Needs attention, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 78 text Retire planner and recover reply + 79 text Needs attention + 16 button (disabled) Pause, ID: pause + 80 text reply is uncertain; reconcile exact command without resending + 18 container Description: Body connection, ID: network-card + 81 text Body connected + 82 text Body identity verified by read-only TCP health + 21 button Disconnect, ID: disconnect + 22 button Check receipt, ID: reconcile + 70 container + 71 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 72 text Test the simulated connection + 73 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 74 button (disabled) Lose next reply, ID: fault-drop + 75 button (disabled) Delay next reply, ID: fault-delay + 76 button (disabled) Duplicate next command, ID: fault-duplicate + 83 text No reply fault armed. + 25 container Camera observation + 26 text OVERHEAD CAMERA + 84 text Last observation · Retire planner and recover reply + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 85 text Captured · generation 3 + 86 text OUTCOME UNCERTAIN + 87 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 88 text Pending + 39 text The part’s visible footprint will be measured after its placement is verified. + 40 container Activity + 41 heading Activity, Value: 2, ID: activity-heading + 42 text Activity + 89 text 24 events + 44 content list activity + 90 container + 91 text 11:00 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 92 container + 93 text 11:00 PM Sending a guarded action + 94 container + 95 text 11:00 PM Fresh body check verified The next command uses current body evidence. + 96 container + 97 text 11:00 PM Action proposal accepted Retire planner and recover reply + 98 container + 99 text 11:00 PM Checking the planned action + 100 container + 101 text 11:00 PM Fresh body check verified The next command uses current body evidence. + 102 container + 103 text 11:00 PM Reply fault armed The next command reply will be lost. + 104 container + 105 text 11:00 PM AI planning requested Retire planner and recover reply + 106 container + 107 text 11:00 PM Camera observation received Retire planner and recover reply + 108 container + 109 text 11:00 PM Requesting camera observation + 110 container + 111 text 11:00 PM Fresh body check verified The next command uses current body evidence. + 112 container + 113 text 11:00 PM Action receipt checked Retire planner and recover reply + 63 button (collapsed) Session details, Secondary Actions: Expand + 64 text Session details + 114 text 5 model requests · Reported tokens: 51,775 input / 638 output · Reporting incomplete (4 of 5 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/restored.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/restored.ledger.txt new file mode 100644 index 0000000..702a178 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/restored.ledger.txt @@ -0,0 +1,40 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 checkbox Retire planner and recover reply Paused, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 14 text Retire planner and recover reply + 15 text Paused + 16 button Resume, ID: pause + 17 text Prior planner retired; explicit Resume obtains a fresh image + 18 container Description: Body connection, ID: network-card + 19 text Body connected + 20 text Body identity verified by read-only TCP health + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 23 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 24 text Test the simulated connection + 25 container Camera observation + 26 text OVERHEAD CAMERA + 27 text Last observation · Retire planner and recover reply + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 30 text Captured · generation 0 + 31 text PAUSED + 32 text Paused. Resume when you are ready for the next action. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 38 text Pending + 39 text The part’s visible footprint will be measured after its placement is verified. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/restored.png b/jevdual/results/reusable-body-recovery-20261012/planning/ui/restored.png new file mode 100644 index 0000000..46fbc51 Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/planning/ui/restored.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/restored.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/restored.txt new file mode 100644 index 0000000..4992b38 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/restored.txt @@ -0,0 +1,68 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 checkbox Retire planner and recover reply Paused, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 14 text Retire planner and recover reply + 15 text Paused + 16 button Resume, ID: pause + 17 text Prior planner retired; explicit Resume obtains a fresh image + 18 container Description: Body connection, ID: network-card + 19 text Body connected + 20 text Body identity verified by read-only TCP health + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 23 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 24 text Test the simulated connection + 25 container Camera observation + 26 text OVERHEAD CAMERA + 27 text Last observation · Retire planner and recover reply + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 30 text Captured · generation 0 + 31 text PAUSED + 32 text Paused. Resume when you are ready for the next action. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 38 text Pending + 39 text The part’s visible footprint will be measured after its placement is verified. + 40 container Activity + 41 heading Activity, Value: 2, ID: activity-heading + 42 text Activity + 43 text 9 events + 44 content list activity + 45 container + 46 text 10:58 PM Previous planner retired The previous planner has stopped. Resume to plan from a fresh camera image. + 47 container + 48 text 10:56 PM AI planning requested Retire planner and recover reply + 49 container + 50 text 10:56 PM Camera observation received Retire planner and recover reply + 51 container + 52 text 10:56 PM Requesting camera observation + 53 container + 54 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 55 container + 56 text 10:56 PM Inspection started Retire planner and recover reply + 57 container + 58 text 10:56 PM Preparing inspection + 59 container + 60 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 61 container + 62 text 10:56 PM Job queued Retire planner and recover reply + 63 button (collapsed) Session details, Secondary Actions: Expand + 64 text Session details + 65 text 1 model requests · Reported tokens: 0 input / 0 output · Reporting incomplete (0 of 1 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/resumed.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/resumed.ledger.txt new file mode 100644 index 0000000..9a360c2 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/resumed.ledger.txt @@ -0,0 +1,40 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 65 checkbox Retire planner and recover reply Paused, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 66 text Retire planner and recover reply + 67 text Running + 16 button Pause, ID: pause + 68 text Prior planner retired; explicit Resume obtains a fresh image + 18 container Description: Body connection, ID: network-card + 69 text Body connected + 70 text Current body boot verified for explicit Resume + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 23 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 24 text Test the simulated connection + 25 container Camera observation + 26 text OVERHEAD CAMERA + 71 text Last observation · Retire planner and recover reply + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 72 text Captured · generation 0 + 73 text AGENT + 74 text Waiting for the agent’s next observation. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 75 text Pending + 39 text The part’s visible footprint will be measured after its placement is verified. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/resumed.png b/jevdual/results/reusable-body-recovery-20261012/planning/ui/resumed.png new file mode 100644 index 0000000..2113230 Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/planning/ui/resumed.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/planning/ui/resumed.txt b/jevdual/results/reusable-body-recovery-20261012/planning/ui/resumed.txt new file mode 100644 index 0000000..cbe521f --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/planning/ui/resumed.txt @@ -0,0 +1,70 @@ +Browser tab: 6, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51419/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 65 checkbox Retire planner and recover reply Paused, Value: 1 + 9 text One bench. Successive jobs. + 10 text The AI prepares the part, takes a measurement, and returns it home before the next job. + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 66 text Retire planner and recover reply + 67 text Running + 16 button Pause, ID: pause + 68 text Prior planner retired; explicit Resume obtains a fresh image + 18 container Description: Body connection, ID: network-card + 69 text Body connected + 70 text Current body boot verified for explicit Resume + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 23 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 24 text Test the simulated connection + 25 container Camera observation + 26 text OVERHEAD CAMERA + 71 text Last observation · Retire planner and recover reply + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 72 text Captured · generation 0 + 73 text AGENT + 74 text Waiting for the agent’s next observation. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 75 text Pending + 39 text The part’s visible footprint will be measured after its placement is verified. + 40 container Activity + 41 heading Activity, Value: 2, ID: activity-heading + 42 text Activity + 76 text 10 events + 44 content list activity + 77 container + 78 text 10:59 PM Agent resumed + 79 container + 80 text 10:58 PM Previous planner retired The previous planner has stopped. Resume to plan from a fresh camera image. + 81 container + 82 text 10:56 PM AI planning requested Retire planner and recover reply + 83 container + 84 text 10:56 PM Camera observation received Retire planner and recover reply + 85 container + 86 text 10:56 PM Requesting camera observation + 87 container + 88 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 89 container + 90 text 10:56 PM Inspection started Retire planner and recover reply + 91 container + 92 text 10:56 PM Preparing inspection + 93 container + 94 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 95 container + 96 text 10:56 PM Job queued Retire planner and recover reply + 63 button (collapsed) Session details, Secondary Actions: Expand + 64 text Session details + 97 text 1 model requests · Reported tokens: 0 input / 0 output · Reporting incomplete (0 of 1 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51419/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/crash-ledger.json b/jevdual/results/reusable-body-recovery-20261012/publication/crash-ledger.json new file mode 100644 index 0000000..521038b --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/crash-ledger.json @@ -0,0 +1,46 @@ +{ + "boundaries": [ + { + "case": "host-publication", + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "finished_snapshot": "000004-finished.json", + "gate": { + "barrier": "rpc_applied_event_before_manifest", + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "diagnostic_sha256": "a97bd24b6320995f5ef2ef1b4e06ec1392f3f51987b551c45759612efa874a18", + "event_sequence": 14, + "event_sha256": "84ca3163950c0874f6239ab7f81d3a7e0c21a938f40fd858c88faa23fc8bb496", + "manifest_sequence": 13, + "manifest_sha256": "628817f9c0e858d12f0e801399021bd731e8b8d1a2bd64b9df86e4b3608a64f3", + "pid": 49762, + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "time": 1791860187.3617659, + "version": 1 + }, + "new_epoch": 1, + "offline_snapshot": "000002-offline.json", + "old_epoch": 1, + "pre_snapshot": "000001-before.json", + "process": { + "boot": 1, + "children_before": [], + "exit_observed": true, + "group": 49762, + "initialized": false, + "log": "workbench-01.log", + "pid": 49762, + "port": 51413, + "returncode": -9, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/operator", + "signal": "SIGKILL", + "signal_target": "host_pid_only", + "stopped_time": 1791860187.418896 + }, + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "restored_snapshot": "000003-restored.json", + "role": "workbench" + } + ], + "case": "host-publication" +} diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/final-public.json b/jevdual/results/reusable-body-recovery-20261012/publication/final-public.json new file mode 100644 index 0000000..b018ad3 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/final-public.json @@ -0,0 +1,290 @@ +{ + "active_job_id": null, + "activity": [ + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "propose: command-cb41c1601faf44de9c84f0c74f0593e6", + "seq": 126, + "time": 1791860479.652856 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "738f29f8f5e9fd312ae3f99f4c188ddd39987a2a58116f33edcd588aba07ff6a" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 127, + "time": 1791860479.706536 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 128, + "time": 1791860479.746814 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "dispatch: command-5f74b9dd5b4042e0ba10e22b7ebd5639", + "seq": 129, + "time": 1791860479.963549 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "fa0dc5a06088d32b1b15e65152d9a50bd0ad228ffbbfdae8bf728e6c6714a296" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 130, + "time": 1791860480.022887 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 131, + "time": 1791860480.065069 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "observe: command-fda4688de77f4621be0fef063d3be610", + "seq": 132, + "time": 1791860480.2281978 + }, + { + "intent": "Recover interrupted move history", + "kind": "inference", + "outcome": "13", + "seq": 133, + "time": 1791860480.229849 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "3b298b9527f5e8cb07c8b7db3b171d9f6278051e3eec85fe4a7f148d795c6910" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 134, + "time": 1791860485.8839219 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 135, + "time": 1791860485.9236622 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "propose: command-af88ba2ef5854c978be55561a7e8f131", + "seq": 136, + "time": 1791860486.004915 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "2ba31eac98e12130060a434a8cf7dbb24aede37a43a776e3326506fc1974bb55" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 137, + "time": 1791860486.078175 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 138, + "time": 1791860486.122886 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "dispatch: command-f0807bf65ea94cceb62c15ff010dc8ef", + "seq": 139, + "time": 1791860486.393727 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "8b5eb923352a524eba5d884851c9c3b8380b3c1e18a11a8c78e75205ba313389" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 140, + "time": 1791860486.456125 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 141, + "time": 1791860486.497833 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "observe: command-83cd8e8a10694db7a532503aa41c7377", + "seq": 142, + "time": 1791860486.685514 + }, + { + "intent": "Recover interrupted move history", + "kind": "inference", + "outcome": "14", + "seq": 143, + "time": 1791860486.6878562 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "eef7f4f099d1744601db548ad504b9195160a4f86c8147c42bf328ace295337e" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 144, + "time": 1791860494.4948008 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 145, + "time": 1791860494.537086 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "propose: command-8ca707790e514e0c9ae33cb1f11d4c51", + "seq": 146, + "time": 1791860494.624099 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "e16a3012af9b0ca0e4d640f641f7ecd78fd72a84052f5d45fea96fa846a77f69" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 147, + "time": 1791860494.680424 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 148, + "time": 1791860494.719587 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "dispatch: command-22709a65620240edb98fba24d06d9cd1", + "seq": 149, + "time": 1791860495.0038579 + } + ], + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "connection": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 11, + "image_url": "/api/frame?generation=11&sha256=970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "label": "Recover interrupted move history", + "last_intent": "Verify the block center and pusher are both at their home positions.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "block_home_verified": true, + "generation": 11, + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "kind": "finish_reset", + "native_time_s": 23.61399999999779, + "operation_id": "agent-42b2d69f655d4ecdaf4ca1bfa5bfd41e", + "pusher_home_verified": true, + "reset_contact_samples": 3132, + "reset_native_steps": 8439, + "state_sha256": "c88451508675d9e3a033cfcf0d8c626f353001ea20783eb3877b655452423257" + }, + "status": "completed", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": true, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 14, + "input_tokens": 182740, + "output_tokens": 2070, + "reported_calls": 14, + "token_usage_complete": true + } +} diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/process-ledger.json b/jevdual/results/reusable-body-recovery-20261012/publication/process-ledger.json new file mode 100644 index 0000000..891e315 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/process-ledger.json @@ -0,0 +1,63 @@ +{ + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "body_origin": "http://127.0.0.1:51411", + "browser_origin": "http://127.0.0.1:51413", + "case": "host-publication", + "owned_processes_exited": true, + "planner": "actual_codex_cli_images_over_TCP", + "processes": [ + { + "boot": 1, + "exit_observed": true, + "group": 49737, + "initialized": true, + "log": "body-01.log", + "pid": 49737, + "port": 51411, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/body", + "stopped_time": 1791861031.430909 + }, + { + "exit_observed": true, + "group": 49754, + "pid": 49754, + "returncode": 0, + "role": "initialization" + }, + { + "boot": 1, + "children_before": [], + "exit_observed": true, + "group": 49762, + "initialized": false, + "log": "workbench-01.log", + "pid": 49762, + "port": 51413, + "returncode": -9, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/operator", + "signal": "SIGKILL", + "signal_target": "host_pid_only", + "stopped_time": 1791860187.418896 + }, + { + "boot": 2, + "exit_observed": true, + "group": 56151, + "initialized": false, + "log": "workbench-02.log", + "pid": 56151, + "port": 51413, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/operator", + "stopped_time": 1791861031.2593348 + } + ], + "ready": true, + "simulation": true, + "source_commit": "c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54" +} diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/recovery-score.json b/jevdual/results/reusable-body-recovery-20261012/publication/recovery-score.json new file mode 100644 index 0000000..90329a9 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/recovery-score.json @@ -0,0 +1,566 @@ +{ + "admissions": [ + { + "command_id": "command-6a3f77b6799e42f79e802c7069d4f1ec", + "passed": true, + "proof_event": 2 + }, + { + "command_id": "command-6184aa30e497418e8416b68ef4b0c87e", + "passed": true, + "proof_event": 5 + }, + { + "command_id": "command-0eb32e67ff4943979c667f45701af419", + "passed": true, + "proof_event": 9 + }, + { + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "passed": true, + "proof_event": 12 + }, + { + "command_id": "command-0112b4d76e234e00a72bc248dbeb97c7", + "passed": true, + "proof_event": 20 + }, + { + "command_id": "command-2198897f165f4a1da7b0a3ca5e181d74", + "passed": true, + "proof_event": 24 + }, + { + "command_id": "command-09e69ad7c8eb402a8afa0afbe2c07ed5", + "passed": true, + "proof_event": 27 + }, + { + "command_id": "command-595ea7ba5c6f4604a8c77ef32473a284", + "passed": true, + "proof_event": 30 + }, + { + "command_id": "command-eaafe005cb894f6d8e76154fedf101e0", + "passed": true, + "proof_event": 34 + }, + { + "command_id": "command-f031c3b324e0455796d09f27d38030da", + "passed": true, + "proof_event": 37 + }, + { + "command_id": "command-3dfb3a3579b14b59b03c6f77c05fbf7a", + "passed": true, + "proof_event": 40 + }, + { + "command_id": "command-9f6aa399a0324089a50edcde725cd4f9", + "passed": true, + "proof_event": 44 + }, + { + "command_id": "command-c2c7736a9e5f4f7884e76b71fdc3426e", + "passed": true, + "proof_event": 47 + }, + { + "command_id": "command-8bac74b0999343098a7fdfa1ea1d286f", + "passed": true, + "proof_event": 50 + }, + { + "command_id": "command-cef7fcccbbfb4ff5a386b3edb53d126c", + "passed": true, + "proof_event": 54 + }, + { + "command_id": "command-97f9bc09a708441db34225bf3ed4717b", + "passed": true, + "proof_event": 57 + }, + { + "command_id": "command-c75b90b9776b4fa69476343684e06d30", + "passed": true, + "proof_event": 60 + }, + { + "command_id": "command-b28a6b2d77444f468a2078943ac2b2fd", + "passed": true, + "proof_event": 64 + }, + { + "command_id": "command-7546bd1cad864fc19dc4663d373ba7b7", + "passed": true, + "proof_event": 67 + }, + { + "command_id": "command-71a49a7f398c482a8ea879b6ce415b32", + "passed": true, + "proof_event": 70 + }, + { + "command_id": "command-86a881f49b55477b80266213e0a23a11", + "passed": true, + "proof_event": 74 + }, + { + "command_id": "command-ee34fbb0d81b4ebf9f5563ccf149b3c9", + "passed": true, + "proof_event": 77 + }, + { + "command_id": "command-a112a64649334572a922c9bb42e18837", + "passed": true, + "proof_event": 80 + }, + { + "command_id": "command-da177becbb0c471e8858d465658c238e", + "passed": true, + "proof_event": 84 + }, + { + "command_id": "command-05229f8323fb47948ec8f06a4e8bfd0b", + "passed": true, + "proof_event": 87 + }, + { + "command_id": "command-aa1e6fa4fe9c4adb861246aa454e92c4", + "passed": true, + "proof_event": 90 + }, + { + "command_id": "command-f068bdf4e6d84d6c9267ce6e0bc540d3", + "passed": true, + "proof_event": 94 + }, + { + "command_id": "command-18963848635b4a69b89107f589013601", + "passed": true, + "proof_event": 97 + }, + { + "command_id": "command-dbb8187065264bc79d611a821a954e79", + "passed": true, + "proof_event": 100 + }, + { + "command_id": "command-bb8e4592a615416381e6b7b2016a7144", + "passed": true, + "proof_event": 104 + }, + { + "command_id": "command-45714737b58846deb2c7487445d1a6c2", + "passed": true, + "proof_event": 107 + }, + { + "command_id": "command-56c12b4ec539446baa274bc4afea7a0d", + "passed": true, + "proof_event": 110 + }, + { + "command_id": "command-9a7127dbd95c46ff81d0e7fcd283de7f", + "passed": true, + "proof_event": 114 + }, + { + "command_id": "command-393a47142aeb4c789ee2722ae79992c9", + "passed": true, + "proof_event": 117 + }, + { + "command_id": "command-eb7c688252724d42bf7dfdab84841d20", + "passed": true, + "proof_event": 120 + }, + { + "command_id": "command-cb41c1601faf44de9c84f0c74f0593e6", + "passed": true, + "proof_event": 124 + }, + { + "command_id": "command-5f74b9dd5b4042e0ba10e22b7ebd5639", + "passed": true, + "proof_event": 127 + }, + { + "command_id": "command-fda4688de77f4621be0fef063d3be610", + "passed": true, + "proof_event": 130 + }, + { + "command_id": "command-af88ba2ef5854c978be55561a7e8f131", + "passed": true, + "proof_event": 134 + }, + { + "command_id": "command-f0807bf65ea94cceb62c15ff010dc8ef", + "passed": true, + "proof_event": 137 + }, + { + "command_id": "command-83cd8e8a10694db7a532503aa41c7377", + "passed": true, + "proof_event": 140 + }, + { + "command_id": "command-8ca707790e514e0c9ae33cb1f11d4c51", + "passed": true, + "proof_event": 144 + }, + { + "command_id": "command-22709a65620240edb98fba24d06d9cd1", + "passed": true, + "proof_event": 147 + } + ], + "browser": { + "offline_then_paused": true, + "passed": true, + "steps": [ + "crash-offline", + "restored", + "recovered", + "resumed", + "completed" + ] + }, + "calls": [ + { + "action": "move", + "call": 1, + "camera_generation": 0, + "challenge_id": "challenge-7d780d4d02634b3cb2140c637af0004b", + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 50178, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-29b7df0a14ad4747b5301091b0500015" + }, + { + "action": "move", + "call": 2, + "camera_generation": 1, + "challenge_id": "challenge-d4b31bfc0ae24544881a5e709bafddc8", + "command_id": "command-09e69ad7c8eb402a8afa0afbe2c07ed5", + "image_sha256": "a831bd4576c7fa2d1ccf8d5369de1a242e0292a976ad40da9c39304dd00d96e5", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 57077, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-3a80198b21aa488ba1f2fae281f492f2" + }, + { + "action": "move", + "call": 3, + "camera_generation": 2, + "challenge_id": "challenge-b2bd04fa4e8d48a5bb5214cac3dc2b6d", + "command_id": "command-f031c3b324e0455796d09f27d38030da", + "image_sha256": "42819fa0bc8837bbd91f1098939426208f093b3ed7e6a2e198d725dd1e53787e", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 57649, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-654eac8936af4b7598a8e9e4ea58dfdf" + }, + { + "action": "measure", + "call": 4, + "camera_generation": 3, + "challenge_id": "challenge-0875a6703fc04606a3849d91bc6943ec", + "command_id": "command-c2c7736a9e5f4f7884e76b71fdc3426e", + "image_sha256": "37eff0642da366ea639079d52a540617d36eb1bede2b25aa8eceba7cd7bf4d04", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 57996, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-6ef797fc330e4eedb8c0b403376c18bd" + }, + { + "action": "begin_reset", + "call": 5, + "camera_generation": 3, + "challenge_id": "challenge-8efa70a2954541e097c6274c5af08e64", + "command_id": "command-97f9bc09a708441db34225bf3ed4717b", + "image_sha256": "37eff0642da366ea639079d52a540617d36eb1bede2b25aa8eceba7cd7bf4d04", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 58452, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-a277a9dbf58a43e6a82fa460a1ef3d7f" + }, + { + "action": "move", + "call": 6, + "camera_generation": 3, + "challenge_id": "challenge-9a1c0a191dca4e8094ba3ddcfddc0141", + "command_id": "command-7546bd1cad864fc19dc4663d373ba7b7", + "image_sha256": "37eff0642da366ea639079d52a540617d36eb1bede2b25aa8eceba7cd7bf4d04", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 58844, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-7a4d663128eb424fa2f2aeaf2366af1e" + }, + { + "action": "move", + "call": 7, + "camera_generation": 4, + "challenge_id": "challenge-e1fe261eb919433ab6f81bf20b7aed87", + "command_id": "command-ee34fbb0d81b4ebf9f5563ccf149b3c9", + "image_sha256": "df8b742937dd5949abd341de5f0a5c53b4096266806e9f3c3bb4182d14001b8e", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 59259, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-c328b496beb04670a67221d694e6cafa" + }, + { + "action": "move", + "call": 8, + "camera_generation": 5, + "challenge_id": "challenge-a08e2db587164c2f9adb81971de618cd", + "command_id": "command-05229f8323fb47948ec8f06a4e8bfd0b", + "image_sha256": "6190b019f225c3b7dbaa4974798da798787bbba6ade79b197f6fd8c5e3547e46", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 59531, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-0810c410eb1340a58ce077c08b6e5cd4" + }, + { + "action": "move", + "call": 9, + "camera_generation": 6, + "challenge_id": "challenge-a423bb0fe310430e99b090ec7814a901", + "command_id": "command-18963848635b4a69b89107f589013601", + "image_sha256": "bc7ea7cf98607fc2dcc490c5d234b59ad266652809ed972d188650a5a1a66d97", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 61738, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-166a209655fb44d5a074602a53927221" + }, + { + "action": "move", + "call": 10, + "camera_generation": 7, + "challenge_id": "challenge-f55bc50820fc416badae9d491bbed7dd", + "command_id": "command-45714737b58846deb2c7487445d1a6c2", + "image_sha256": "c4955a67bf00f4aedca9f61177cc50e16550508bb1469aaee9dd3cec6a3f3668", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 62315, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-871b02928832456fa6e5704af906b4fb" + }, + { + "action": "move", + "call": 11, + "camera_generation": 8, + "challenge_id": "challenge-550731604f584fd49c9f3cc8a3413162", + "command_id": "command-393a47142aeb4c789ee2722ae79992c9", + "image_sha256": "02f1e6f71facb02e05c11bf02a7f7895180c14b2ccd90eded1d75f5c999b2c94", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 62728, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-709789e64895425883e94ecaed8c4f22" + }, + { + "action": "move", + "call": 12, + "camera_generation": 9, + "challenge_id": "challenge-2c52a7c75c3649328f120d41d2c93a17", + "command_id": "command-5f74b9dd5b4042e0ba10e22b7ebd5639", + "image_sha256": "a70748b1f83625d7774f1002736b9d8ab101cd20e1852fb69e2aa66e63e63627", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 63144, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-337ba4a08d7f4584b0bbe7d18a2f652e" + }, + { + "action": "move", + "call": 13, + "camera_generation": 10, + "challenge_id": "challenge-c5ff5b474fa54342a48c750c791a18a2", + "command_id": "command-f0807bf65ea94cceb62c15ff010dc8ef", + "image_sha256": "3a0658fbcc98db2233e01c3cf4d4dcf31eaaca13ff25f778165da8d6bb180be0", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 63415, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-8f11ad94d02a4f36806fd3431fee2f9e" + }, + { + "action": "finish_reset", + "call": 14, + "camera_generation": 11, + "challenge_id": "challenge-effbdd97f4fc4c558af4a6d71ec899b8", + "command_id": "command-22709a65620240edb98fba24d06d9cd1", + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "model_pid": 63652, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-fd7a9242a4a04f068a9d4019bd0d9bff" + } + ], + "cancelled_calls": 0, + "case": "host-publication", + "completed_cycles": 1, + "crash": { + "fresh_challenge": "challenge-d4b31bfc0ae24544881a5e709bafddc8", + "host_pid_only": true, + "original_commands_preserved": true, + "passed": true, + "resume_sequence": 19 + }, + "event_cutoff": 149, + "native": { + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "measurement_receipt": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "generation": 3, + "image_sha256": "37eff0642da366ea639079d52a540617d36eb1bede2b25aa8eceba7cd7bf4d04", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 6.73599999999948, + "operation_id": "agent-d27c560eecf9487081d0d4a85806b55d", + "source_kind": "mujoco_simulation", + "state_sha256": "ca31851fa25da5c7518815e74b94b7a340ce26faf94063a0608582f1347c8961", + "version": 1, + "worker_pid": 58433 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06580395103635941, + -1.528921713631715e-10, + 0.03499573045536986 + ], + "external_force_cleared": true, + "generation": 3, + "native_time_s": 6.73599999999948, + "pusher_home": false, + "pusher_xyz": [ + -0.07999996926244182, + 8.447581403450109e-28, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "ca31851fa25da5c7518815e74b94b7a340ce26faf94063a0608582f1347c8961" + }, + "model_calls": 14, + "passed": true, + "reset_receipt": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "block_home_verified": true, + "generation": 11, + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "kind": "finish_reset", + "native_time_s": 23.61399999999779, + "operation_id": "agent-42b2d69f655d4ecdaf4ca1bfa5bfd41e", + "pusher_home_verified": true, + "reset_contact_samples": 3132, + "reset_native_steps": 8439, + "state_sha256": "c88451508675d9e3a033cfcf0d8c626f353001ea20783eb3877b655452423257" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.1157663460696213, + -2.428032397192988e-09, + 0.03499573045536987 + ], + "external_force_cleared": true, + "generation": 11, + "native_time_s": 23.61399999999779, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "c88451508675d9e3a033cfcf0d8c626f353001ea20783eb3877b655452423257" + } + }, + "owned_processes_exited": true, + "passed": true, + "scope": "selected event publication and active-planning host SIGKILL; loopback simulation", + "single_wire_post_every_command": true, + "source_commit": "c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54", + "source_unchanged": true, + "sqlite": { + "count": 1, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "generation": 3, + "image_sha256": "37eff0642da366ea639079d52a540617d36eb1bede2b25aa8eceba7cd7bf4d04", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "operation_id": "agent-d27c560eecf9487081d0d4a85806b55d", + "receipt": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "generation": 3, + "image_sha256": "37eff0642da366ea639079d52a540617d36eb1bede2b25aa8eceba7cd7bf4d04", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 6.73599999999948, + "operation_id": "agent-d27c560eecf9487081d0d4a85806b55d", + "source_kind": "mujoco_simulation", + "state_sha256": "ca31851fa25da5c7518815e74b94b7a340ce26faf94063a0608582f1347c8961", + "version": 1, + "worker_pid": 58433 + }, + "state_sha256": "ca31851fa25da5c7518815e74b94b7a340ce26faf94063a0608582f1347c8961" + } + ] + }, + "transport_command_count": 43, + "version": 1 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/snapshots/000001-before.json b/jevdual/results/reusable-body-recovery-20261012/publication/snapshots/000001-before.json new file mode 100644 index 0000000..24a09f3 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/snapshots/000001-before.json @@ -0,0 +1,321 @@ +{ + "body_command": { + "digest_valid": true, + "intent": { + "request": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "arguments": { + "proposal_id": "proposal-29b7df0a14ad4747b5301091b0500015" + }, + "body_id": "88ef33b6618f41f58507907680f0bde8", + "client_id": "client-ee0f30ca70e9419296b602113d7775b9", + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "epoch": 1, + "lease_id": "f20a8e34e125480bab75c54183bb9355", + "operation": "dispatch", + "protocol": "footwork.body/1", + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "result_sha256": "f664cc35200c51b3992589dc2f86f7e525cb3d1cffa1370430908a865cfb097e" + }, + "terminal": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "protocol": "footwork.body/1", + "reason": null, + "receipt_scope": "retained_command_result", + "recorded_epoch": 1, + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "result": { + "next_observation": { + "challenge_id": "challenge-401b1194fabd45e28164ac2a3bfa08fd", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/body/bench/images/000001-688c5044eacced6b.png", + "observation": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "calibration": { + "bbox_convention": "exclusive_max_pixel_edges", + "height": 360, + "image_center_world_xy": [ + 0.0, + 0.0 + ], + "image_x_direction": "world_positive_x", + "image_y_direction": "world_negative_y", + "meters_per_pixel": 0.0016666666666666666, + "projection": "orthographic", + "valid": true, + "validity_reason": "bound orthographic calibration", + "vertical_span_m": 0.6, + "width": 640, + "world_frame": "sim_world" + }, + "generation": 1, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/body/bench/images/000001-688c5044eacced6b.png", + "image_sha256": "a831bd4576c7fa2d1ccf8d5369de1a242e0292a976ad40da9c39304dd00d96e5", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "native_time_s": 2.8339999999999095, + "phase": "prepare", + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state_sha256": "217fecedff3930f0e05f3a872d63789b89e7f1fdcfdd1475052ac27b7c8d72d7", + "version": 1 + }, + "observation_sha256": "5029c0a5b6f9473d370102217058277a925969ce964a35f14d48ecaa34087491", + "sequence": 4, + "session_id": "2658977143f14c54a5f37a582fadb568", + "task": { + "bounds": { + "x_max": 0.18, + "x_min": -0.32, + "y_max": 0.15, + "y_min": -0.15 + }, + "home_zone": { + "x_max": -0.08, + "x_min": -0.16, + "y_max": 0.03, + "y_min": -0.03 + }, + "pusher_dimensions_m": { + "x": 0.04, + "y": 0.14 + }, + "zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "observation_scope": "snapshot_at_dispatch_result", + "outcome": "executed", + "proposal_id": "proposal-29b7df0a14ad4747b5301091b0500015", + "receipt": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "command_speed_limit_m_s": 0.15, + "generation": 1, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "kind": "move", + "max_axis_actuator_force_n": 0.38503045596822894, + "native_steps": 917, + "native_time_s": 2.8339999999999095, + "operation_id": "agent-3cff47d83674457d9a0f353e7802863f", + "pusher_block_contact_samples": 0, + "state_sha256": "217fecedff3930f0e05f3a872d63789b89e7f1fdcfdd1475052ac27b7c8d72d7", + "target_xy": [ + -0.175, + 0.0 + ] + } + }, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state": "completed" + } + }, + "body_identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "moves": 1, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "diagnostic_enabled": true, + "generation": 1, + "jobs": {}, + "native_time_s": 2.8339999999999095, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-ee0f30ca70e9419296b602113d7775b9", + "epoch": 1, + "expires_in_s": 29.876166624948382, + "lease_id": "f20a8e34e125480bab75c54183bb9355" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-0eb32e67ff4943979c667f45701af419": { + "operation": "propose", + "request_sha256": "7aa96a1b5a27d086843fb21726afe9457ebc6ec442304c1d0e169000387b6126", + "send_count": 1, + "state": "completed" + }, + "command-6184aa30e497418e8416b68ef4b0c87e": { + "operation": "observe", + "request_sha256": "986f5d45a1f3aaaf691d2d5b1fc9d263bc3ec6951711d4084404e934d594c540", + "send_count": 1, + "state": "completed" + }, + "command-64fbd4a32ea94706b5cd8a3269debf04": { + "operation": "dispatch", + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "send_count": 1, + "state": "completed" + }, + "command-6a3f77b6799e42f79e802c7069d4f1ec": { + "operation": "start_job", + "request_sha256": "1910f73d909279fb9fe270813c8e8fe6f15630420d3da4fe0b18fac3c15bef7d", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-ee0f30ca70e9419296b602113d7775b9", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "f20a8e34e125480bab75c54183bb9355" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "body_identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/operator/client/images/challenge-7d780d4d02634b3cb2140c637af0004b-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "planner_owner": "9ceffbe2186a44f4a0017f64900d82b3", + "runner_status": "running", + "sequence": 13, + "stage": { + "arguments": { + "proposal_id": "proposal-29b7df0a14ad4747b5301091b0500015" + }, + "before_commands": [ + "command-0eb32e67ff4943979c667f45701af419", + "command-6184aa30e497418e8416b68ef4b0c87e", + "command-6a3f77b6799e42f79e802c7069d4f1ec" + ], + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "context": { + "action": { + "action": "move", + "intent": "Approach the block from the left to establish contact for a rightward push.", + "target_x": -0.175, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/operator/calls/9740c402-0754-4052-9a0e-55cc99c93600-01", + "decision_epoch": 0, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600" + }, + "control_epoch": 0, + "operation": "dispatch", + "proof": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "765c2bb33a6534296ca1b1d8fbb79414ebc70eb6626b170cbfaf8165710380bc" + } + }, + "usage": { + "calls": 1, + "input_tokens": 13057, + "output_tokens": 168, + "reported_calls": 1 + } + }, + "label": "before", + "posts": [ + { + "command_id": "command-6a3f77b6799e42f79e802c7069d4f1ec", + "payload_sha256": "1910f73d909279fb9fe270813c8e8fe6f15630420d3da4fe0b18fac3c15bef7d", + "sequence": 8 + }, + { + "command_id": "command-6184aa30e497418e8416b68ef4b0c87e", + "payload_sha256": "986f5d45a1f3aaaf691d2d5b1fc9d263bc3ec6951711d4084404e934d594c540", + "sequence": 16 + }, + { + "command_id": "command-0eb32e67ff4943979c667f45701af419", + "payload_sha256": "7aa96a1b5a27d086843fb21726afe9457ebc6ec442304c1d0e169000387b6126", + "sequence": 28 + }, + { + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "payload_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "sequence": 36 + } + ], + "processes": { + "body": 49737, + "workbench": 49762 + }, + "public": null, + "time": 1791860187.362379 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/snapshots/000002-offline.json b/jevdual/results/reusable-body-recovery-20261012/publication/snapshots/000002-offline.json new file mode 100644 index 0000000..2a12e54 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/snapshots/000002-offline.json @@ -0,0 +1,321 @@ +{ + "body_command": { + "digest_valid": true, + "intent": { + "request": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "arguments": { + "proposal_id": "proposal-29b7df0a14ad4747b5301091b0500015" + }, + "body_id": "88ef33b6618f41f58507907680f0bde8", + "client_id": "client-ee0f30ca70e9419296b602113d7775b9", + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "epoch": 1, + "lease_id": "f20a8e34e125480bab75c54183bb9355", + "operation": "dispatch", + "protocol": "footwork.body/1", + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "result_sha256": "f664cc35200c51b3992589dc2f86f7e525cb3d1cffa1370430908a865cfb097e" + }, + "terminal": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "protocol": "footwork.body/1", + "reason": null, + "receipt_scope": "retained_command_result", + "recorded_epoch": 1, + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "result": { + "next_observation": { + "challenge_id": "challenge-401b1194fabd45e28164ac2a3bfa08fd", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/body/bench/images/000001-688c5044eacced6b.png", + "observation": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "calibration": { + "bbox_convention": "exclusive_max_pixel_edges", + "height": 360, + "image_center_world_xy": [ + 0.0, + 0.0 + ], + "image_x_direction": "world_positive_x", + "image_y_direction": "world_negative_y", + "meters_per_pixel": 0.0016666666666666666, + "projection": "orthographic", + "valid": true, + "validity_reason": "bound orthographic calibration", + "vertical_span_m": 0.6, + "width": 640, + "world_frame": "sim_world" + }, + "generation": 1, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/body/bench/images/000001-688c5044eacced6b.png", + "image_sha256": "a831bd4576c7fa2d1ccf8d5369de1a242e0292a976ad40da9c39304dd00d96e5", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "native_time_s": 2.8339999999999095, + "phase": "prepare", + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state_sha256": "217fecedff3930f0e05f3a872d63789b89e7f1fdcfdd1475052ac27b7c8d72d7", + "version": 1 + }, + "observation_sha256": "5029c0a5b6f9473d370102217058277a925969ce964a35f14d48ecaa34087491", + "sequence": 4, + "session_id": "2658977143f14c54a5f37a582fadb568", + "task": { + "bounds": { + "x_max": 0.18, + "x_min": -0.32, + "y_max": 0.15, + "y_min": -0.15 + }, + "home_zone": { + "x_max": -0.08, + "x_min": -0.16, + "y_max": 0.03, + "y_min": -0.03 + }, + "pusher_dimensions_m": { + "x": 0.04, + "y": 0.14 + }, + "zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "observation_scope": "snapshot_at_dispatch_result", + "outcome": "executed", + "proposal_id": "proposal-29b7df0a14ad4747b5301091b0500015", + "receipt": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "command_speed_limit_m_s": 0.15, + "generation": 1, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "kind": "move", + "max_axis_actuator_force_n": 0.38503045596822894, + "native_steps": 917, + "native_time_s": 2.8339999999999095, + "operation_id": "agent-3cff47d83674457d9a0f353e7802863f", + "pusher_block_contact_samples": 0, + "state_sha256": "217fecedff3930f0e05f3a872d63789b89e7f1fdcfdd1475052ac27b7c8d72d7", + "target_xy": [ + -0.175, + 0.0 + ] + } + }, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state": "completed" + } + }, + "body_identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "moves": 1, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "diagnostic_enabled": true, + "generation": 1, + "jobs": {}, + "native_time_s": 2.8339999999999095, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-ee0f30ca70e9419296b602113d7775b9", + "epoch": 1, + "expires_in_s": 29.81952941697091, + "lease_id": "f20a8e34e125480bab75c54183bb9355" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-0eb32e67ff4943979c667f45701af419": { + "operation": "propose", + "request_sha256": "7aa96a1b5a27d086843fb21726afe9457ebc6ec442304c1d0e169000387b6126", + "send_count": 1, + "state": "completed" + }, + "command-6184aa30e497418e8416b68ef4b0c87e": { + "operation": "observe", + "request_sha256": "986f5d45a1f3aaaf691d2d5b1fc9d263bc3ec6951711d4084404e934d594c540", + "send_count": 1, + "state": "completed" + }, + "command-64fbd4a32ea94706b5cd8a3269debf04": { + "operation": "dispatch", + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "send_count": 1, + "state": "completed" + }, + "command-6a3f77b6799e42f79e802c7069d4f1ec": { + "operation": "start_job", + "request_sha256": "1910f73d909279fb9fe270813c8e8fe6f15630420d3da4fe0b18fac3c15bef7d", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-ee0f30ca70e9419296b602113d7775b9", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "f20a8e34e125480bab75c54183bb9355" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "body_identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/operator/client/images/challenge-7d780d4d02634b3cb2140c637af0004b-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "planner_owner": "9ceffbe2186a44f4a0017f64900d82b3", + "runner_status": "running", + "sequence": 13, + "stage": { + "arguments": { + "proposal_id": "proposal-29b7df0a14ad4747b5301091b0500015" + }, + "before_commands": [ + "command-0eb32e67ff4943979c667f45701af419", + "command-6184aa30e497418e8416b68ef4b0c87e", + "command-6a3f77b6799e42f79e802c7069d4f1ec" + ], + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "context": { + "action": { + "action": "move", + "intent": "Approach the block from the left to establish contact for a rightward push.", + "target_x": -0.175, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/operator/calls/9740c402-0754-4052-9a0e-55cc99c93600-01", + "decision_epoch": 0, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600" + }, + "control_epoch": 0, + "operation": "dispatch", + "proof": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "765c2bb33a6534296ca1b1d8fbb79414ebc70eb6626b170cbfaf8165710380bc" + } + }, + "usage": { + "calls": 1, + "input_tokens": 13057, + "output_tokens": 168, + "reported_calls": 1 + } + }, + "label": "offline", + "posts": [ + { + "command_id": "command-6a3f77b6799e42f79e802c7069d4f1ec", + "payload_sha256": "1910f73d909279fb9fe270813c8e8fe6f15630420d3da4fe0b18fac3c15bef7d", + "sequence": 8 + }, + { + "command_id": "command-6184aa30e497418e8416b68ef4b0c87e", + "payload_sha256": "986f5d45a1f3aaaf691d2d5b1fc9d263bc3ec6951711d4084404e934d594c540", + "sequence": 16 + }, + { + "command_id": "command-0eb32e67ff4943979c667f45701af419", + "payload_sha256": "7aa96a1b5a27d086843fb21726afe9457ebc6ec442304c1d0e169000387b6126", + "sequence": 28 + }, + { + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "payload_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "sequence": 36 + } + ], + "processes": { + "body": 49737, + "workbench": null + }, + "public": null, + "time": 1791860187.418903 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/snapshots/000003-restored.json b/jevdual/results/reusable-body-recovery-20261012/publication/snapshots/000003-restored.json new file mode 100644 index 0000000..b5a6120 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/snapshots/000003-restored.json @@ -0,0 +1,516 @@ +{ + "body_command": { + "digest_valid": true, + "intent": { + "request": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "arguments": { + "proposal_id": "proposal-29b7df0a14ad4747b5301091b0500015" + }, + "body_id": "88ef33b6618f41f58507907680f0bde8", + "client_id": "client-ee0f30ca70e9419296b602113d7775b9", + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "epoch": 1, + "lease_id": "f20a8e34e125480bab75c54183bb9355", + "operation": "dispatch", + "protocol": "footwork.body/1", + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "result_sha256": "f664cc35200c51b3992589dc2f86f7e525cb3d1cffa1370430908a865cfb097e" + }, + "terminal": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "protocol": "footwork.body/1", + "reason": null, + "receipt_scope": "retained_command_result", + "recorded_epoch": 1, + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "result": { + "next_observation": { + "challenge_id": "challenge-401b1194fabd45e28164ac2a3bfa08fd", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/body/bench/images/000001-688c5044eacced6b.png", + "observation": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "calibration": { + "bbox_convention": "exclusive_max_pixel_edges", + "height": 360, + "image_center_world_xy": [ + 0.0, + 0.0 + ], + "image_x_direction": "world_positive_x", + "image_y_direction": "world_negative_y", + "meters_per_pixel": 0.0016666666666666666, + "projection": "orthographic", + "valid": true, + "validity_reason": "bound orthographic calibration", + "vertical_span_m": 0.6, + "width": 640, + "world_frame": "sim_world" + }, + "generation": 1, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/body/bench/images/000001-688c5044eacced6b.png", + "image_sha256": "a831bd4576c7fa2d1ccf8d5369de1a242e0292a976ad40da9c39304dd00d96e5", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "native_time_s": 2.8339999999999095, + "phase": "prepare", + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state_sha256": "217fecedff3930f0e05f3a872d63789b89e7f1fdcfdd1475052ac27b7c8d72d7", + "version": 1 + }, + "observation_sha256": "5029c0a5b6f9473d370102217058277a925969ce964a35f14d48ecaa34087491", + "sequence": 4, + "session_id": "2658977143f14c54a5f37a582fadb568", + "task": { + "bounds": { + "x_max": 0.18, + "x_min": -0.32, + "y_max": 0.15, + "y_min": -0.15 + }, + "home_zone": { + "x_max": -0.08, + "x_min": -0.16, + "y_max": 0.03, + "y_min": -0.03 + }, + "pusher_dimensions_m": { + "x": 0.04, + "y": 0.14 + }, + "zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "observation_scope": "snapshot_at_dispatch_result", + "outcome": "executed", + "proposal_id": "proposal-29b7df0a14ad4747b5301091b0500015", + "receipt": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "command_speed_limit_m_s": 0.15, + "generation": 1, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "kind": "move", + "max_axis_actuator_force_n": 0.38503045596822894, + "native_steps": 917, + "native_time_s": 2.8339999999999095, + "operation_id": "agent-3cff47d83674457d9a0f353e7802863f", + "pusher_block_contact_samples": 0, + "state_sha256": "217fecedff3930f0e05f3a872d63789b89e7f1fdcfdd1475052ac27b7c8d72d7", + "target_xy": [ + -0.175, + 0.0 + ] + } + }, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state": "completed" + } + }, + "body_identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "moves": 1, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "diagnostic_enabled": true, + "generation": 1, + "jobs": {}, + "native_time_s": 2.8339999999999095, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-ee0f30ca70e9419296b602113d7775b9", + "epoch": 1, + "expires_in_s": 0, + "lease_id": "f20a8e34e125480bab75c54183bb9355" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-0eb32e67ff4943979c667f45701af419": { + "operation": "propose", + "request_sha256": "7aa96a1b5a27d086843fb21726afe9457ebc6ec442304c1d0e169000387b6126", + "send_count": 1, + "state": "completed" + }, + "command-6184aa30e497418e8416b68ef4b0c87e": { + "operation": "observe", + "request_sha256": "986f5d45a1f3aaaf691d2d5b1fc9d263bc3ec6951711d4084404e934d594c540", + "send_count": 1, + "state": "completed" + }, + "command-64fbd4a32ea94706b5cd8a3269debf04": { + "operation": "dispatch", + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "send_count": 1, + "state": "completed" + }, + "command-6a3f77b6799e42f79e802c7069d4f1ec": { + "operation": "start_job", + "request_sha256": "1910f73d909279fb9fe270813c8e8fe6f15630420d3da4fe0b18fac3c15bef7d", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-ee0f30ca70e9419296b602113d7775b9", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "f20a8e34e125480bab75c54183bb9355" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "body_identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/operator/client/images/challenge-7d780d4d02634b3cb2140c637af0004b-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "planner_owner": "6a148f9728924372beb2ed2d24ebf6bd", + "runner_status": "blocked", + "sequence": 16, + "stage": { + "arguments": { + "proposal_id": "proposal-29b7df0a14ad4747b5301091b0500015" + }, + "before_commands": [ + "command-0eb32e67ff4943979c667f45701af419", + "command-6184aa30e497418e8416b68ef4b0c87e", + "command-6a3f77b6799e42f79e802c7069d4f1ec" + ], + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "context": { + "action": { + "action": "move", + "intent": "Approach the block from the left to establish contact for a rightward push.", + "target_x": -0.175, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/operator/calls/9740c402-0754-4052-9a0e-55cc99c93600-01", + "decision_epoch": 0, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600" + }, + "control_epoch": 0, + "operation": "dispatch", + "proof": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "765c2bb33a6534296ca1b1d8fbb79414ebc70eb6626b170cbfaf8165710380bc" + } + }, + "usage": { + "calls": 1, + "input_tokens": 13057, + "output_tokens": 168, + "reported_calls": 1 + } + }, + "label": "restored", + "posts": [ + { + "command_id": "command-6a3f77b6799e42f79e802c7069d4f1ec", + "payload_sha256": "1910f73d909279fb9fe270813c8e8fe6f15630420d3da4fe0b18fac3c15bef7d", + "sequence": 8 + }, + { + "command_id": "command-6184aa30e497418e8416b68ef4b0c87e", + "payload_sha256": "986f5d45a1f3aaaf691d2d5b1fc9d263bc3ec6951711d4084404e934d594c540", + "sequence": 16 + }, + { + "command_id": "command-0eb32e67ff4943979c667f45701af419", + "payload_sha256": "7aa96a1b5a27d086843fb21726afe9457ebc6ec442304c1d0e169000387b6126", + "sequence": 28 + }, + { + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "payload_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "sequence": 36 + } + ], + "processes": { + "body": 49737, + "workbench": 56151 + }, + "public": { + "active_job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "activity": [ + { + "intent": "Recover interrupted move history", + "kind": "queued", + "outcome": "9740c402-0754-4052-9a0e-55cc99c93600", + "seq": 1, + "time": 1791860177.76828 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "1847f878480040c3bd6c815a4ad00a57f0d438356e9622d810510a80b8360662" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "start_job", + "seq": 2, + "time": 1791860177.779979 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 3, + "time": 1791860177.792662 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "start_job: command-6a3f77b6799e42f79e802c7069d4f1ec", + "seq": 4, + "time": 1791860177.807897 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "a8d2af57dd25b53b377aae185fdc1e8370d1e366f95cda8fee7ba4b8476b122a" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 5, + "time": 1791860177.817512 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 6, + "time": 1791860177.827703 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "observe: command-6184aa30e497418e8416b68ef4b0c87e", + "seq": 7, + "time": 1791860177.9513931 + }, + { + "intent": "Recover interrupted move history", + "kind": "inference", + "outcome": "1", + "seq": 8, + "time": 1791860177.9526951 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "2d928adef315873d71b63a8da09badb1a1ecc99b3df2210f9adc5dd67c514997" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 9, + "time": 1791860187.1988962 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 10, + "time": 1791860187.2108061 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "propose: command-0eb32e67ff4943979c667f45701af419", + "seq": 11, + "time": 1791860187.227283 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "765c2bb33a6534296ca1b1d8fbb79414ebc70eb6626b170cbfaf8165710380bc" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 12, + "time": 1791860187.23678 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 13, + "time": 1791860187.2483342 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "dispatch: command-64fbd4a32ea94706b5cd8a3269debf04", + "seq": 14, + "time": 1791860187.361224 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Restarted with a retained command stage; check its exact receipt", + "seq": 15, + "time": 1791860317.9355571 + }, + { + "intent": "", + "kind": "journal_recovered", + "outcome": "Retained 1 audit event(s); no command replayed", + "seq": 16, + "time": 1791860317.93731 + } + ], + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "connection": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Restarted with a retained command stage; check its exact receipt", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "label": "Recover interrupted move history", + "last_intent": "Approach the block from the left to establish contact for a rightward push.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "Restarted with a retained command stage; check its exact receipt" + }, + "recovery": { + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "operation": "dispatch", + "reason": "Restarted with a retained command stage; check its exact receipt", + "state": "uncertain" + }, + "runner_status": "blocked", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 1, + "input_tokens": 13057, + "output_tokens": 168, + "reported_calls": 1, + "token_usage_complete": true + } + }, + "time": 1791860318.0208929 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/snapshots/000004-finished.json b/jevdual/results/reusable-body-recovery-20261012/publication/snapshots/000004-finished.json new file mode 100644 index 0000000..cf053ca --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/snapshots/000004-finished.json @@ -0,0 +1,1046 @@ +{ + "body_command": { + "digest_valid": true, + "intent": { + "request": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "arguments": { + "proposal_id": "proposal-29b7df0a14ad4747b5301091b0500015" + }, + "body_id": "88ef33b6618f41f58507907680f0bde8", + "client_id": "client-ee0f30ca70e9419296b602113d7775b9", + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "epoch": 1, + "lease_id": "f20a8e34e125480bab75c54183bb9355", + "operation": "dispatch", + "protocol": "footwork.body/1", + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "result_sha256": "f664cc35200c51b3992589dc2f86f7e525cb3d1cffa1370430908a865cfb097e" + }, + "terminal": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "protocol": "footwork.body/1", + "reason": null, + "receipt_scope": "retained_command_result", + "recorded_epoch": 1, + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "result": { + "next_observation": { + "challenge_id": "challenge-401b1194fabd45e28164ac2a3bfa08fd", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/body/bench/images/000001-688c5044eacced6b.png", + "observation": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "calibration": { + "bbox_convention": "exclusive_max_pixel_edges", + "height": 360, + "image_center_world_xy": [ + 0.0, + 0.0 + ], + "image_x_direction": "world_positive_x", + "image_y_direction": "world_negative_y", + "meters_per_pixel": 0.0016666666666666666, + "projection": "orthographic", + "valid": true, + "validity_reason": "bound orthographic calibration", + "vertical_span_m": 0.6, + "width": 640, + "world_frame": "sim_world" + }, + "generation": 1, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/body/bench/images/000001-688c5044eacced6b.png", + "image_sha256": "a831bd4576c7fa2d1ccf8d5369de1a242e0292a976ad40da9c39304dd00d96e5", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "native_time_s": 2.8339999999999095, + "phase": "prepare", + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state_sha256": "217fecedff3930f0e05f3a872d63789b89e7f1fdcfdd1475052ac27b7c8d72d7", + "version": 1 + }, + "observation_sha256": "5029c0a5b6f9473d370102217058277a925969ce964a35f14d48ecaa34087491", + "sequence": 4, + "session_id": "2658977143f14c54a5f37a582fadb568", + "task": { + "bounds": { + "x_max": 0.18, + "x_min": -0.32, + "y_max": 0.15, + "y_min": -0.15 + }, + "home_zone": { + "x_max": -0.08, + "x_min": -0.16, + "y_max": 0.03, + "y_min": -0.03 + }, + "pusher_dimensions_m": { + "x": 0.04, + "y": 0.14 + }, + "zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "observation_scope": "snapshot_at_dispatch_result", + "outcome": "executed", + "proposal_id": "proposal-29b7df0a14ad4747b5301091b0500015", + "receipt": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "command_speed_limit_m_s": 0.15, + "generation": 1, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "kind": "move", + "max_axis_actuator_force_n": 0.38503045596822894, + "native_steps": 917, + "native_time_s": 2.8339999999999095, + "operation_id": "agent-3cff47d83674457d9a0f353e7802863f", + "pusher_block_contact_samples": 0, + "state_sha256": "217fecedff3930f0e05f3a872d63789b89e7f1fdcfdd1475052ac27b7c8d72d7", + "target_xy": [ + -0.175, + 0.0 + ] + } + }, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state": "completed" + } + }, + "body_identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "body_state": { + "bench": { + "active_job": null, + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "diagnostic_enabled": true, + "generation": 11, + "jobs": { + "9740c402-0754-4052-9a0e-55cc99c93600": { + "completed_generation": 11, + "completed_native_time_s": 23.61399999999779, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "measurement_operation_id": "agent-d27c560eecf9487081d0d4a85806b55d", + "measurement_receipt": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "generation": 3, + "image_sha256": "37eff0642da366ea639079d52a540617d36eb1bede2b25aa8eceba7cd7bf4d04", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 6.73599999999948, + "operation_id": "agent-d27c560eecf9487081d0d4a85806b55d", + "source_kind": "mujoco_simulation", + "state_sha256": "ca31851fa25da5c7518815e74b94b7a340ce26faf94063a0608582f1347c8961", + "version": 1, + "worker_pid": 58433 + }, + "moves": 11, + "phase": "completed", + "reset_receipt": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "block_home_verified": true, + "generation": 11, + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "kind": "finish_reset", + "native_time_s": 23.61399999999779, + "operation_id": "agent-42b2d69f655d4ecdaf4ca1bfa5bfd41e", + "pusher_home_verified": true, + "reset_contact_samples": 3132, + "reset_native_steps": 8439, + "state_sha256": "c88451508675d9e3a033cfcf0d8c626f353001ea20783eb3877b655452423257" + }, + "reset_started_generation": 3, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 23.61399999999779, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-ee0f30ca70e9419296b602113d7775b9", + "epoch": 1, + "expires_in_s": 0, + "lease_id": "375e3bb3703246bda3688aacd4d9fe5b" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-0112b4d76e234e00a72bc248dbeb97c7": { + "operation": "observe", + "request_sha256": "1c778c5ba3c17c45a6d4eafa27e05c34cbcedfb3ee62068ea5bbe8d099365751", + "send_count": 1, + "state": "completed" + }, + "command-05229f8323fb47948ec8f06a4e8bfd0b": { + "operation": "dispatch", + "request_sha256": "ce7b3109b3ead0311549f5042aad6c68ee389fd025b88e8d131f5bee0b597a1a", + "send_count": 1, + "state": "completed" + }, + "command-09e69ad7c8eb402a8afa0afbe2c07ed5": { + "operation": "dispatch", + "request_sha256": "66f379219af87e00f25f65833e500b66f3a0d5afa7bd7f3cb6e6cca27f068c74", + "send_count": 1, + "state": "completed" + }, + "command-0eb32e67ff4943979c667f45701af419": { + "operation": "propose", + "request_sha256": "7aa96a1b5a27d086843fb21726afe9457ebc6ec442304c1d0e169000387b6126", + "send_count": 1, + "state": "completed" + }, + "command-18963848635b4a69b89107f589013601": { + "operation": "dispatch", + "request_sha256": "8991a8940ebd63ee993372b473f318334474ba66792c1943f172deddad2c20f8", + "send_count": 1, + "state": "completed" + }, + "command-2198897f165f4a1da7b0a3ca5e181d74": { + "operation": "propose", + "request_sha256": "98815ef3733a8385e1a1f016584487b9f55517bde5ceaec947398bf31d0d9fbe", + "send_count": 1, + "state": "completed" + }, + "command-22709a65620240edb98fba24d06d9cd1": { + "operation": "dispatch", + "request_sha256": "81180749430a729000658397cf88ae1705fa8539374e0297c959c0ad0ef1189c", + "send_count": 1, + "state": "completed" + }, + "command-393a47142aeb4c789ee2722ae79992c9": { + "operation": "dispatch", + "request_sha256": "188a4b336f16f6eca410c9e227ed8f47902030884754e2ba64a8f16b8529c390", + "send_count": 1, + "state": "completed" + }, + "command-3dfb3a3579b14b59b03c6f77c05fbf7a": { + "operation": "observe", + "request_sha256": "a82c2def8beb99e2cbad2938d997e32862bb8be83b11233a3c82fd4ee6504322", + "send_count": 1, + "state": "completed" + }, + "command-45714737b58846deb2c7487445d1a6c2": { + "operation": "dispatch", + "request_sha256": "08d3cbfdce7ebdef39f65c05b21e9b98830539ff70bc6b4dfbd17781a34cad61", + "send_count": 1, + "state": "completed" + }, + "command-56c12b4ec539446baa274bc4afea7a0d": { + "operation": "observe", + "request_sha256": "8f8800e50142027dc2c10778f4b63fa0248d0841edce00507bc1b2981676e3dc", + "send_count": 1, + "state": "completed" + }, + "command-595ea7ba5c6f4604a8c77ef32473a284": { + "operation": "observe", + "request_sha256": "7c8eda69e3afe01ae59497221b2d912375b943491598171ffefa7a6a36199b3b", + "send_count": 1, + "state": "completed" + }, + "command-5f74b9dd5b4042e0ba10e22b7ebd5639": { + "operation": "dispatch", + "request_sha256": "9a0b72e5cc9e6a924c8a004f938adaa3d381ff01faf1bcd240a187f4c62e48cd", + "send_count": 1, + "state": "completed" + }, + "command-6184aa30e497418e8416b68ef4b0c87e": { + "operation": "observe", + "request_sha256": "986f5d45a1f3aaaf691d2d5b1fc9d263bc3ec6951711d4084404e934d594c540", + "send_count": 1, + "state": "completed" + }, + "command-64fbd4a32ea94706b5cd8a3269debf04": { + "operation": "dispatch", + "request_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "send_count": 1, + "state": "completed" + }, + "command-6a3f77b6799e42f79e802c7069d4f1ec": { + "operation": "start_job", + "request_sha256": "1910f73d909279fb9fe270813c8e8fe6f15630420d3da4fe0b18fac3c15bef7d", + "send_count": 1, + "state": "completed" + }, + "command-71a49a7f398c482a8ea879b6ce415b32": { + "operation": "observe", + "request_sha256": "03fcc6e4582ef95c175629c682a2001ecbe2728f6584cbc44e437155d1da212b", + "send_count": 1, + "state": "completed" + }, + "command-7546bd1cad864fc19dc4663d373ba7b7": { + "operation": "dispatch", + "request_sha256": "e53fc76e032b036a39cf36a1f3982398a9bbdd98c62f70215a530dbb9d8719d8", + "send_count": 1, + "state": "completed" + }, + "command-83cd8e8a10694db7a532503aa41c7377": { + "operation": "observe", + "request_sha256": "9dea0e2df83b04e7c926dfb82cacac2d1f3e18128b31f163f69a3d82793a7575", + "send_count": 1, + "state": "completed" + }, + "command-86a881f49b55477b80266213e0a23a11": { + "operation": "propose", + "request_sha256": "c377430ce7104e206caeed6111077e98e8caff9df1c9e65c167640864360ecc1", + "send_count": 1, + "state": "completed" + }, + "command-8bac74b0999343098a7fdfa1ea1d286f": { + "operation": "observe", + "request_sha256": "e622ba36eb5da9993fb466f0a95bc08fca9c6e894675ad681193fcd7fe18fa41", + "send_count": 1, + "state": "completed" + }, + "command-8ca707790e514e0c9ae33cb1f11d4c51": { + "operation": "propose", + "request_sha256": "0ec2b9e85e9f1eb70e824aa3a52db9edee201359cd426e7288a3d8d40ca471ec", + "send_count": 1, + "state": "completed" + }, + "command-97f9bc09a708441db34225bf3ed4717b": { + "operation": "dispatch", + "request_sha256": "fff3e9d80c44beb51038af77d39a2043b54aee5afcc761914bca026838fa946c", + "send_count": 1, + "state": "completed" + }, + "command-9a7127dbd95c46ff81d0e7fcd283de7f": { + "operation": "propose", + "request_sha256": "7062772b8970c4c49a40f196f77be3eb70f4bd93143ee925e3306198d16396e7", + "send_count": 1, + "state": "completed" + }, + "command-9f6aa399a0324089a50edcde725cd4f9": { + "operation": "propose", + "request_sha256": "285faf081765ad1248a57a307a361b628e4f2f1d4932f6f0ea9dafe2860dcdc5", + "send_count": 1, + "state": "completed" + }, + "command-a112a64649334572a922c9bb42e18837": { + "operation": "observe", + "request_sha256": "45df5e3c21488887b713c3b2c8d91474889feaa6dd1d4c46b98df849ff9e5ee9", + "send_count": 1, + "state": "completed" + }, + "command-aa1e6fa4fe9c4adb861246aa454e92c4": { + "operation": "observe", + "request_sha256": "e4d37e668533378c728fec6d87099ef58e5b3b654a152a6907f40b05ce54ed1a", + "send_count": 1, + "state": "completed" + }, + "command-af88ba2ef5854c978be55561a7e8f131": { + "operation": "propose", + "request_sha256": "406fa4af363ddc7ddfa3fcb78d0cfb8d5a76bbbb8b87ff29e3cf61d6711f0fe7", + "send_count": 1, + "state": "completed" + }, + "command-b28a6b2d77444f468a2078943ac2b2fd": { + "operation": "propose", + "request_sha256": "1059846519580b34ce13142aa226722ead68a91211b6b86384dab97f094eea2b", + "send_count": 1, + "state": "completed" + }, + "command-bb8e4592a615416381e6b7b2016a7144": { + "operation": "propose", + "request_sha256": "132e6f75ef2f8acbb43bbd940ee9212d9730a4411024bf1554cc4e38cfe6eff9", + "send_count": 1, + "state": "completed" + }, + "command-c2c7736a9e5f4f7884e76b71fdc3426e": { + "operation": "dispatch", + "request_sha256": "d70a8c76fe8604c31e373d00b8e768af704dd72370dfe1f0d0c318f8a4176077", + "send_count": 1, + "state": "completed" + }, + "command-c75b90b9776b4fa69476343684e06d30": { + "operation": "observe", + "request_sha256": "43e403321b5032780f94f2efb359a97125b4367fcaa1e06b2d396e6fa0500301", + "send_count": 1, + "state": "completed" + }, + "command-cb41c1601faf44de9c84f0c74f0593e6": { + "operation": "propose", + "request_sha256": "b62c7990b658b7d0435d9e4b06f97b7e380d1b2c7cd498f289be858fc8e863e1", + "send_count": 1, + "state": "completed" + }, + "command-cef7fcccbbfb4ff5a386b3edb53d126c": { + "operation": "propose", + "request_sha256": "0f7a157347bfc084d6433a25a00f8d0cb76974e417b277eaad30550de5a91316", + "send_count": 1, + "state": "completed" + }, + "command-da177becbb0c471e8858d465658c238e": { + "operation": "propose", + "request_sha256": "a16467bc80348f98bb5da4b6656113dd95619cbedaded1bc8bf8c6f215641814", + "send_count": 1, + "state": "completed" + }, + "command-dbb8187065264bc79d611a821a954e79": { + "operation": "observe", + "request_sha256": "4941d38b8da9bfa0aaf966e09fb7a825b160553695cf29258f5a5f32ee3dbc38", + "send_count": 1, + "state": "completed" + }, + "command-eaafe005cb894f6d8e76154fedf101e0": { + "operation": "propose", + "request_sha256": "59d7ab3d5988ca0b94077bf83360660baca180d74ac5115daa41c004081fec00", + "send_count": 1, + "state": "completed" + }, + "command-eb7c688252724d42bf7dfdab84841d20": { + "operation": "observe", + "request_sha256": "445adda192294f252cd1ff83213d773cf3d98a5c375c82e0c3b8f90a0869440f", + "send_count": 1, + "state": "completed" + }, + "command-ee34fbb0d81b4ebf9f5563ccf149b3c9": { + "operation": "dispatch", + "request_sha256": "351b85cd5b188cd7251ef513da3da4dc12596f0815ed450cb6df511afa258424", + "send_count": 1, + "state": "completed" + }, + "command-f031c3b324e0455796d09f27d38030da": { + "operation": "dispatch", + "request_sha256": "781049c543aae76bde732bca9b208bcc8b676b85d2c74a2b594a00dab8d28454", + "send_count": 1, + "state": "completed" + }, + "command-f068bdf4e6d84d6c9267ce6e0bc540d3": { + "operation": "propose", + "request_sha256": "d5c4a5aa48fa684768add40769f91f86b2f1fdf6392998e71a3cf9d29c22f55a", + "send_count": 1, + "state": "completed" + }, + "command-f0807bf65ea94cceb62c15ff010dc8ef": { + "operation": "dispatch", + "request_sha256": "0d0fab6dab0830bd106a72a86a4ba097052fcc1079e54e67eea5748ceeb0c0a7", + "send_count": 1, + "state": "completed" + }, + "command-fda4688de77f4621be0fef063d3be610": { + "operation": "observe", + "request_sha256": "2ac54619ad4895f41a8d2ec119a637daa350dd00bf90c89bdfa0e031ad0382be", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "lease": { + "client_id": "client-ee0f30ca70e9419296b602113d7775b9", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "375e3bb3703246bda3688aacd4d9fe5b" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "body_identity": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691" + }, + "frame": { + "generation": 11, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/operator/client/images/challenge-effbdd97f4fc4c558af4a6d71ec899b8-970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f.png", + "sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f" + }, + "planner_owner": "6a148f9728924372beb2ed2d24ebf6bd", + "runner_status": "running", + "sequence": 149, + "stage": null, + "usage": { + "calls": 14, + "input_tokens": 182740, + "output_tokens": 2070, + "reported_calls": 14 + } + }, + "label": "finished", + "posts": [ + { + "command_id": "command-6a3f77b6799e42f79e802c7069d4f1ec", + "payload_sha256": "1910f73d909279fb9fe270813c8e8fe6f15630420d3da4fe0b18fac3c15bef7d", + "sequence": 8 + }, + { + "command_id": "command-6184aa30e497418e8416b68ef4b0c87e", + "payload_sha256": "986f5d45a1f3aaaf691d2d5b1fc9d263bc3ec6951711d4084404e934d594c540", + "sequence": 16 + }, + { + "command_id": "command-0eb32e67ff4943979c667f45701af419", + "payload_sha256": "7aa96a1b5a27d086843fb21726afe9457ebc6ec442304c1d0e169000387b6126", + "sequence": 28 + }, + { + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "payload_sha256": "134c80ce5b51c6d125d0be700af5e42afec26e95ea6032a3b3c69b0abf1659c6", + "sequence": 36 + }, + { + "command_id": "command-0112b4d76e234e00a72bc248dbeb97c7", + "payload_sha256": "1c778c5ba3c17c45a6d4eafa27e05c34cbcedfb3ee62068ea5bbe8d099365751", + "sequence": 49 + }, + { + "command_id": "command-2198897f165f4a1da7b0a3ca5e181d74", + "payload_sha256": "98815ef3733a8385e1a1f016584487b9f55517bde5ceaec947398bf31d0d9fbe", + "sequence": 61 + }, + { + "command_id": "command-09e69ad7c8eb402a8afa0afbe2c07ed5", + "payload_sha256": "66f379219af87e00f25f65833e500b66f3a0d5afa7bd7f3cb6e6cca27f068c74", + "sequence": 69 + }, + { + "command_id": "command-595ea7ba5c6f4604a8c77ef32473a284", + "payload_sha256": "7c8eda69e3afe01ae59497221b2d912375b943491598171ffefa7a6a36199b3b", + "sequence": 77 + }, + { + "command_id": "command-eaafe005cb894f6d8e76154fedf101e0", + "payload_sha256": "59d7ab3d5988ca0b94077bf83360660baca180d74ac5115daa41c004081fec00", + "sequence": 89 + }, + { + "command_id": "command-f031c3b324e0455796d09f27d38030da", + "payload_sha256": "781049c543aae76bde732bca9b208bcc8b676b85d2c74a2b594a00dab8d28454", + "sequence": 97 + }, + { + "command_id": "command-3dfb3a3579b14b59b03c6f77c05fbf7a", + "payload_sha256": "a82c2def8beb99e2cbad2938d997e32862bb8be83b11233a3c82fd4ee6504322", + "sequence": 105 + }, + { + "command_id": "command-9f6aa399a0324089a50edcde725cd4f9", + "payload_sha256": "285faf081765ad1248a57a307a361b628e4f2f1d4932f6f0ea9dafe2860dcdc5", + "sequence": 117 + }, + { + "command_id": "command-c2c7736a9e5f4f7884e76b71fdc3426e", + "payload_sha256": "d70a8c76fe8604c31e373d00b8e768af704dd72370dfe1f0d0c318f8a4176077", + "sequence": 125 + }, + { + "command_id": "command-8bac74b0999343098a7fdfa1ea1d286f", + "payload_sha256": "e622ba36eb5da9993fb466f0a95bc08fca9c6e894675ad681193fcd7fe18fa41", + "sequence": 133 + }, + { + "command_id": "command-cef7fcccbbfb4ff5a386b3edb53d126c", + "payload_sha256": "0f7a157347bfc084d6433a25a00f8d0cb76974e417b277eaad30550de5a91316", + "sequence": 145 + }, + { + "command_id": "command-97f9bc09a708441db34225bf3ed4717b", + "payload_sha256": "fff3e9d80c44beb51038af77d39a2043b54aee5afcc761914bca026838fa946c", + "sequence": 153 + }, + { + "command_id": "command-c75b90b9776b4fa69476343684e06d30", + "payload_sha256": "43e403321b5032780f94f2efb359a97125b4367fcaa1e06b2d396e6fa0500301", + "sequence": 161 + }, + { + "command_id": "command-b28a6b2d77444f468a2078943ac2b2fd", + "payload_sha256": "1059846519580b34ce13142aa226722ead68a91211b6b86384dab97f094eea2b", + "sequence": 173 + }, + { + "command_id": "command-7546bd1cad864fc19dc4663d373ba7b7", + "payload_sha256": "e53fc76e032b036a39cf36a1f3982398a9bbdd98c62f70215a530dbb9d8719d8", + "sequence": 181 + }, + { + "command_id": "command-71a49a7f398c482a8ea879b6ce415b32", + "payload_sha256": "03fcc6e4582ef95c175629c682a2001ecbe2728f6584cbc44e437155d1da212b", + "sequence": 189 + }, + { + "command_id": "command-86a881f49b55477b80266213e0a23a11", + "payload_sha256": "c377430ce7104e206caeed6111077e98e8caff9df1c9e65c167640864360ecc1", + "sequence": 201 + }, + { + "command_id": "command-ee34fbb0d81b4ebf9f5563ccf149b3c9", + "payload_sha256": "351b85cd5b188cd7251ef513da3da4dc12596f0815ed450cb6df511afa258424", + "sequence": 209 + }, + { + "command_id": "command-a112a64649334572a922c9bb42e18837", + "payload_sha256": "45df5e3c21488887b713c3b2c8d91474889feaa6dd1d4c46b98df849ff9e5ee9", + "sequence": 217 + }, + { + "command_id": "command-da177becbb0c471e8858d465658c238e", + "payload_sha256": "a16467bc80348f98bb5da4b6656113dd95619cbedaded1bc8bf8c6f215641814", + "sequence": 229 + }, + { + "command_id": "command-05229f8323fb47948ec8f06a4e8bfd0b", + "payload_sha256": "ce7b3109b3ead0311549f5042aad6c68ee389fd025b88e8d131f5bee0b597a1a", + "sequence": 237 + }, + { + "command_id": "command-aa1e6fa4fe9c4adb861246aa454e92c4", + "payload_sha256": "e4d37e668533378c728fec6d87099ef58e5b3b654a152a6907f40b05ce54ed1a", + "sequence": 245 + }, + { + "command_id": "command-f068bdf4e6d84d6c9267ce6e0bc540d3", + "payload_sha256": "d5c4a5aa48fa684768add40769f91f86b2f1fdf6392998e71a3cf9d29c22f55a", + "sequence": 257 + }, + { + "command_id": "command-18963848635b4a69b89107f589013601", + "payload_sha256": "8991a8940ebd63ee993372b473f318334474ba66792c1943f172deddad2c20f8", + "sequence": 265 + }, + { + "command_id": "command-dbb8187065264bc79d611a821a954e79", + "payload_sha256": "4941d38b8da9bfa0aaf966e09fb7a825b160553695cf29258f5a5f32ee3dbc38", + "sequence": 273 + }, + { + "command_id": "command-bb8e4592a615416381e6b7b2016a7144", + "payload_sha256": "132e6f75ef2f8acbb43bbd940ee9212d9730a4411024bf1554cc4e38cfe6eff9", + "sequence": 285 + }, + { + "command_id": "command-45714737b58846deb2c7487445d1a6c2", + "payload_sha256": "08d3cbfdce7ebdef39f65c05b21e9b98830539ff70bc6b4dfbd17781a34cad61", + "sequence": 293 + }, + { + "command_id": "command-56c12b4ec539446baa274bc4afea7a0d", + "payload_sha256": "8f8800e50142027dc2c10778f4b63fa0248d0841edce00507bc1b2981676e3dc", + "sequence": 301 + }, + { + "command_id": "command-9a7127dbd95c46ff81d0e7fcd283de7f", + "payload_sha256": "7062772b8970c4c49a40f196f77be3eb70f4bd93143ee925e3306198d16396e7", + "sequence": 313 + }, + { + "command_id": "command-393a47142aeb4c789ee2722ae79992c9", + "payload_sha256": "188a4b336f16f6eca410c9e227ed8f47902030884754e2ba64a8f16b8529c390", + "sequence": 321 + }, + { + "command_id": "command-eb7c688252724d42bf7dfdab84841d20", + "payload_sha256": "445adda192294f252cd1ff83213d773cf3d98a5c375c82e0c3b8f90a0869440f", + "sequence": 329 + }, + { + "command_id": "command-cb41c1601faf44de9c84f0c74f0593e6", + "payload_sha256": "b62c7990b658b7d0435d9e4b06f97b7e380d1b2c7cd498f289be858fc8e863e1", + "sequence": 341 + }, + { + "command_id": "command-5f74b9dd5b4042e0ba10e22b7ebd5639", + "payload_sha256": "9a0b72e5cc9e6a924c8a004f938adaa3d381ff01faf1bcd240a187f4c62e48cd", + "sequence": 349 + }, + { + "command_id": "command-fda4688de77f4621be0fef063d3be610", + "payload_sha256": "2ac54619ad4895f41a8d2ec119a637daa350dd00bf90c89bdfa0e031ad0382be", + "sequence": 357 + }, + { + "command_id": "command-af88ba2ef5854c978be55561a7e8f131", + "payload_sha256": "406fa4af363ddc7ddfa3fcb78d0cfb8d5a76bbbb8b87ff29e3cf61d6711f0fe7", + "sequence": 369 + }, + { + "command_id": "command-f0807bf65ea94cceb62c15ff010dc8ef", + "payload_sha256": "0d0fab6dab0830bd106a72a86a4ba097052fcc1079e54e67eea5748ceeb0c0a7", + "sequence": 377 + }, + { + "command_id": "command-83cd8e8a10694db7a532503aa41c7377", + "payload_sha256": "9dea0e2df83b04e7c926dfb82cacac2d1f3e18128b31f163f69a3d82793a7575", + "sequence": 385 + }, + { + "command_id": "command-8ca707790e514e0c9ae33cb1f11d4c51", + "payload_sha256": "0ec2b9e85e9f1eb70e824aa3a52db9edee201359cd426e7288a3d8d40ca471ec", + "sequence": 397 + }, + { + "command_id": "command-22709a65620240edb98fba24d06d9cd1", + "payload_sha256": "81180749430a729000658397cf88ae1705fa8539374e0297c959c0ad0ef1189c", + "sequence": 405 + } + ], + "processes": { + "body": 49737, + "workbench": 56151 + }, + "public": { + "active_job_id": null, + "activity": [ + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "propose: command-cb41c1601faf44de9c84f0c74f0593e6", + "seq": 126, + "time": 1791860479.652856 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "738f29f8f5e9fd312ae3f99f4c188ddd39987a2a58116f33edcd588aba07ff6a" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 127, + "time": 1791860479.706536 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 128, + "time": 1791860479.746814 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "dispatch: command-5f74b9dd5b4042e0ba10e22b7ebd5639", + "seq": 129, + "time": 1791860479.963549 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "fa0dc5a06088d32b1b15e65152d9a50bd0ad228ffbbfdae8bf728e6c6714a296" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 130, + "time": 1791860480.022887 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 131, + "time": 1791860480.065069 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "observe: command-fda4688de77f4621be0fef063d3be610", + "seq": 132, + "time": 1791860480.2281978 + }, + { + "intent": "Recover interrupted move history", + "kind": "inference", + "outcome": "13", + "seq": 133, + "time": 1791860480.229849 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "3b298b9527f5e8cb07c8b7db3b171d9f6278051e3eec85fe4a7f148d795c6910" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 134, + "time": 1791860485.8839219 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 135, + "time": 1791860485.9236622 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "propose: command-af88ba2ef5854c978be55561a7e8f131", + "seq": 136, + "time": 1791860486.004915 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "2ba31eac98e12130060a434a8cf7dbb24aede37a43a776e3326506fc1974bb55" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 137, + "time": 1791860486.078175 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 138, + "time": 1791860486.122886 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "dispatch: command-f0807bf65ea94cceb62c15ff010dc8ef", + "seq": 139, + "time": 1791860486.393727 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "8b5eb923352a524eba5d884851c9c3b8380b3c1e18a11a8c78e75205ba313389" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 140, + "time": 1791860486.456125 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 141, + "time": 1791860486.497833 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "observe: command-83cd8e8a10694db7a532503aa41c7377", + "seq": 142, + "time": 1791860486.685514 + }, + { + "intent": "Recover interrupted move history", + "kind": "inference", + "outcome": "14", + "seq": 143, + "time": 1791860486.6878562 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "eef7f4f099d1744601db548ad504b9195160a4f86c8147c42bf328ace295337e" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 144, + "time": 1791860494.4948008 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 145, + "time": 1791860494.537086 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "propose: command-8ca707790e514e0c9ae33cb1f11d4c51", + "seq": 146, + "time": 1791860494.624099 + }, + { + "evidence": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "source_sha256": "9b1a1c7843da38da19b35aee16e8899b4f02887f75fab361cea78b74087ee691", + "state_sha256": "e16a3012af9b0ca0e4d640f641f7ecd78fd72a84052f5d45fea96fa846a77f69" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 147, + "time": 1791860494.680424 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 148, + "time": 1791860494.719587 + }, + { + "intent": "Recover interrupted move history", + "kind": "rpc_applied", + "outcome": "dispatch: command-22709a65620240edb98fba24d06d9cd1", + "seq": 149, + "time": 1791860495.0038579 + } + ], + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "connection": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 11, + "image_url": "/api/frame?generation=11&sha256=970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "label": "Recover interrupted move history", + "last_intent": "Verify the block center and pusher are both at their home positions.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "block_home_verified": true, + "generation": 11, + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "kind": "finish_reset", + "native_time_s": 23.61399999999779, + "operation_id": "agent-42b2d69f655d4ecdaf4ca1bfa5bfd41e", + "pusher_home_verified": true, + "reset_contact_samples": 3132, + "reset_native_steps": 8439, + "state_sha256": "c88451508675d9e3a033cfcf0d8c626f353001ea20783eb3877b655452423257" + }, + "status": "completed", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": true, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 14, + "input_tokens": 182740, + "output_tokens": 2070, + "reported_calls": 14, + "token_usage_complete": true + } + }, + "time": 1791861030.771453 +} diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/source-freeze.json b/jevdual/results/reusable-body-recovery-20261012/publication/source-freeze.json new file mode 100644 index 0000000..11dbaf8 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/source-freeze.json @@ -0,0 +1,45 @@ +{ + "commit": "c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/bench_body_contract.py": "a4dd11d946a3226c6bc32f135b5d4484e86ac684ac3aa941d52d093062933a2b", + "tools/physical-workcell/body_client.py": "3ff0449f0865136d8c2d0913f34ca1dc8d9f5f25216ccbdcd1bf6530dcfb6fd1", + "tools/physical-workcell/body_contract.py": "3a8ba3f44d5f8ab9b5ab30e53642a78b176608fdab7719a68d725dc03d6974bd", + "tools/physical-workcell/body_network.py": "5dbb4480765ebb75bcdbafc870b1d6a468f56d4f89d924d8cd4ec851c4c02d0a", + "tools/physical-workcell/body_protocol.py": "301fca90dc215694468f5c6989080cf796ee0c205ff045f5e18e4968a2ca6bdf", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "d7ba2e5f3a520a4679f8309b866e45ba3abd1d22e4f8849b970302159af134a3", + "tools/physical-workcell/crash_body.py": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "tools/physical-workcell/crash_operator.py": "a97bd24b6320995f5ef2ef1b4e06ec1392f3f51987b551c45759612efa874a18", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/network_bench_operator.html": "10efef540879da4a710cee2aafe6712213ad645c8fd7890382da1048f96a6686", + "tools/physical-workcell/network_bench_operator.py": "9079729ea4e8b56ccdf2a6d187b0c5d79a23a33688748fe55d92d10922500b40", + "tools/physical-workcell/network_fault_proxy.py": "44b0b52f7c213c0f5f93b8997ba72be4e750bc9f1a4f7f6d7b2a8c4f1233b595", + "tools/physical-workcell/network_operator_runtime.py": "530666b4d4e54cc8e67a6180ac38821590bc42acd8a460c3d9066ad02542e229", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/persistent_body_client.py": "57ba866da8bcb53995ea9d93fbefbfd0556e53a2bde2b445deb4f427cd3d2a14", + "tools/physical-workcell/planner_guard.py": "52eeb806b824d62fc78d500d3bf285fcf8c78facedec8c17721eabae0e7c87da", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/qualify_network_body.py": "769c2514277a6f3101d8ff237041039c1e4ca85a5a76ec453dd9c9f87997a09c", + "tools/physical-workcell/qualify_network_workbench.py": "e5c72acda2ea8fae3658faba907448765df2f1e34e05749fbe85faed28ef003b", + "tools/physical-workcell/qualify_workbench_crashes.py": "da971a4190389414dd2114b86d2b7dd40adb2dba2c16e304782eab362244932e", + "tools/physical-workcell/qualify_workbench_recovery.py": "acdd708c879582cadefbdbe734da855143ad71242551d55625a0abacff8b26eb", + "tools/physical-workcell/qualify_workbench_restarts.py": "2bc3edc5ea39b69f3372d4274b757f06472409407a22cd3149dfa52fbf314dcb", + "tools/physical-workcell/record_network_workbench.py": "7604865caa309c7002c01a2384da84eb62d8b22dcd63ab256618c536bc5fedc4", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_network_body.py": "d1f7150a5ff23775664b5656fd21379502ba71175487fd7aadb2b51bffcdaf8c", + "tools/physical-workcell/score_network_workbench.py": "67a53586081fe425bd14b0a0fbbf3d7f0fc1e66daccda06765bb88f3d9d4d84c", + "tools/physical-workcell/score_workbench_crashes.py": "10bec02c29afdb9963006884fd9ce310a52be8ba0995698cc11f5d6e4475b998", + "tools/physical-workcell/score_workbench_recovery.py": "8bac7d17ca63f54f70c0ad9321c9d399f0e55f17f22de61a2783d3535a443135", + "tools/physical-workcell/score_workbench_restarts.py": "181cbf4ee1bbd5483f38edc4a84244fc3efa98dfd21b5c9751d43edfe8f9f5a5", + "tools/physical-workcell/test_workbench_crash_harness.py": "b5a1398f9ccec220731f30c9f082260e378743f6fa6c3d954f008c2e79aeaf6e", + "tools/physical-workcell/test_workbench_recovery_trials.py": "9983772236f3867c1507f90546969fa99bfe6dba99de43bbcf4b18daa60a9b76", + "tools/physical-workcell/workbench_link.py": "79571f62c5a9b5fb843a852bc34a2a509b0dc6dc1a6e091fa9f948ab98fae420", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui-ledger.json b/jevdual/results/reusable-body-recovery-20261012/publication/ui-ledger.json new file mode 100644 index 0000000..91b7cf0 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/ui-ledger.json @@ -0,0 +1,246 @@ +{ + "steps": [ + { + "browser_action": "Observed offline state; cached evidence and disabled controls retained", + "cached_public": null, + "client_commands": 4, + "fetch_error": "[Errno 61] Connection refused", + "name": "crash-offline", + "offline": true, + "pending_command_ids": [], + "pins": { + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "body_id": "88ef33b6618f41f58507907680f0bde8" + }, + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/ui/crash-offline.png", + "snapshot_provenance": { + "name": "000002-offline.json", + "sha256": "992c40b69b4f48109253bb0602c7b9644e583adaff0c26ecdb09169514d4ecec" + }, + "state": null, + "time": 1791860317.602944, + "visible_text": "Browser tab: 5, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51413/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51413/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t54 checkbox Recover interrupted move history Working, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t55 text Recover interrupted move history\n\t\t56 text Disconnected\n\t\t14 button (disabled) Pause, ID: pause\n\t\t57 text The workbench is unavailable. The last camera image and job status are retained.\n\t\t15 container Description: Body connection, ID: network-card\n\t\t\t58 text Body connected\n\t\t\t59 text Last known body status. The workbench cannot check it while disconnected.\n\t\t\t18 button (disabled) Disconnect, ID: disconnect\n\t\t\t19 button (disabled) Check receipt, ID: reconcile\n\t\t\t20 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t21 text Test the simulated connection\n\t\t22 container Camera observation\n\t\t\t23 text OVERHEAD CAMERA\n\t\t\t60 text Retained observation \u00b7 workbench offline\n\t\t\t53 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t26 text Orange part Blue pusher Green task region\n\t\t\t61 text Retained \u00b7 workbench offline \u00b7 generation 0\n\t\t62 text WORKBENCH OFFLINE\n\t\t63 text The camera image and job status are from the workbench\u2019s last reply. Reconnect before continuing.\n\t\t30 container Job progress\n\t\t\t31 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t32 container Measurement\n\t\t\t33 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t34 text Measurement\n\t\t\t64 text Pending\n\t\t\t36 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Reloaded retained root; no automatic continuation or command replay", + "cached_public": null, + "client_commands": 4, + "name": "restored", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/ui/restored.png", + "state": { + "active_job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "connection": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Restarted with a retained command stage; check its exact receipt", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "label": "Recover interrupted move history", + "last_intent": "Approach the block from the left to establish contact for a rightward push.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "operation": "dispatch", + "reason": "Restarted with a retained command stage; check its exact receipt", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791860352.906879, + "visible_text": "Browser tab: 5, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51413/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51413/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 checkbox Recover interrupted move history Needs attention, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t12 text Recover interrupted move history\n\t\t13 text Needs attention\n\t\t14 button (disabled) Pause, ID: pause\n\t\t15 text Restarted with a retained command stage; check its exact receipt\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t17 text Body connected\n\t\t\t18 text Body identity verified by read-only TCP health\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t25 text Last observation \u00b7 Recover interrupted move history\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t28 text Captured \u00b7 generation 0\n\t\t29 text OUTCOME UNCERTAIN\n\t\t30 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t36 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Clicked Check receipt; original move receipt recovered while paused without new sends", + "cached_public": null, + "client_commands": 4, + "name": "recovered", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/ui/recovered.png", + "state": { + "active_job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "connection": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": null, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "label": "Recover interrupted move history", + "last_intent": "Approach the block from the left to establish contact for a rightward push.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-64fbd4a32ea94706b5cd8a3269debf04", + "operation": "dispatch", + "reason": "Exact historical stage recovered; explicit Resume observes fresh evidence", + "state": "recovered" + }, + "runner_status": "paused" + }, + "time": 1791860365.9127622, + "visible_text": "Browser tab: 5, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51413/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51413/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t69 checkbox Recover interrupted move history Paused, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t70 text Recover interrupted move history\n\t\t71 text Paused\n\t\t14 button Resume, ID: pause\n\t\t72 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue.\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t73 text Body connected\n\t\t\t74 text Body identity verified by read-only TCP health\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button (disabled) Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t75 text Last observation \u00b7 Recover interrupted move history\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t76 text Captured \u00b7 generation 0\n\t\t77 text RECEIPT RECOVERED\n\t\t78 text The exact command receipt was recovered. The agent is paused until you resume.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t79 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Clicked explicit Resume before any additional reply fault; fresh TCP camera planning resumes", + "cached_public": null, + "client_commands": 8, + "name": "resumed", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/ui/resumed.png", + "state": { + "active_job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "connection": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 2, + "image_url": "/api/frame?generation=2&sha256=42819fa0bc8837bbd91f1098939426208f093b3ed7e6a2e198d725dd1e53787e" + }, + "jobs": [ + { + "call_count": 3, + "disturbance_test": false, + "error": null, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "label": "Recover interrupted move history", + "last_intent": "Push the block rightward until its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "A" + } + ], + "model_request_pending": true, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791860398.3226151, + "visible_text": "Browser tab: 5, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51413/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51413/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t69 checkbox Recover interrupted move history Paused, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t70 text Recover interrupted move history\n\t\t71 text Running\n\t\t14 button Pause, ID: pause\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t72 text Body connected\n\t\t\t73 text Current body boot verified for explicit Resume\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button (disabled) Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t74 text Last observation \u00b7 Recover interrupted move history\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t75 text Captured \u00b7 generation 0\n\t\t76 text AGENT\n\t\t77 text Approach the block from the left to establish contact for a rightward push.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t78 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Observed recorded measurement and verified reset after publication recovery", + "cached_public": null, + "client_commands": 43, + "name": "completed", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2/ui/completed.png", + "state": { + "active_job_id": null, + "app_id": "7fb0ac9324bd404fb51896b0f89c1c7d", + "connection": { + "adapter_id": "6d17e55437444025af20a6cc32e00464", + "body_id": "88ef33b6618f41f58507907680f0bde8", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 11, + "image_url": "/api/frame?generation=11&sha256=970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "label": "Recover interrupted move history", + "last_intent": "Verify the block center and pusher are both at their home positions.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "88ef33b6618f41f58507907680f0bde8", + "block_home_verified": true, + "generation": 11, + "image_sha256": "970fbf7a383c132c204bf8738fb951dbb9e226a02bc458dfa8c1bfca16d9855f", + "job_id": "9740c402-0754-4052-9a0e-55cc99c93600", + "kind": "finish_reset", + "native_time_s": 23.61399999999779, + "operation_id": "agent-42b2d69f655d4ecdaf4ca1bfa5bfd41e", + "pusher_home_verified": true, + "reset_contact_samples": 3132, + "reset_native_steps": 8439, + "state_sha256": "c88451508675d9e3a033cfcf0d8c626f353001ea20783eb3877b655452423257" + }, + "status": "completed", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791861030.574198, + "visible_text": "Browser tab: 5, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51413/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51413/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t69 checkbox Recover interrupted move history Completed, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t70 text Recover interrupted move history\n\t\t71 text Running\n\t\t14 button Pause, ID: pause\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t72 text Body connected\n\t\t\t73 text Body identity verified by read-only TCP health\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button (disabled) Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t74 text Last camera observation\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t75 text Captured \u00b7 generation 11\n\t\t76 text COMPLETED\n\t\t77 text Measurement recorded and reset verified. The bench is ready for another job.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t78 text Historical result\n\t\t\t79 container measurement-result\n\t\t\t\t80 text 1,726\n\t\t\t\t81 text Orange pixels\n\t\t\t\t82 container\n\t\t\t\t\t83 text 4,794.4\n\t\t\t\t\t84 text mm\u00b2\n\t\t\t\t85 text Apparent footprint\n\t\t\t\t86 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment.\n\t\t\t87 button Repeat job, ID: repeat" + } + ] +} diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/completed.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/publication/ui/completed.ledger.txt new file mode 100644 index 0000000..4508dd5 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/ui/completed.ledger.txt @@ -0,0 +1,45 @@ +Browser tab: 5, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51413/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 69 checkbox Recover interrupted move history Completed, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 70 text Recover interrupted move history + 71 text Running + 14 button Pause, ID: pause + 16 container Description: Body connection, ID: network-card + 72 text Body connected + 73 text Body identity verified by read-only TCP health + 19 button Disconnect, ID: disconnect + 20 button (disabled) Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 74 text Last camera observation + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 75 text Captured · generation 11 + 76 text COMPLETED + 77 text Measurement recorded and reset verified. The bench is ready for another job. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 78 text Historical result + 79 container measurement-result + 80 text 1,726 + 81 text Orange pixels + 82 container + 83 text 4,794.4 + 84 text mm² + 85 text Apparent footprint + 86 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 87 button Repeat job, ID: repeat \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/completed.png b/jevdual/results/reusable-body-recovery-20261012/publication/ui/completed.png new file mode 100644 index 0000000..2305ed3 Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/publication/ui/completed.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/completed.txt b/jevdual/results/reusable-body-recovery-20261012/publication/ui/completed.txt new file mode 100644 index 0000000..03d1f22 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/ui/completed.txt @@ -0,0 +1,79 @@ +Browser tab: 5, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51413/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 69 checkbox Recover interrupted move history Completed, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 70 text Recover interrupted move history + 71 text Running + 14 button Pause, ID: pause + 16 container Description: Body connection, ID: network-card + 72 text Body connected + 73 text Body identity verified by read-only TCP health + 19 button Disconnect, ID: disconnect + 20 button (disabled) Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 74 text Last camera observation + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 75 text Captured · generation 11 + 76 text COMPLETED + 77 text Measurement recorded and reset verified. The bench is ready for another job. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 78 text Historical result + 79 container measurement-result + 80 text 1,726 + 81 text Orange pixels + 82 container + 83 text 4,794.4 + 84 text mm² + 85 text Apparent footprint + 86 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 87 button Repeat job, ID: repeat + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 88 text 24 events + 42 content list activity + 89 container + 90 text 11:01 PM Action receipt checked Recover interrupted move history + 91 container + 92 text 11:01 PM Sending a guarded action + 93 container + 94 text 11:01 PM Fresh body check verified The next command uses current body evidence. + 95 container + 96 text 11:01 PM Action proposal accepted Recover interrupted move history + 97 container + 98 text 11:01 PM Checking the planned action + 99 container + 100 text 11:01 PM Fresh body check verified The next command uses current body evidence. + 101 container + 102 text 11:01 PM AI planning requested Recover interrupted move history + 103 container + 104 text 11:01 PM Camera observation received Recover interrupted move history + 105 container + 106 text 11:01 PM Requesting camera observation + 107 container + 108 text 11:01 PM Fresh body check verified The next command uses current body evidence. + 109 container + 110 text 11:01 PM Action receipt checked Recover interrupted move history + 111 container + 112 text 11:01 PM Sending a guarded action + 67 button (collapsed) Session details, Secondary Actions: Expand + 68 text Session details + 113 text 14 model requests · Reported tokens: 182,740 input / 2,070 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/crash-offline.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/publication/ui/crash-offline.ledger.txt new file mode 100644 index 0000000..631a234 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/ui/crash-offline.ledger.txt @@ -0,0 +1,38 @@ +Browser tab: 5, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51413/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 54 checkbox Recover interrupted move history Working, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 55 text Recover interrupted move history + 56 text Disconnected + 14 button (disabled) Pause, ID: pause + 57 text The workbench is unavailable. The last camera image and job status are retained. + 15 container Description: Body connection, ID: network-card + 58 text Body connected + 59 text Last known body status. The workbench cannot check it while disconnected. + 18 button (disabled) Disconnect, ID: disconnect + 19 button (disabled) Check receipt, ID: reconcile + 20 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 21 text Test the simulated connection + 22 container Camera observation + 23 text OVERHEAD CAMERA + 60 text Retained observation · workbench offline + 53 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 26 text Orange part Blue pusher Green task region + 61 text Retained · workbench offline · generation 0 + 62 text WORKBENCH OFFLINE + 63 text The camera image and job status are from the workbench’s last reply. Reconnect before continuing. + 30 container Job progress + 31 text 1 Prepare 2 Measure 3 Reset 4 Ready + 32 container Measurement + 33 heading Measurement, Value: 2, ID: measurement-heading + 34 text Measurement + 64 text Pending + 36 text The part’s visible footprint will be measured after its placement is verified. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/crash-offline.png b/jevdual/results/reusable-body-recovery-20261012/publication/ui/crash-offline.png new file mode 100644 index 0000000..6a30856 Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/publication/ui/crash-offline.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/crash-offline.txt b/jevdual/results/reusable-body-recovery-20261012/publication/ui/crash-offline.txt new file mode 100644 index 0000000..a4f5ef3 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/ui/crash-offline.txt @@ -0,0 +1,72 @@ +Browser tab: 5, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51413/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 54 checkbox Recover interrupted move history Working, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 55 text Recover interrupted move history + 56 text Disconnected + 14 button (disabled) Pause, ID: pause + 57 text The workbench is unavailable. The last camera image and job status are retained. + 15 container Description: Body connection, ID: network-card + 58 text Body connected + 59 text Last known body status. The workbench cannot check it while disconnected. + 18 button (disabled) Disconnect, ID: disconnect + 19 button (disabled) Check receipt, ID: reconcile + 20 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 21 text Test the simulated connection + 22 container Camera observation + 23 text OVERHEAD CAMERA + 60 text Retained observation · workbench offline + 53 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 26 text Orange part Blue pusher Green task region + 61 text Retained · workbench offline · generation 0 + 62 text WORKBENCH OFFLINE + 63 text The camera image and job status are from the workbench’s last reply. Reconnect before continuing. + 30 container Job progress + 31 text 1 Prepare 2 Measure 3 Reset 4 Ready + 32 container Measurement + 33 heading Measurement, Value: 2, ID: measurement-heading + 34 text Measurement + 64 text Pending + 36 text The part’s visible footprint will be measured after its placement is verified. + 37 container Activity + 38 heading Activity, Value: 2, ID: activity-heading + 39 text Activity + 65 text 13 events + 41 content list activity + 66 container + 67 text 10:56 PM Sending a guarded action + 68 container + 69 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 70 container + 71 text 10:56 PM Action proposal accepted Recover interrupted move history + 72 container + 73 text 10:56 PM Checking the planned action + 74 container + 75 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 76 container + 77 text 10:56 PM AI planning requested Recover interrupted move history + 78 container + 79 text 10:56 PM Camera observation received Recover interrupted move history + 80 container + 81 text 10:56 PM Requesting camera observation + 82 container + 83 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 84 container + 85 text 10:56 PM Inspection started Recover interrupted move history + 86 container + 87 text 10:56 PM Preparing inspection + 88 container + 89 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 44 button (collapsed) Session details, Secondary Actions: Expand + 45 text Session details + 90 text 1 model requests · Reported tokens: 13,057 input / 168 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/recovered.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/publication/ui/recovered.ledger.txt new file mode 100644 index 0000000..dc6ad1b --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/ui/recovered.ledger.txt @@ -0,0 +1,38 @@ +Browser tab: 5, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51413/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 69 checkbox Recover interrupted move history Paused, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 70 text Recover interrupted move history + 71 text Paused + 14 button Resume, ID: pause + 72 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue. + 16 container Description: Body connection, ID: network-card + 73 text Body connected + 74 text Body identity verified by read-only TCP health + 19 button Disconnect, ID: disconnect + 20 button (disabled) Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 75 text Last observation · Recover interrupted move history + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 76 text Captured · generation 0 + 77 text RECEIPT RECOVERED + 78 text The exact command receipt was recovered. The agent is paused until you resume. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 79 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/recovered.png b/jevdual/results/reusable-body-recovery-20261012/publication/ui/recovered.png new file mode 100644 index 0000000..b78ebda Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/publication/ui/recovered.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/recovered.txt b/jevdual/results/reusable-body-recovery-20261012/publication/ui/recovered.txt new file mode 100644 index 0000000..231916d --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/ui/recovered.txt @@ -0,0 +1,72 @@ +Browser tab: 5, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51413/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 69 checkbox Recover interrupted move history Paused, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 70 text Recover interrupted move history + 71 text Paused + 14 button Resume, ID: pause + 72 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue. + 16 container Description: Body connection, ID: network-card + 73 text Body connected + 74 text Body identity verified by read-only TCP health + 19 button Disconnect, ID: disconnect + 20 button (disabled) Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 75 text Last observation · Recover interrupted move history + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 76 text Captured · generation 0 + 77 text RECEIPT RECOVERED + 78 text The exact command receipt was recovered. The agent is paused until you resume. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 79 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 80 text 18 events + 42 content list activity + 81 container + 82 text 10:59 PM Command receipt recovered The agent remains paused until you resume. + 83 container + 84 text 10:59 PM Action receipt checked Recover interrupted move history + 85 container + 86 text 10:58 PM Interrupted history recovered The recorded history was retained. Command outcomes still require their exact receipts. + 87 container + 88 text 10:58 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt + 89 container + 90 text 10:56 PM Action receipt checked Recover interrupted move history + 91 container + 92 text 10:56 PM Sending a guarded action + 93 container + 94 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 95 container + 96 text 10:56 PM Action proposal accepted Recover interrupted move history + 97 container + 98 text 10:56 PM Checking the planned action + 99 container + 100 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 101 container + 102 text 10:56 PM AI planning requested Recover interrupted move history + 103 container + 104 text 10:56 PM Camera observation received Recover interrupted move history + 67 button (collapsed) Session details, Secondary Actions: Expand + 68 text Session details + 105 text 1 model requests · Reported tokens: 13,057 input / 168 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/restored.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/publication/ui/restored.ledger.txt new file mode 100644 index 0000000..9a29645 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/ui/restored.ledger.txt @@ -0,0 +1,38 @@ +Browser tab: 5, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51413/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 checkbox Recover interrupted move history Needs attention, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 12 text Recover interrupted move history + 13 text Needs attention + 14 button (disabled) Pause, ID: pause + 15 text Restarted with a retained command stage; check its exact receipt + 16 container Description: Body connection, ID: network-card + 17 text Body connected + 18 text Body identity verified by read-only TCP health + 19 button Disconnect, ID: disconnect + 20 button Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 25 text Last observation · Recover interrupted move history + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 28 text Captured · generation 0 + 29 text OUTCOME UNCERTAIN + 30 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 36 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/restored.png b/jevdual/results/reusable-body-recovery-20261012/publication/ui/restored.png new file mode 100644 index 0000000..ccbe983 Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/publication/ui/restored.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/restored.txt b/jevdual/results/reusable-body-recovery-20261012/publication/ui/restored.txt new file mode 100644 index 0000000..d3abeb4 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/ui/restored.txt @@ -0,0 +1,72 @@ +Browser tab: 5, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51413/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 checkbox Recover interrupted move history Needs attention, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 12 text Recover interrupted move history + 13 text Needs attention + 14 button (disabled) Pause, ID: pause + 15 text Restarted with a retained command stage; check its exact receipt + 16 container Description: Body connection, ID: network-card + 17 text Body connected + 18 text Body identity verified by read-only TCP health + 19 button Disconnect, ID: disconnect + 20 button Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 25 text Last observation · Recover interrupted move history + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 28 text Captured · generation 0 + 29 text OUTCOME UNCERTAIN + 30 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 36 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 41 text 16 events + 42 content list activity + 43 container + 44 text 10:58 PM Interrupted history recovered The recorded history was retained. Command outcomes still require their exact receipts. + 45 container + 46 text 10:58 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt + 47 container + 48 text 10:56 PM Action receipt checked Recover interrupted move history + 49 container + 50 text 10:56 PM Sending a guarded action + 51 container + 52 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 53 container + 54 text 10:56 PM Action proposal accepted Recover interrupted move history + 55 container + 56 text 10:56 PM Checking the planned action + 57 container + 58 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 59 container + 60 text 10:56 PM AI planning requested Recover interrupted move history + 61 container + 62 text 10:56 PM Camera observation received Recover interrupted move history + 63 container + 64 text 10:56 PM Requesting camera observation + 65 container + 66 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 67 button (collapsed) Session details, Secondary Actions: Expand + 68 text Session details + 69 text 1 model requests · Reported tokens: 13,057 input / 168 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/resumed.ledger.txt b/jevdual/results/reusable-body-recovery-20261012/publication/ui/resumed.ledger.txt new file mode 100644 index 0000000..1e58dfb --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/ui/resumed.ledger.txt @@ -0,0 +1,37 @@ +Browser tab: 5, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51413/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 69 checkbox Recover interrupted move history Paused, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 70 text Recover interrupted move history + 71 text Running + 14 button Pause, ID: pause + 16 container Description: Body connection, ID: network-card + 72 text Body connected + 73 text Current body boot verified for explicit Resume + 19 button Disconnect, ID: disconnect + 20 button (disabled) Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 74 text Last observation · Recover interrupted move history + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 75 text Captured · generation 0 + 76 text AGENT + 77 text Approach the block from the left to establish contact for a rightward push. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 78 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/resumed.png b/jevdual/results/reusable-body-recovery-20261012/publication/ui/resumed.png new file mode 100644 index 0000000..19008b2 Binary files /dev/null and b/jevdual/results/reusable-body-recovery-20261012/publication/ui/resumed.png differ diff --git a/jevdual/results/reusable-body-recovery-20261012/publication/ui/resumed.txt b/jevdual/results/reusable-body-recovery-20261012/publication/ui/resumed.txt new file mode 100644 index 0000000..c619ccd --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/publication/ui/resumed.txt @@ -0,0 +1,71 @@ +Browser tab: 5, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51413/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 69 checkbox Recover interrupted move history Paused, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 70 text Recover interrupted move history + 71 text Running + 14 button Pause, ID: pause + 16 container Description: Body connection, ID: network-card + 72 text Body connected + 73 text Current body boot verified for explicit Resume + 19 button Disconnect, ID: disconnect + 20 button (disabled) Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 74 text Last observation · Recover interrupted move history + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 75 text Captured · generation 0 + 76 text AGENT + 77 text Approach the block from the left to establish contact for a rightward push. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 78 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 79 text 19 events + 42 content list activity + 80 container + 81 text 10:59 PM Agent resumed + 82 container + 83 text 10:59 PM Command receipt recovered The agent remains paused until you resume. + 84 container + 85 text 10:59 PM Action receipt checked Recover interrupted move history + 86 container + 87 text 10:58 PM Interrupted history recovered The recorded history was retained. Command outcomes still require their exact receipts. + 88 container + 89 text 10:58 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt + 90 container + 91 text 10:56 PM Action receipt checked Recover interrupted move history + 92 container + 93 text 10:56 PM Sending a guarded action + 94 container + 95 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 96 container + 97 text 10:56 PM Action proposal accepted Recover interrupted move history + 98 container + 99 text 10:56 PM Checking the planned action + 100 container + 101 text 10:56 PM Fresh body check verified The next command uses current body evidence. + 102 container + 103 text 10:56 PM AI planning requested Recover interrupted move history + 67 button (collapsed) Session details, Secondary Actions: Expand + 68 text Session details + 104 text 1 model requests · Reported tokens: 13,057 input / 168 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51413/ \ No newline at end of file diff --git a/jevdual/results/reusable-body-recovery-20261012/report.md b/jevdual/results/reusable-body-recovery-20261012/report.md new file mode 100644 index 0000000..71e6124 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/report.md @@ -0,0 +1,37 @@ +# Reusable body recovery + +The two host recovery gaps are fixed, and the shared body contract is separated from bench operations. Three final browser cases passed independent post-stop scoring on `c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54`. + +| Final case | AI decisions | Interrupted request | Commands | Outcome | +|---|---:|---:|---:|---| +| Event saved before host manifest | 14 | 0 | 43 | Exact receipt recovered; measurement and contact reset completed | +| Host killed during actual planning, then measurement reply lost | 14 | 1 | 44 | All observed old process identities/groups absent; fresh planning; exact measurement receipt recovered; reset completed | +| Native motion saved before completion receipt | 1 | 0 | 4 | Original command remains uncertain; two receipt checks cannot release it | + +All 91 commands had one wire POST. The final runs retained 29 actual image-guided decisions and one interrupted reservation. Both completed jobs have exactly one independently checked SQLite measurement and a verified native contact reset. The missing-receipt case remains quarantined, with no successor command or automatic reset. Evidence is in the three case folders and `summary.json`. + +## Implementation + +Startup validates the immutable event sequence and retained activity before importing an orphan audit tail. It repairs the index, never the physical outcome. Malformed or inconsistent evidence refuses startup; an unfinished command keeps its identity and requires its exact receipt. + +Planning uses a host-liveness pipe, a call ownership lock, atomic proof publication and a new owner after restart. Prompt delivery remains nonblocking while checking liveness and timeout. The guardian accounts for observed child identities and withholds closure if one survives. It never signals arbitrary retained PIDs. Codex shell snapshots are disabled. The final actual planning gate still observed transient node/git processes in other groups; independent qualification accounted for every captured identity/group before continuation. + +`body_contract.py` contains identity, epoch, lease and envelope rules. `persistent_body_client.py` supplies durable single-send transport and exact receipt reads with required schema hooks. `bench_body_contract.py` contains reachable zones, movement bounds, bench actions and image/result validation. Existing `BodyClient` callers retain their bench facade. A separate simulated camera-pointing vocabulary exercised the shared client over real TCP without importing bench, MuJoCo or numpy. + +The browser shows interrupted history and retired planners. When the host is unavailable, cached job selection keeps actions disabled and the camera explicitly labelled as retained evidence. Recovery stays paused until explicit Resume obtains a fresh challenge. + +## Checks and review + +The retained logs cover 43 body/client/contract/network checks, 59 existing operator/HTTP/health regressions, 25 final focused planner/journal checks and 25 final native/scorer/trial controls. The unique test identifiers are listed in `checks/test-identifiers.json`. Focused independent review found and closed a blocked-input availability concern, incomplete descendant/leader proof checks, and offline camera provenance drift. Scoped Ruff and notebook checks are recorded separately. + +The expected pre-fix stdin stall did not reproduce on this Mac: the original guardian buffered the valid 64 KiB prompt. The revised path was tested under partial-delivery backpressure and retired its child after host death. This is a failed initial prediction, not evidence of a prior reproduced stall. + +## Earlier attempts + +`attempt-ledger.json` separates the earlier passing publication predecessor from the unqualified planning capture race. The original screenshots, API record and private roots are retained. The failed Darwin cleanup attempt, source-edit-invalidated network test, and partial proof observation are recorded in the engineering notebook. Final metrics exclude the predecessor and failed capture attempt. + +## Scope + +This qualifies selected process-SIGKILL boundaries in a loopback simulation. It does not establish power-loss durability, arbitrary instruction-level recovery, OS-level containment of unsampled detachment, authenticated remote control or physical device safety. Client event-publication/pre-send windows remain outside this host repair. Identity admission remains simulation-only; a hardware controller must qualify its own operation bounds and evidence. Journal and job budgets remain finite. + +The saved scores and snapshots describe stopped trials before live handoff appends administrative restart events. Handoff state and its browser URL are recorded separately in `handoff.json`. diff --git a/jevdual/results/reusable-body-recovery-20261012/summary.json b/jevdual/results/reusable-body-recovery-20261012/summary.json new file mode 100644 index 0000000..23396e5 --- /dev/null +++ b/jevdual/results/reusable-body-recovery-20261012/summary.json @@ -0,0 +1,46 @@ +{ + "source_commit": "c00c65cf29fb468cbf2c1601d44f0e5d26bf4b54", + "cases": [ + { + "case": "publication", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-publication-v2", + "passed": true, + "reservations": 14, + "interrupted": 0, + "decisions": 14, + "commands": 43, + "completed_cycles": 1, + "safely_blocked": false, + "event_cutoff": 149 + }, + { + "case": "planning", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-planning-v2", + "passed": true, + "reservations": 15, + "interrupted": 1, + "decisions": 14, + "commands": 44, + "completed_cycles": 1, + "safely_blocked": false, + "event_cutoff": 154 + }, + { + "case": "native-gap", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim14-native-gap-v1", + "passed": true, + "reservations": 1, + "interrupted": 0, + "decisions": 1, + "commands": 4, + "completed_cycles": 0, + "safely_blocked": true, + "event_cutoff": 19 + } + ], + "decisions": 29, + "interrupted": 1, + "commands": 91, + "completed_cycles": 2, + "passing_test_identifiers": 152 +} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/baseline.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/baseline.txt new file mode 100644 index 0000000..c5b9b50 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/baseline.txt @@ -0,0 +1,53 @@ +ProductName: macOS +ProductVersion: 26.6.2 +BuildVersion: 25G83 +System Integrity Protection status: enabled. + Device Identifier: disk0 + Device Node: /dev/disk0 + Whole: Yes + Part of Whole: disk0 + Device / Media Name: Apple Inc. VirtIO Block Media + + Volume Name: FOOTWORK_U2 + Mounted: Yes + Mount Point: /Volumes/FOOTWORK_U2 + + Content (IOContent): None + File System Personality: ISO Joliet + Type (Bundle): cd9660 + Name (User Visible): ISO 9660 (Joliet) + + OS Can Be Installed: No + Media Type: + Protocol: + SMART Status: Not Supported + + Disk Size: 379.8 MB (379752448 Bytes) (exactly 741704 512-Byte-Units) + Device Block Size: 512 Bytes + + Volume Total Space: 379.8 MB (379752448 Bytes) (exactly 741704 512-Byte-Units) + Volume Used Space: 379.8 MB (379752448 Bytes) (exactly 741704 512-Byte-Units) (100.0%) + Volume Free Space: 0 B (0 Bytes) (exactly 0 512-Byte-Units) (0.0%) + Allocation Block Size: 2048 Bytes + + Media OS Use Only: No + Media Read-Only: Yes + Volume Read-Only: Yes (read-only mount flag set) + + Device Location: Internal + Removable Media: Fixed + + Solid State: Yes + Hardware AES Support: Yes + +total 741608 +drwxr-xr-x 1 tom staff 2048 Oct 11 11:08 . +drwxr-xr-x 4 root wheel 128 Oct 11 13:02 .. +-rwxr-xr-x 1 tom staff 308344 Oct 11 11:08 footwork-onboarding-u2-packet-v3.zip +-rwxr-xr-x 1 tom staff 282304813 Oct 11 11:08 googlechrome.dmg +-rwxr-xr-x 1 tom staff 2340 Oct 11 11:08 PROVENANCE.json +-rwxr-xr-x 1 tom staff 78557763 Oct 11 11:08 python-3.14.7-macos11.pkg +-rwxr-xr-x 1 tom staff 382 Oct 11 11:08 SHA256SUMS.txt +-rwxr-xr-x 1 tom staff 18518284 Oct 11 11:08 uv-0.12.5-aarch64-apple-darwin.tar.gz +com.apple.Terminal +2.15 diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/chrome-installed-version.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/chrome-installed-version.txt new file mode 100644 index 0000000..5e0c189 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/chrome-installed-version.txt @@ -0,0 +1 @@ +155.0.8059.40 diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/chrome-signature.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/chrome-signature.txt new file mode 100644 index 0000000..3a48196 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/chrome-signature.txt @@ -0,0 +1,54 @@ +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/. +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/app_mode_loader +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/app_mode_loader +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/chrome_crashpad_handler +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/chrome_crashpad_handler +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (Alerts).app +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (Alerts).app +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (Aperitif Alerts).app +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (Aperitif Alerts).app +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (Aperitif GPU).app +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (Aperitif GPU).app +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (Aperitif Renderer).app +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (Aperitif Renderer).app +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (Aperitif).app +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (Aperitif).app +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (GPU).app +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (GPU).app +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (Renderer).app +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper (Renderer).app +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper.app +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/Google Chrome Helper.app +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/GoogleUpdater.app +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/GoogleUpdater.app/Contents/Helpers/GoogleSoftwareUpdate.bundle +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/GoogleUpdater.app/Contents/Helpers/GoogleSoftwareUpdate.bundle/Contents/Helpers/ksadmin +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/GoogleUpdater.app/Contents/Helpers/GoogleSoftwareUpdate.bundle/Contents/Helpers/ksadmin +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/GoogleUpdater.app/Contents/Helpers/GoogleSoftwareUpdate.bundle/Contents/Helpers/ksinstall +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/GoogleUpdater.app/Contents/Helpers/GoogleSoftwareUpdate.bundle/Contents/Helpers/ksinstall +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/GoogleUpdater.app/Contents/Helpers/GoogleSoftwareUpdate.bundle +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/GoogleUpdater.app/Contents/Helpers/launcher +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/GoogleUpdater.app/Contents/Helpers/launcher +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/GoogleUpdater.app +--prepared:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/web_app_shortcut_copier +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/Helpers/web_app_shortcut_copier +--validated:/Volumes/Google Chrome/Google Chrome.app/Contents/Frameworks/Google Chrome Framework.framework/Versions/Current/. +/Volumes/Google Chrome/Google Chrome.app: valid on disk +/Volumes/Google Chrome/Google Chrome.app: satisfies its Designated Requirement +Executable=/Volumes/Google Chrome/Google Chrome.app/Contents/MacOS/Google Chrome +Identifier=com.google.Chrome +Format=app bundle with Mach-O universal (x86_64 arm64) +CodeDirectory v=20500 size=765 flags=0x12a00(kill,restrict,library-validation,runtime) hashes=13+7 location=embedded +Signature size=8991 +Authority=Developer ID Application: Google LLC (EQHXZ8M8AV) +Authority=Developer ID Certification Authority +Authority=Apple Root CA +Timestamp=Oct 5, 2026 at 10:43:45 PM +Notarization Ticket=stapled +Info.plist entries=44 +TeamIdentifier=EQHXZ8M8AV +Runtime Version=26.5.0 +Sealed Resources version=2 rules=13 files=63 +Internal requirements count=1 size=288 +/Volumes/Google Chrome/Google Chrome.app: accepted +source=Notarized Developer ID +155.0.8059.40 diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/denied-baseline.sha256 b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/denied-baseline.sha256 new file mode 100644 index 0000000..c0b8fbe --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/denied-baseline.sha256 @@ -0,0 +1,3 @@ +ead3090a56bc29b4a971b41f991b79872e07099f3f1ba9bb634825495ed32512 /Users/tom/footwork-u2/footwork/output/doctor-before.json +72c307cdccd3ec932c0927ab6143545726664f02cfcd7afdaccd320f8a412cfe /Users/tom/footwork-u2/denied-fixture.txt +54d419eb2b383f6c08172c87f5c084d2b0bb36ac552e695a8b7a9f5d611ea806 /Users/tom/footwork-u2/footwork/output/install-v1/receipt.json diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/denied-fixture-exit.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/denied-fixture-exit.txt new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/denied-fixture-exit.txt @@ -0,0 +1 @@ +2 diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/denied-fixture.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/denied-fixture.txt new file mode 100644 index 0000000..88d16cb --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/denied-fixture.txt @@ -0,0 +1,3 @@ +jevdual._core unavailable (cannot import name '_core' from partially initialized module 'jevdual' (most likely due to a circular import) (/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py)); falling back to pure Python +usage: capability_fixture.py [-h] --run-dir RUN_DIR +capability_fixture.py: error: fixture requires existing Accessibility permission; no resources created diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/doctor-after-exit.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/doctor-after-exit.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/doctor-after-exit.txt @@ -0,0 +1 @@ +0 diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/doctor-after.stderr.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/doctor-after.stderr.txt new file mode 100644 index 0000000..f6c4b85 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/doctor-after.stderr.txt @@ -0,0 +1 @@ +jevdual._core unavailable (cannot import name '_core' from partially initialized module 'jevdual' (most likely due to a circular import) (/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py)); falling back to pure Python diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/doctor-before-exit.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/doctor-before-exit.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/doctor-before-exit.txt @@ -0,0 +1 @@ +1 diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/doctor-before.stderr.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/doctor-before.stderr.txt new file mode 100644 index 0000000..f6c4b85 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/doctor-before.stderr.txt @@ -0,0 +1 @@ +jevdual._core unavailable (cannot import name '_core' from partially initialized module 'jevdual' (most likely due to a circular import) (/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py)); falling back to pure Python diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/fixture-console.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/fixture-console.txt new file mode 100644 index 0000000..2f559fe --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/fixture-console.txt @@ -0,0 +1,20 @@ +jevdual._core unavailable (cannot import name '_core' from partially initialized module 'jevdual' (most likely due to a circular import) (/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py)); falling back to pure Python +Fixture document cleanup could not be confirmed; browser cleanup continues. +Traceback (most recent call last): + File "/Users/tom/footwork-u2/footwork/jevdual/scripts/capability_fixture.py", line 173, in + main() + ~~~~^^ + File "/Users/tom/footwork-u2/footwork/jevdual/scripts/capability_fixture.py", line 128, in main + scope = asyncio.run(scope_for(browser.pid, url, document)) + File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/asyncio/runners.py", line 205, in run + return runner.run(main) + ~~~~~~~~~~^^^^^^ + File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/asyncio/runners.py", line 128, in run + return self._loop.run_until_complete(task) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^ + File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/asyncio/base_events.py", line 720, in run_until_complete + return future.result() + ~~~~~~~~~~~~~^^ + File "/Users/tom/footwork-u2/footwork/jevdual/scripts/capability_fixture.py", line 82, in scope_for + raise RuntimeError("Owned browser/document windows could not be identified uniquely") +RuntimeError: Owned browser/document windows could not be identified uniquely diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/footwork/output/doctor-after.json b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/footwork/output/doctor-after.json new file mode 100644 index 0000000..66ae255 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/footwork/output/doctor-after.json @@ -0,0 +1 @@ +{"platform": "darwin", "supported": true, "runtime": {"python_version": "3.14.7", "python_requirement": ">=3.11,<4.0", "python_compatible": true, "interpreter": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", "architecture": "arm64", "distribution": {"version": "0.0.1", "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "loaded_modules": {"jevdual": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", "jevdual.capability.setup": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/setup.py", "ApplicationServices": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py", "Quartz": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py"}}, "dependencies": {"browser-use": {"version": "0.13.10", "requirement": "==0.13.10", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "typesafe-sdk": {"version": "0.7.4", "requirement": "<1,>=0.7", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "orjson": {"version": "3.13.0", "requirement": ">=3.10", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "psutil": {"version": "7.2.2", "requirement": "==7.2.2", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "mcp": {"version": "2.1.1", "requirement": "==2.1.1", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "packaging": {"version": "26.3", "requirement": "<27,>=25", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "cua-driver": {"version": "0.28.2", "requirement": "==0.28.2", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-applicationservices": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-quartz": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-cocoa": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}}, "permissions": {"accessibility": "granted", "screen_recording": "not_granted"}, "scope": "not_checked", "journal": "not_checked", "warnings": ["Screen Recording is not qualified. Routes that capture pixels may require it."], "dependency_metadata": "installed distribution", "limitation": "macOS only: one loopback HTTP browser fixture and one identified TextEdit document. Window identity is checked when binding.", "provider_account_required": false, "rust_toolchain_required": false, "blockers": [], "prerequisite_status": "met", "qualification": "Prerequisites only. Window identity, current state, effects and job completion require separate checks."} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/footwork/output/doctor-before.json b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/footwork/output/doctor-before.json new file mode 100644 index 0000000..6da1941 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/footwork/output/doctor-before.json @@ -0,0 +1 @@ +{"platform": "darwin", "supported": true, "runtime": {"python_version": "3.14.7", "python_requirement": ">=3.11,<4.0", "python_compatible": true, "interpreter": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", "architecture": "arm64", "distribution": {"version": "0.0.1", "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "loaded_modules": {"jevdual": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", "jevdual.capability.setup": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/setup.py", "ApplicationServices": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py", "Quartz": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py"}}, "dependencies": {"browser-use": {"version": "0.13.10", "requirement": "==0.13.10", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "typesafe-sdk": {"version": "0.7.4", "requirement": "<1,>=0.7", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "orjson": {"version": "3.13.0", "requirement": ">=3.10", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "psutil": {"version": "7.2.2", "requirement": "==7.2.2", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "mcp": {"version": "2.1.1", "requirement": "==2.1.1", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "packaging": {"version": "26.3", "requirement": "<27,>=25", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "cua-driver": {"version": "0.28.2", "requirement": "==0.28.2", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-applicationservices": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-quartz": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-cocoa": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages"}}, "permissions": {"accessibility": "not_granted", "screen_recording": "not_granted"}, "scope": "not_checked", "journal": "not_checked", "warnings": ["Screen Recording is not qualified. Routes that capture pixels may require it."], "dependency_metadata": "installed distribution", "limitation": "macOS only: one loopback HTTP browser fixture and one identified TextEdit document. Window identity is checked when binding.", "provider_account_required": false, "rust_toolchain_required": false, "blockers": ["accessibility_not_granted"], "prerequisite_status": "blocked", "qualification": "Prerequisites only. Window identity, current state, effects and job completion require separate checks."} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/footwork/output/fixture-v1/fixture-v1-invoice.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/footwork/output/fixture-v1/fixture-v1-invoice.txt new file mode 100644 index 0000000..14cfbd2 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/footwork/output/fixture-v1/fixture-v1-invoice.txt @@ -0,0 +1 @@ +Invoice report diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/footwork/output/install-v1/receipt.json b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/footwork/output/install-v1/receipt.json new file mode 100644 index 0000000..ef1c5fb --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/footwork/output/install-v1/receipt.json @@ -0,0 +1,1523 @@ +{ + "source_commit": "33e1afb06710e2d2a57a0f7d1aea7ea0e19a878d", + "host": { + "platform": "Darwin", + "machine": "arm64" + }, + "interpreter_requested": "/Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14", + "wheel": "/Users/tom/footwork-u2/footwork/artifacts/jevdual-0.0.1-py3-none-any.whl", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7", + "cache_initially_empty": true, + "system_site_packages": false, + "borrowed_dependencies": false, + "registry": "https://pypi.org/simple", + "binary_only": true, + "commands": [ + { + "stage": "venv", + "argv": [ + "/Users/tom/footwork-u2/tools/uv-aarch64-apple-darwin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/tom/footwork-u2/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "venv", + "--python", + "/Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14", + "/Users/tom/footwork-u2/footwork/output/install-v1/venv" + ], + "returncode": 0 + }, + { + "stage": "install", + "argv": [ + "/Users/tom/footwork-u2/tools/uv-aarch64-apple-darwin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/tom/footwork-u2/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "pip", + "install", + "--python", + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "--default-index", + "https://pypi.org/simple", + "--only-binary", + ":all:", + "/Users/tom/footwork-u2/footwork/artifacts/jevdual-0.0.1-py3-none-any.whl" + ], + "returncode": 0 + }, + { + "stage": "consistency", + "argv": [ + "/Users/tom/footwork-u2/tools/uv-aarch64-apple-darwin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/tom/footwork-u2/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "pip", + "check", + "--python", + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python" + ], + "returncode": 0 + }, + { + "stage": "inventory", + "argv": [ + "/Users/tom/footwork-u2/tools/uv-aarch64-apple-darwin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/tom/footwork-u2/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "pip", + "list", + "--python", + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "--format", + "json" + ], + "returncode": 0 + }, + { + "stage": "origins", + "argv": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "-I", + "-c", + "\nimport sys,json,importlib.metadata,importlib\nfrom pathlib import Path\nimport jevdual,jevdual.capability.execution_worker as worker\nfrom jevdual._native import BACKEND\nnames=(\"cua_driver\",\"psutil\",\"mcp\",\"ApplicationServices\",\"Quartz\",\"Foundation\",\"packaging\")\nmodules={n:str(importlib.import_module(n).__file__) for n in names}\nprint(json.dumps({\"version\":sys.version,\"prefix\":sys.prefix,\"base_prefix\":sys.base_prefix,\"python\":sys.executable,\"package\":jevdual.__file__,\"worker\":worker.__file__,\"backend\":BACKEND,\"module_origins\":modules,\"sys_path\":sys.path}))\n" + ], + "returncode": 0 + }, + { + "stage": "doctor", + "argv": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/footwork-capability", + "doctor" + ], + "returncode": 0 + }, + { + "stage": "wheel-tags", + "argv": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "-I", + "-c", + "import json,importlib.metadata;print(json.dumps([{\"name\":d.metadata[\"Name\"],\"version\":d.version,\"tags\":[l[5:] for l in (d.read_text(\"WHEEL\") or \"\").splitlines() if l.startswith(\"Tag: \")]} for d in importlib.metadata.distributions()]))" + ], + "returncode": 0 + } + ], + "passed": true, + "limits": "Fresh dependencies on existing host OS/interpreter, not a fresh machine or TCC permission database.", + "origins": { + "version": "3.14.7 (v3.14.7:823f0323ee6, Aug 5 2026, 07:07:01) [Clang 21.0.0 (clang-2100.1.1.101)]", + "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", + "base_prefix": "/Library/Frameworks/Python.framework/Versions/3.14", + "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "package": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", + "worker": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/execution_worker.py", + "backend": "pure-python", + "module_origins": { + "cua_driver": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py", + "psutil": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/psutil/__init__.py", + "mcp": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py", + "ApplicationServices": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py", + "Quartz": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py", + "Foundation": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Foundation/__init__.py", + "packaging": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/packaging/__init__.py" + }, + "sys_path": [ + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python314.zip", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/lib-dynload", + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + ] + }, + "doctor": { + "platform": "darwin", + "supported": true, + "runtime": { + "python_version": "3.14.7", + "python_requirement": ">=3.11,<4.0", + "python_compatible": true, + "interpreter": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "architecture": "arm64", + "distribution": { + "version": "0.0.1", + "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "loaded_modules": { + "jevdual": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", + "jevdual.capability.setup": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/setup.py", + "ApplicationServices": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py", + "Quartz": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py" + } + }, + "dependencies": { + "browser-use": { + "version": "0.13.10", + "requirement": "==0.13.10", + "compatible": true, + "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "typesafe-sdk": { + "version": "0.7.4", + "requirement": "<1,>=0.7", + "compatible": true, + "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "orjson": { + "version": "3.13.0", + "requirement": ">=3.10", + "compatible": true, + "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "psutil": { + "version": "7.2.2", + "requirement": "==7.2.2", + "compatible": true, + "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "mcp": { + "version": "2.1.1", + "requirement": "==2.1.1", + "compatible": true, + "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "packaging": { + "version": "26.3", + "requirement": "<27,>=25", + "compatible": true, + "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "cua-driver": { + "version": "0.28.2", + "requirement": "==0.28.2", + "compatible": true, + "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "pyobjc-framework-applicationservices": { + "version": "12.2.2", + "requirement": ">=12", + "compatible": true, + "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "pyobjc-framework-quartz": { + "version": "12.2.2", + "requirement": ">=12", + "compatible": true, + "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "pyobjc-framework-cocoa": { + "version": "12.2.2", + "requirement": ">=12", + "compatible": true, + "location": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + } + }, + "permissions": { + "accessibility": "not_granted", + "screen_recording": "not_granted" + }, + "scope": "not_checked", + "journal": "not_checked", + "warnings": [ + "Screen Recording is not qualified. Routes that capture pixels may require it." + ], + "dependency_metadata": "installed distribution", + "limitation": "macOS only: one loopback HTTP browser fixture and one identified TextEdit document. Window identity is checked when binding.", + "provider_account_required": false, + "rust_toolchain_required": false, + "blockers": [ + "accessibility_not_granted" + ], + "prerequisite_status": "blocked", + "qualification": "Prerequisites only. Window identity, current state, effects and job completion require separate checks." + }, + "inventory": [ + { + "name": "aiofiles", + "version": "25.1.0" + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1" + }, + { + "name": "aiohttp", + "version": "3.14.3" + }, + { + "name": "aiosignal", + "version": "1.4.0" + }, + { + "name": "annotated-types", + "version": "0.8.0" + }, + { + "name": "anthropic", + "version": "0.76.0" + }, + { + "name": "anyio", + "version": "4.12.1" + }, + { + "name": "attrs", + "version": "26.1.0" + }, + { + "name": "backoff", + "version": "2.2.1" + }, + { + "name": "beautifulsoup4", + "version": "4.15.0" + }, + { + "name": "browser-harness", + "version": "0.1.13" + }, + { + "name": "browser-use", + "version": "0.13.10" + }, + { + "name": "browser-use-sdk", + "version": "3.4.2" + }, + { + "name": "bubus", + "version": "1.5.6" + }, + { + "name": "cdp-use", + "version": "1.4.5" + }, + { + "name": "certifi", + "version": "2026.7.22" + }, + { + "name": "cffi", + "version": "2.1.1" + }, + { + "name": "charset-normalizer", + "version": "3.5.2" + }, + { + "name": "click", + "version": "8.3.3" + }, + { + "name": "cloudpickle", + "version": "3.1.2" + }, + { + "name": "cryptography", + "version": "50.0.2" + }, + { + "name": "cua-driver", + "version": "0.28.2" + }, + { + "name": "cython", + "version": "3.3.0" + }, + { + "name": "distro", + "version": "1.9.0" + }, + { + "name": "docstring-parser", + "version": "0.18.0" + }, + { + "name": "fetch-use", + "version": "0.4.0" + }, + { + "name": "frozenlist", + "version": "1.8.0" + }, + { + "name": "google-api-core", + "version": "2.29.0" + }, + { + "name": "google-api-python-client", + "version": "2.188.0" + }, + { + "name": "google-auth", + "version": "2.48.0" + }, + { + "name": "google-auth-httplib2", + "version": "0.4.4" + }, + { + "name": "google-auth-oauthlib", + "version": "1.2.4" + }, + { + "name": "google-genai", + "version": "1.65.0" + }, + { + "name": "googleapis-common-protos", + "version": "1.75.5" + }, + { + "name": "groq", + "version": "1.0.0" + }, + { + "name": "h11", + "version": "0.16.0" + }, + { + "name": "httpcore", + "version": "1.0.9" + }, + { + "name": "httpcore2", + "version": "2.13.1" + }, + { + "name": "httplib2", + "version": "0.32.0" + }, + { + "name": "httpx", + "version": "0.28.1" + }, + { + "name": "httpx2", + "version": "2.13.1" + }, + { + "name": "idna", + "version": "3.20" + }, + { + "name": "inquirerpy", + "version": "0.3.4" + }, + { + "name": "jevdual", + "version": "0.0.1" + }, + { + "name": "jiter", + "version": "0.17.0" + }, + { + "name": "jsonschema", + "version": "4.26.0" + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1" + }, + { + "name": "lxml", + "version": "6.1.3" + }, + { + "name": "markdown-it-py", + "version": "4.2.0" + }, + { + "name": "markdownify", + "version": "1.2.2" + }, + { + "name": "mcp", + "version": "2.1.1" + }, + { + "name": "mcp-types", + "version": "2.1.1" + }, + { + "name": "mdurl", + "version": "0.1.2" + }, + { + "name": "multidict", + "version": "6.9.1" + }, + { + "name": "oauthlib", + "version": "4.0.0" + }, + { + "name": "ollama", + "version": "0.6.1" + }, + { + "name": "openai", + "version": "2.26.0" + }, + { + "name": "opentelemetry-api", + "version": "1.45.1" + }, + { + "name": "orjson", + "version": "3.13.0" + }, + { + "name": "packaging", + "version": "26.3" + }, + { + "name": "pfzy", + "version": "0.3.4" + }, + { + "name": "pillow", + "version": "12.3.0" + }, + { + "name": "portalocker", + "version": "4.4.0" + }, + { + "name": "posthog", + "version": "7.7.0" + }, + { + "name": "prompt-toolkit", + "version": "3.0.53" + }, + { + "name": "propcache", + "version": "0.5.4" + }, + { + "name": "proto-plus", + "version": "1.29.0" + }, + { + "name": "protobuf", + "version": "6.33.6" + }, + { + "name": "psutil", + "version": "7.2.2" + }, + { + "name": "pyasn1", + "version": "0.6.4" + }, + { + "name": "pyasn1-modules", + "version": "0.4.2" + }, + { + "name": "pycparser", + "version": "3.11" + }, + { + "name": "pydantic", + "version": "2.13.5" + }, + { + "name": "pydantic-core", + "version": "2.46.5" + }, + { + "name": "pydantic-settings", + "version": "2.15.0" + }, + { + "name": "pygments", + "version": "2.21.0" + }, + { + "name": "pyjwt", + "version": "2.15.1" + }, + { + "name": "pyobjc-core", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-applicationservices", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-cocoa", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-coretext", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-quartz", + "version": "12.2.2" + }, + { + "name": "pyotp", + "version": "2.9.0" + }, + { + "name": "pyparsing", + "version": "3.3.3" + }, + { + "name": "pypdf", + "version": "6.16.2" + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0" + }, + { + "name": "python-docx", + "version": "1.2.0" + }, + { + "name": "python-dotenv", + "version": "1.2.2" + }, + { + "name": "python-multipart", + "version": "0.0.32" + }, + { + "name": "referencing", + "version": "0.37.0" + }, + { + "name": "reportlab", + "version": "4.4.9" + }, + { + "name": "requests", + "version": "2.33.0" + }, + { + "name": "requests-oauthlib", + "version": "2.0.0" + }, + { + "name": "rich", + "version": "14.3.3" + }, + { + "name": "rpds-py", + "version": "2026.9.1" + }, + { + "name": "rsa", + "version": "4.9.1" + }, + { + "name": "screeninfo", + "version": "0.8.1" + }, + { + "name": "six", + "version": "1.17.0" + }, + { + "name": "sniffio", + "version": "1.3.1" + }, + { + "name": "soupsieve", + "version": "3.0.1" + }, + { + "name": "sse-starlette", + "version": "3.5.0" + }, + { + "name": "starlette", + "version": "1.7.0" + }, + { + "name": "tenacity", + "version": "9.1.4" + }, + { + "name": "tqdm", + "version": "4.70.1" + }, + { + "name": "truststore", + "version": "0.10.4" + }, + { + "name": "typesafe-sdk", + "version": "0.7.4" + }, + { + "name": "typing-extensions", + "version": "4.15.0" + }, + { + "name": "typing-inspection", + "version": "0.4.4" + }, + { + "name": "uritemplate", + "version": "4.2.0" + }, + { + "name": "urllib3", + "version": "2.8.0" + }, + { + "name": "uuid7", + "version": "0.1.0" + }, + { + "name": "uvicorn", + "version": "0.54.0" + }, + { + "name": "wcwidth", + "version": "0.9.2" + }, + { + "name": "websockets", + "version": "15.0.1" + }, + { + "name": "yarl", + "version": "1.25.1" + } + ], + "wheel_tags": [ + { + "name": "anyio", + "version": "4.12.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "uvicorn", + "version": "0.54.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "groq", + "version": "1.0.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "oauthlib", + "version": "4.0.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "cloudpickle", + "version": "3.1.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-framework-CoreText", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "portalocker", + "version": "4.4.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "lxml", + "version": "6.1.3", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "packaging", + "version": "26.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "Pygments", + "version": "2.21.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "inquirerpy", + "version": "0.3.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "browser-harness", + "version": "0.1.13", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "prompt_toolkit", + "version": "3.0.53", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jiter", + "version": "0.17.0", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "typesafe-sdk", + "version": "0.7.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jevdual", + "version": "0.0.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-api-python-client", + "version": "2.188.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "yarl", + "version": "1.25.1", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "posthog", + "version": "7.7.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jsonschema", + "version": "4.26.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "orjson", + "version": "3.13.0", + "tags": [ + "cp314-cp314-macosx_15_0_arm64" + ] + }, + { + "name": "google-auth-oauthlib", + "version": "1.2.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "requests", + "version": "2.33.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "PyJWT", + "version": "2.15.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "opentelemetry-api", + "version": "1.45.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pydantic_core", + "version": "2.46.5", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "browser-use-sdk", + "version": "3.4.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyotp", + "version": "2.9.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "docstring_parser", + "version": "0.18.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "propcache", + "version": "0.5.4", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "mcp", + "version": "2.1.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "rich", + "version": "14.3.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "uritemplate", + "version": "4.2.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "reportlab", + "version": "4.4.9", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "tenacity", + "version": "9.1.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "aiofiles", + "version": "25.1.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-framework-Quartz", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "httpcore", + "version": "1.0.9", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "cryptography", + "version": "50.0.2", + "tags": [ + "cp311-abi3-macosx_11_0_arm64" + ] + }, + { + "name": "pyobjc-framework-Cocoa", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "wcwidth", + "version": "0.9.2", + "tags": [ + "cp310-abi3-macosx_11_0_arm64" + ] + }, + { + "name": "googleapis-common-protos", + "version": "1.75.5", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-genai", + "version": "1.65.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "tqdm", + "version": "4.70.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "urllib3", + "version": "2.8.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-dotenv", + "version": "1.2.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "h11", + "version": "0.16.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pydantic", + "version": "2.13.5", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "protobuf", + "version": "6.33.6", + "tags": [ + "cp39-abi3-macosx_10_9_universal2" + ] + }, + { + "name": "pydantic-settings", + "version": "2.15.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "sniffio", + "version": "1.3.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "rsa", + "version": "4.9.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyasn1", + "version": "0.6.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "screeninfo", + "version": "0.8.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "mdurl", + "version": "0.1.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-multipart", + "version": "0.0.32", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-framework-ApplicationServices", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "fetch-use", + "version": "0.4.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "openai", + "version": "2.26.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-core", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "click", + "version": "8.3.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "markdownify", + "version": "1.2.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "frozenlist", + "version": "1.8.0", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "cffi", + "version": "2.1.1", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "uuid7", + "version": "0.1.0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "typing_extensions", + "version": "4.15.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "aiosignal", + "version": "1.4.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "certifi", + "version": "2026.7.22", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "browser-use", + "version": "0.13.10", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "annotated-types", + "version": "0.8.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "referencing", + "version": "0.37.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-auth-httplib2", + "version": "0.4.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pycparser", + "version": "3.11", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "markdown-it-py", + "version": "4.2.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "soupsieve", + "version": "3.0.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-auth", + "version": "2.48.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "bubus", + "version": "1.5.6", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "typing-inspection", + "version": "0.4.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "starlette", + "version": "1.7.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "distro", + "version": "1.9.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pfzy", + "version": "0.3.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "rpds-py", + "version": "2026.9.1", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "httplib2", + "version": "0.32.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-docx", + "version": "1.2.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "attrs", + "version": "26.1.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "ollama", + "version": "0.6.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "websockets", + "version": "15.0.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pypdf", + "version": "6.16.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "httpx", + "version": "0.28.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "httpx2", + "version": "2.13.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "sse-starlette", + "version": "3.5.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "backoff", + "version": "2.2.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyasn1_modules", + "version": "0.4.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-api-core", + "version": "2.29.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "beautifulsoup4", + "version": "4.15.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "psutil", + "version": "7.2.2", + "tags": [ + "cp36-abi3-macosx_11_0_arm64" + ] + }, + { + "name": "aiohttp", + "version": "3.14.3", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "cdp-use", + "version": "1.4.5", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "truststore", + "version": "0.10.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "charset-normalizer", + "version": "3.5.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "pillow", + "version": "12.3.0", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "Cython", + "version": "3.3.0", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "proto-plus", + "version": "1.29.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "httpcore2", + "version": "2.13.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyparsing", + "version": "3.3.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "cua-driver", + "version": "0.28.2", + "tags": [ + "py3-none-macosx_13_0_universal2" + ] + }, + { + "name": "six", + "version": "1.17.0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "requests-oauthlib", + "version": "2.0.0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "multidict", + "version": "6.9.1", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "anthropic", + "version": "0.76.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "mcp-types", + "version": "2.1.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "idna", + "version": "3.20", + "tags": [ + "py3-none-any" + ] + } + ], + "cache_bytes_after_install": 391978745, + "private_state": { + "private-home": "/Users/tom/footwork-u2/footwork/output/install-v1/private-home", + "private-config": "/Users/tom/footwork-u2/footwork/output/install-v1/private-config", + "private-cache": "/Users/tom/footwork-u2/footwork/output/install-v1/private-cache", + "private-temp": "/Users/tom/footwork-u2/footwork/output/install-v1/private-temp" + } +} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/install-console.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/install-console.txt new file mode 100644 index 0000000..07c54b2 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/install-console.txt @@ -0,0 +1,8 @@ +{"stage": "venv", "returncode": 0} +{"stage": "install", "returncode": 0} +{"stage": "consistency", "returncode": 0} +{"stage": "inventory", "returncode": 0} +{"stage": "origins", "returncode": 0} +{"stage": "doctor", "returncode": 0} +{"stage": "wheel-tags", "returncode": 0} +{"passed": true, "packages": 115, "python": "3.14.7"} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/install-exit.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/install-exit.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/install-exit.txt @@ -0,0 +1 @@ +0 diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/media-hashes.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/media-hashes.txt new file mode 100644 index 0000000..3b484a8 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/media-hashes.txt @@ -0,0 +1,4 @@ +footwork-onboarding-u2-packet-v3.zip: OK +python-3.14.7-macos11.pkg: OK +uv-0.12.5-aarch64-apple-darwin.tar.gz: OK +googlechrome.dmg: OK diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/packet-verification.txt b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/packet-verification.txt new file mode 100644 index 0000000..6656f83 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/guest-evidence/packet-verification.txt @@ -0,0 +1 @@ +{"verified": true, "members": 14, "source_commit": "33e1afb06710e2d2a57a0f7d1aea7ea0e19a878d", "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7"} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/report.md b/jevdual/results/u2-independent-onboarding-20261011-v1/report.md new file mode 100644 index 0000000..b1c8acd --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/report.md @@ -0,0 +1,29 @@ +# U2 independent guest qualification v1: partial record + +The independent guest is running after the human accepted the macOS license and created its account. This records observed setup gates; it does not qualify the installed permission journey. + +Root observed macOS 26.6.2 build 25G83, SIP enabled, and the guest interactive Terminal identified as com.apple.Terminal version 2.15. Guest diskutil reports both media and volume read-only at /Volumes/FOOTWORK_U2. The six staged filenames match the source inventory; shasum reports OK for all four payloads. Guest baseline.txt and media-hashes.txt under /Users/tom/footwork-u2 preserve these outputs. Their contents have not yet been exported to the host. Native CUA tool images in the chat are the independently observed evidence; no local screenshot path is claimed. + +The packet is freshly extracted and uv 0.12.5 runs. The official Python 3.14.7 installer reached its actual Agree dialog; the human approved that license, root accepted it, and installation is waiting for guest administrator authentication. Credential entry is handed to the human to keep it out of tool logs. Chrome's verified image is mounted read-only; codesign and Gatekeeper assessment were launched but their completion has not yet been observed. + +Synthetic modifier keys do not reliably reach this VM: sw_vers initially arrived as sw-vers. Root retained that failed command and verified a printf octal-decoded route with the same public command, then continued through the same guest Terminal without clipboard sharing or SSH. This input workaround is qualification infrastructure, not a product change or permission grant. + +Next gates: authenticate the guest installer; verify installed Python and pristine packet members; verify and install Chrome; run the frozen cold dependency installer; capture strict denied Accessibility diagnostics and fixture refusal; obtain the human's exact responsible-host grant, restart and read back; run the native append/restart/report oracle and qualify owned-resource cleanup. UO2 remains open. No Accessibility grant, product dependency install, native effect or cleanup test has run. + +Chrome assessment subsequently completed: deep strict codesign reports valid on disk and satisfies its designated requirement; Google LLC Team EQHXZ8M8AV; stapled notarization ticket; spctl accepted Notarized Developer ID. The bundle reports version 155.0.8059.40. Copy into the previously absent guest /Applications/Google Chrome.app has started; its completion remains pending observation. Guest chrome-signature.txt preserves the assessment. + +Chrome copy subsequently completed and the installed bundle version readback is 155.0.8059.40. Root left the guest Installer password field focused for human authentication; Python and all dependent qualification gates remain pending. + +The human completed installer authentication. Python 3.14.7 runs and the pristine member verifier exits0 with14members and matching frozen source/wheel hashes. The frozen cold installer completed all seven stages at exit0 and reports115packages. Initial isolated strict doctor exits1 with accessibility_not_granted; Screen Recording is not_granted and remains advisory. The frozen fixture exits2 with the exact Accessibility refusal, and its run directory is absent. No native effect has been admitted. The denied baseline is valid; grant, restart, native oracle and cleanup remain pending. The pure-Python fallback emits a noisy unavailable-core warning, retained in the guest stderr; it does not prevent the actual permission refusal. Guest receipts remain in the guest and have not yet been exported. + +At the handoff, guest System Settings > Privacy & Security > Accessibility visibly lists Terminal with its switch off. Human confirmation is requested before enabling that exact guest host. Claude has the actual baseline review request1791751059206860000, without a new verdict yet. UO2 remains open; no post-grant result is claimed. + +Claude reviewed the actual baseline at413686b and accepted it (message1791751104201824000), subject to two evidence clarifications. Terminal was first observed in the off Settings list after the cold installer diagnostic, strict doctor and fixture refusal; the exact inserting command cannot be reconstructed. No OS control-permission prompt was observed in captured states, and root never manually added Terminal. Direct product probes are nonprompting in source; this does not establish all transitive runtime effects or prove when the Settings entry appeared. Preserve that limitation. Before post-grant execution, print guest receipt hashes and establish verifiable raw export. The user has explicitly authorized root to toggle the exact guest Terminal entry, but the guest auto-locked before root could do so; no grant occurred. Human unlock is requested to keep credentials out of tool logs under the supplied AGENTS.md rule. + +After human unlock, Terminal switch was already on when root reopened Settings. Root did not toggle it; actor/time are not captured. Guest Terminal visibly quit/relaunched; installed strict doctor exits0 with Accessibility granted, Screen Recording not_granted and blockers empty. Original denied receipt hashes were captured before this diagnostic, but not demonstrably before the actual grant. + +The first post-grant fixture-v1 failed its30second identity deadline while normal Chrome first-launch verification was still visibly active. It emitted no scope and no product action ran. Python local-network discovery and Terminal TextEdit Automation prompts were declined. Only the unchanged owned document was closed manually. Preserve this failure; a separately preregistered development continuation uses fresh fixture-v2 and never replaces v1. + +The raw20member guest evidence archive was exported through a bridge-only, source-IP/size/hash-restricted receiver, which closed after one successful upload. The archive hash and three observed baseline hashes match; sanitized evidence copies are under guest-evidence. Claude received actual failure request1791753574080407000. U2 complete first-use qualification remains false. + +Claude independently reviewed the raw v1 archive (1791753660836044000): valid denied baseline and post-grant doctor, but failed first-use journey and unmet permission-transition gate. The separate v2 development continuation passed after normal Chrome verification; see ../u2-independent-onboarding-20261011-v2-dev. This does not change the original failure or qualify full first use. diff --git a/jevdual/results/u2-independent-onboarding-20261011-v1/summary.json b/jevdual/results/u2-independent-onboarding-20261011-v1/summary.json new file mode 100644 index 0000000..b7a70f2 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v1/summary.json @@ -0,0 +1,59 @@ +{ + "recorded_at": "2026-10-11T17:19:00-04:00", + "status": "cold_fixture_timeout_preserved", + "source_commit": "33e1afb06710e2d2a57a0f7d1aea7ea0e19a878d", + "packet_sha256": "769c08c89061731368db33d78b180e7c8c96d531190f43bee44263642a76fd0f", + "guest": { + "os": "macOS", + "version": "26.6.2", + "build": "25G83", + "sip": "enabled", + "responsible_host": "com.apple.Terminal", + "responsible_host_version": "2.15" + }, + "media": { + "mount": "/Volumes/FOOTWORK_U2", + "media_read_only": true, + "volume_read_only": true, + "visible_files": 6, + "payload_checksum_checks": 4, + "payload_checksum_passes": 4 + }, + "prerequisites": { + "uv_version": "0.12.5", + "python_installed": true, + "python_license": "human_approved_and_accepted", + "python_installer": "human authentication completed", + "chrome_dmg": "mounted_read_only", + "chrome_signature": "deep strict codesign valid; spctl accepted Notarized Developer ID; stapled ticket", + "chrome_version": "155.0.8059.40", + "chrome_installation": "completed at /Applications/Google Chrome.app; installed bundle version readback 155.0.8059.40", + "python_version": "3.14.7" + }, + "qualification": { + "packet_member_verification": "passed 14 members, exit0", + "cold_dependency_install": "passed 115 packages, seven stages exit0, installer exit0", + "denied_accessibility_baseline": "observed not_granted, strict doctor exit1, fixture exit2, fixture directory absent", + "permission_transition": "Settings on observed after human unlock; actor/time not captured; Terminal quit/relaunched; strict doctor exit0 Accessibility granted", + "native_effect_restart_report": "not_run: fixture-v1 identity deadline expired during Chrome first-launch verification", + "cleanup": "exact unchanged TextEdit document manually closed after declining Automation; fixture browser cleanup completed", + "qualified": false + }, + "evidence_location": "guest-evidence sanitized copies; exact raw archive output/u2-export-v1/evidence-v1.zip", + "claude_baseline_review": { + "message": "1791751104201824000", + "verdict": "valid denied baseline", + "clarifications": [ + "cause of Terminal entry not reconstructible retrospectively; no prompting dialog observed", + "raw receipt export and hashes required before post-grant run" + ] + }, + "export": { + "sha256": "b9dcde6aae648d760d1730bfe979756c6165549b46d7f6eaf6d00dcd9561ea18", + "bytes": 11988, + "members": 20, + "baseline_hashes_matched": 3, + "receiver_closed": true, + "hash_timing": "before doctor-after, actual grant timing not captured" + } +} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/cleanup-v2.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/cleanup-v2.json new file mode 100644 index 0000000..a2ee843 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/cleanup-v2.json @@ -0,0 +1,10 @@ +{ + "owned_pids_absent": { + "24765": true, + "24766": true, + "42605": true + }, + "fixture_loopback_closed": true, + "document_closed_by_operator": "exact fixture-v2 title observed then closed using native CUA; persistence not checked", + "automation_grant": "declined" +} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/config-v2-console.txt b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/config-v2-console.txt new file mode 100644 index 0000000..37111d3 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/config-v2-console.txt @@ -0,0 +1,2 @@ +jevdual._core unavailable (cannot import name '_core' from partially initialized module 'jevdual' (most likely due to a circular import) (/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py)); falling back to pure Python +{"task": "task_a5de7021e73f434e8713e86c651d73f7", "host_config": "/Users/tom/footwork-u2/footwork/output/job-v2/host.json", "scope": "/Users/tom/footwork-u2/footwork/output/job-v2/scope.json", "report_schema": "/Users/tom/footwork-u2/footwork/output/job-v2/report-schema.json", "report_adapter": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", "-I", "-m", "jevdual.capability.cli", "report", "--journal", "/Users/tom/footwork-u2/footwork/output/job-v2/operations.sqlite", "--task", "task_a5de7021e73f434e8713e86c651d73f7"], "limitation": "macOS only: one loopback HTTP browser fixture and one identified TextEdit document. Window identity is checked when binding.", "publication": "Validate the structured final report through report_adapter and publish only trusted_summary. Never fall back to raw host prose.", "lifecycle": "Reuse this task only for reconnect/restart of this job. Generate a new run directory for each new job."} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/config-v2-exit.txt b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/config-v2-exit.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/config-v2-exit.txt @@ -0,0 +1 @@ +0 diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/fixture-v2-console.txt b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/fixture-v2-console.txt new file mode 100644 index 0000000..8725dc4 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/fixture-v2-console.txt @@ -0,0 +1,3 @@ +jevdual._core unavailable (cannot import name '_core' from partially initialized module 'jevdual' (most likely due to a circular import) (/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py)); falling back to pure Python +{"ready": true, "scope": "/Users/tom/footwork-u2/footwork/output/fixture-v2/scope.json"} +Fixture document cleanup could not be confirmed; browser cleanup continues. diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/fixture-v2/fixture-v2-invoice.txt b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/fixture-v2/fixture-v2-invoice.txt new file mode 100644 index 0000000..c0b3c07 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/fixture-v2/fixture-v2-invoice.txt @@ -0,0 +1,2 @@ +Invoice report +17.25 USD \ No newline at end of file diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/fixture-v2/fixture.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/fixture-v2/fixture.json new file mode 100644 index 0000000..37330be --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/fixture-v2/fixture.json @@ -0,0 +1,7 @@ +{ + "url": "http://127.0.0.1:49175/", + "document": "/Users/tom/footwork-u2/footwork/output/fixture-v2/fixture-v2-invoice.txt", + "value": "17.25 USD", + "prefix": "Invoice report\n", + "browser_pid": 24766 +} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/fixture-v2/scope.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/fixture-v2/scope.json new file mode 100644 index 0000000..ea1d375 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/fixture-v2/scope.json @@ -0,0 +1,13 @@ +{ + "task": "fixture-v2", + "source": { + "pid": 24766, + "window_id": 237, + "url": "http://127.0.0.1:49175/" + }, + "destination": { + "pid": 2817, + "window_id": 235, + "document": "/Users/tom/footwork-u2/footwork/output/fixture-v2/fixture-v2-invoice.txt" + } +} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/host.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/host.json new file mode 100644 index 0000000..0796ee9 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/host.json @@ -0,0 +1,24 @@ +{ + "mcpServers": { + "footwork": { + "command": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "env": { + "PYTHONPATH": "", + "PYTHONHOME": "", + "PYTHONNOUSERSITE": "1", + "ANONYMIZED_TELEMETRY": "false", + "BROWSER_USE_CLOUD_SYNC": "false" + }, + "args": [ + "-I", + "-m", + "jevdual.capability.cli", + "server", + "--scope", + "/Users/tom/footwork-u2/footwork/output/job-v2/scope.json", + "--journal", + "/Users/tom/footwork-u2/footwork/output/job-v2/operations.sqlite" + ] + } + } +} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/claimed.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/claimed.json new file mode 100644 index 0000000..574ac7f --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/claimed.json @@ -0,0 +1 @@ +{"nonce":"956396088a8145bdba8a6442d25010b5","operation_id":"op_19f9d53bc4e6","packet_digest":"cfca1696986e68585305a237764bef3804c998ee31d8558c91e1950c35067376","request_digest":"6375e2777f8b4f59302959bb0c1b5c53537b1957c052f105f5e306520c0847a7","version":1} \ No newline at end of file diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/input_started.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/input_started.json new file mode 100644 index 0000000..f225d10 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/input_started.json @@ -0,0 +1 @@ +{"nonce":"956396088a8145bdba8a6442d25010b5","operation_id":"op_19f9d53bc4e6","packet_digest":"cfca1696986e68585305a237764bef3804c998ee31d8558c91e1950c35067376","request_digest":"6375e2777f8b4f59302959bb0c1b5c53537b1957c052f105f5e306520c0847a7","t":1791753864.227186,"version":1} \ No newline at end of file diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/packet.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/packet.json new file mode 100644 index 0000000..7c3aa58 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/packet.json @@ -0,0 +1 @@ +{"args":{"text":"17.25 USD"},"operation_id":"op_19f9d53bc4e6","recovery":{"before":{"length":15,"sha256":"ac11060594d5bcde616f5e88156ea7c0f55e2cca74c2de52c4da375fbde33339"},"expected":{"length":24,"sha256":"783172d2e6ac11f2c5450015856703ebe655b8c2aa86ca81b802ab1d8d210aee"},"field":{"frame":[118.0,89.0,656.0,384.0],"role":"AXTextArea"},"identity":{"boot":"11EBDE2F-6F02-4282-B73B-6DB59AABD9A1","device":16777233,"document":"/Users/tom/footwork-u2/footwork/output/fixture-v2/fixture-v2-invoice.txt","inode":118886,"pid":2817,"start":"0x1.ab2ff26e1fcadp+30"},"nonce":"956396088a8145bdba8a6442d25010b5","route":"textedit-ax-append-v1","scope":{"destination":{"document":"/Users/tom/footwork-u2/footwork/output/fixture-v2/fixture-v2-invoice.txt","pid":2817,"window_id":235},"source":{"pid":24766,"url":"http://127.0.0.1:49175/","window_id":237},"task":"task_a5de7021e73f434e8713e86c651d73f7"},"scope_digest":"4c6a65aaa0f5f9c2dd294e474ceddd63355e05bca6655e088563c33359d95a5f","version":1},"request":{"args":{"text":"17.25 USD"},"move":"append","observation":"obs_0714d47266514cc982e9185127f221b0","resource":"native:2817:235","target":"target_e9ef6b07fc344d6f9f4172ea50d2ff2a","task":"task_a5de7021e73f434e8713e86c651d73f7"},"request_digest":"6375e2777f8b4f59302959bb0c1b5c53537b1957c052f105f5e306520c0847a7","version":1} \ No newline at end of file diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/registration.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/registration.json new file mode 100644 index 0000000..e7555df --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/registration.json @@ -0,0 +1 @@ +{"nonce":"956396088a8145bdba8a6442d25010b5","operation_id":"op_19f9d53bc4e6","packet_digest":"cfca1696986e68585305a237764bef3804c998ee31d8558c91e1950c35067376","pgid":42605,"pid":42605,"request_digest":"6375e2777f8b4f59302959bb0c1b5c53537b1957c052f105f5e306520c0847a7","start":"0x1.ab2ffa1db2863p+30","version":1} \ No newline at end of file diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/result.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/result.json new file mode 100644 index 0000000..aa2128a --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/result.json @@ -0,0 +1 @@ +{"disposition":"applied","error":null,"evidence":{"after":{"length":24,"sha256":"783172d2e6ac11f2c5450015856703ebe655b8c2aa86ca81b802ab1d8d210aee"},"before":{"length":15,"sha256":"ac11060594d5bcde616f5e88156ea7c0f55e2cca74c2de52c4da375fbde33339"},"driver_effect":"confirmed","identity":{"boot":"11EBDE2F-6F02-4282-B73B-6DB59AABD9A1","device":16777233,"document":"/Users/tom/footwork-u2/footwork/output/fixture-v2/fixture-v2-invoice.txt","inode":118886,"pid":2817,"start":"0x1.ab2ff26e1fcadp+30"},"identity_after":{"boot":"11EBDE2F-6F02-4282-B73B-6DB59AABD9A1","device":16777233,"document":"/Users/tom/footwork-u2/footwork/output/fixture-v2/fixture-v2-invoice.txt","inode":118886,"pid":2817,"start":"0x1.ab2ff26e1fcadp+30"},"readback":{"kind":"exact_field_readback","passed":true,"persistence":"not_checked"}},"nonce":"956396088a8145bdba8a6442d25010b5","operation_id":"op_19f9d53bc4e6","packet_digest":"cfca1696986e68585305a237764bef3804c998ee31d8558c91e1950c35067376","phase":"sent","request_digest":"6375e2777f8b4f59302959bb0c1b5c53537b1957c052f105f5e306520c0847a7","version":1} \ No newline at end of file diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/scope.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/scope.json new file mode 100644 index 0000000..5bf0108 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/job-v2/scope.json @@ -0,0 +1,13 @@ +{ + "task": "task_a5de7021e73f434e8713e86c651d73f7", + "source": { + "pid": 24766, + "window_id": 237, + "url": "http://127.0.0.1:49175/" + }, + "destination": { + "pid": 2817, + "window_id": 235, + "document": "/Users/tom/footwork-u2/footwork/output/fixture-v2/fixture-v2-invoice.txt" + } +} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/origin-probe.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/origin-probe.json new file mode 100644 index 0000000..4d62793 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/origin-probe.json @@ -0,0 +1,16 @@ +{ + "artifact_version": "0.0.1", + "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", + "kernel_executable": "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + "kernel_argv0": "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + "package": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", + "worker": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/execution_worker.py", + "backend": "pure-python", + "sys_path": [ + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python314.zip", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/lib-dynload", + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + ] +} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/receipt.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/receipt.json new file mode 100644 index 0000000..868940f --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/receipt.json @@ -0,0 +1,372 @@ +{ + "passed": true, + "source_commit": "33e1afb06710e2d2a57a0f7d1aea7ea0e19a878d", + "origins": { + "artifact_version": "0.0.1", + "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", + "kernel_executable": "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + "kernel_argv0": "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + "package": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", + "worker": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/execution_worker.py", + "backend": "pure-python", + "sys_path": [ + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python314.zip", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/lib-dynload", + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages" + ] + }, + "actual_runtime_contexts": [ + { + "role": "server", + "operation": null, + "pid": 42481, + "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": null, + "mcp": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py" + ], + "ApplicationServices": null, + "Quartz": null, + "Foundation": null + }, + "cua_library": null, + "home": "/Users/tom/footwork-u2/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791753862176581000 + }, + { + "role": "server", + "operation": null, + "pid": 42481, + "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py" + ], + "mcp": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py" + ], + "ApplicationServices": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py" + ], + "Quartz": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py" + ], + "Foundation": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Foundation/__init__.py" + ] + }, + "cua_library": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/libcua_driver_sdk.dylib", + "home": "/Users/tom/footwork-u2/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791753863241269000 + }, + { + "role": "server", + "operation": null, + "pid": 42640, + "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": null, + "mcp": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py" + ], + "ApplicationServices": null, + "Quartz": null, + "Foundation": null + }, + "cua_library": null, + "home": "/Users/tom/footwork-u2/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791753865106768000 + }, + { + "role": "server", + "operation": null, + "pid": 42481, + "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py" + ], + "mcp": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py" + ], + "ApplicationServices": null, + "Quartz": null, + "Foundation": null + }, + "cua_library": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/libcua_driver_sdk.dylib", + "home": "/Users/tom/footwork-u2/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791753862535644000 + }, + { + "role": "worker", + "operation": "op_19f9d53bc4e6", + "pid": 42605, + "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py" + ], + "mcp": null, + "ApplicationServices": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py" + ], + "Quartz": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py" + ], + "Foundation": [ + "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Foundation/__init__.py" + ] + }, + "cua_library": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/libcua_driver_sdk.dylib", + "home": "/Users/tom/footwork-u2/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791753864068183000 + } + ], + "emitted_configuration_checked": true, + "initialized": { + "capabilities": { + "experimental": {}, + "tools": { + "listChanged": false + } + }, + "protocolVersion": "2025-06-18", + "serverInfo": { + "name": "footwork-capability", + "version": "0.0.1" + } + }, + "worker_proofs": [ + { + "registration": { + "nonce": "956396088a8145bdba8a6442d25010b5", + "operation_id": "op_19f9d53bc4e6", + "packet_digest": "cfca1696986e68585305a237764bef3804c998ee31d8558c91e1950c35067376", + "pgid": 42605, + "pid": 42605, + "request_digest": "6375e2777f8b4f59302959bb0c1b5c53537b1957c052f105f5e306520c0847a7", + "start": "0x1.ab2ffa1db2863p+30", + "version": 1 + }, + "cmdline": [ + "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + "-I", + "-m", + "jevdual.capability.execution_worker", + "--packet", + "/Users/tom/footwork-u2/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/packet.json" + ], + "executable": "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python" + } + ], + "action": { + "is_error": false, + "schema_version": "1", + "operation_id": "op_19f9d53bc4e6", + "disposition": "applied", + "state": "final", + "phase": "sent", + "summary": "Historical append confirmed by retained execution evidence; current agreement is separate.", + "repeat": false, + "evidence": { + "execution_receipt": { + "disposition": "applied", + "error": null, + "evidence": { + "after": { + "length": 24, + "sha256": "783172d2e6ac11f2c5450015856703ebe655b8c2aa86ca81b802ab1d8d210aee" + }, + "before": { + "length": 15, + "sha256": "ac11060594d5bcde616f5e88156ea7c0f55e2cca74c2de52c4da375fbde33339" + }, + "driver_effect": "confirmed", + "identity": { + "boot": "11EBDE2F-6F02-4282-B73B-6DB59AABD9A1", + "device": 16777233, + "document": "/Users/tom/footwork-u2/footwork/output/fixture-v2/fixture-v2-invoice.txt", + "inode": 118886, + "pid": 2817, + "start": "0x1.ab2ff26e1fcadp+30" + }, + "identity_after": { + "boot": "11EBDE2F-6F02-4282-B73B-6DB59AABD9A1", + "device": 16777233, + "document": "/Users/tom/footwork-u2/footwork/output/fixture-v2/fixture-v2-invoice.txt", + "inode": 118886, + "pid": 2817, + "start": "0x1.ab2ff26e1fcadp+30" + }, + "readback": { + "kind": "exact_field_readback", + "passed": true, + "persistence": "not_checked" + } + }, + "nonce": "956396088a8145bdba8a6442d25010b5", + "operation_id": "op_19f9d53bc4e6", + "packet_digest": "cfca1696986e68585305a237764bef3804c998ee31d8558c91e1950c35067376", + "phase": "sent", + "request_digest": "6375e2777f8b4f59302959bb0c1b5c53537b1957c052f105f5e306520c0847a7", + "version": 1 + }, + "readback": { + "kind": "exact_field_readback", + "passed": true, + "persistence": "not_checked", + "outcome": "applied", + "historical": true + } + }, + "reporting": { + "historical_effect": "applied", + "disk_persistence": "not_checked", + "guidance": "Disk persistence was not checked. Do not claim the file is saved or unsaved. Report the retained execution result separately from current document agreement." + }, + "client_token": "installed-once" + }, + "repeat": { + "is_error": false, + "schema_version": "1", + "operation_id": "op_19f9d53bc4e6", + "disposition": "applied", + "state": "final", + "phase": "sent", + "summary": "Historical append confirmed by retained execution evidence; current agreement is separate.", + "repeat": true, + "evidence": { + "execution_receipt": { + "disposition": "applied", + "error": null, + "evidence": { + "after": { + "length": 24, + "sha256": "783172d2e6ac11f2c5450015856703ebe655b8c2aa86ca81b802ab1d8d210aee" + }, + "before": { + "length": 15, + "sha256": "ac11060594d5bcde616f5e88156ea7c0f55e2cca74c2de52c4da375fbde33339" + }, + "driver_effect": "confirmed", + "identity": { + "boot": "11EBDE2F-6F02-4282-B73B-6DB59AABD9A1", + "device": 16777233, + "document": "/Users/tom/footwork-u2/footwork/output/fixture-v2/fixture-v2-invoice.txt", + "inode": 118886, + "pid": 2817, + "start": "0x1.ab2ff26e1fcadp+30" + }, + "identity_after": { + "boot": "11EBDE2F-6F02-4282-B73B-6DB59AABD9A1", + "device": 16777233, + "document": "/Users/tom/footwork-u2/footwork/output/fixture-v2/fixture-v2-invoice.txt", + "inode": 118886, + "pid": 2817, + "start": "0x1.ab2ff26e1fcadp+30" + }, + "readback": { + "kind": "exact_field_readback", + "passed": true, + "persistence": "not_checked" + } + }, + "nonce": "956396088a8145bdba8a6442d25010b5", + "operation_id": "op_19f9d53bc4e6", + "packet_digest": "cfca1696986e68585305a237764bef3804c998ee31d8558c91e1950c35067376", + "phase": "sent", + "request_digest": "6375e2777f8b4f59302959bb0c1b5c53537b1957c052f105f5e306520c0847a7", + "version": 1 + }, + "readback": { + "kind": "exact_field_readback", + "passed": true, + "persistence": "not_checked", + "outcome": "applied", + "historical": true + } + }, + "reporting": { + "historical_effect": "applied", + "disk_persistence": "not_checked", + "guidance": "Disk persistence was not checked. Do not claim the file is saved or unsaved. Report the retained execution result separately from current document agreement." + }, + "client_token": "installed-once" + }, + "report": { + "schema_version": "1", + "scope": "all_admitted_task_operations", + "validation": { + "accepted": true, + "reasons": [] + }, + "operation_completion": "completed", + "operations": [ + { + "operation": "op_19f9d53bc4e6", + "state": "final", + "disposition": "applied", + "updated": 1791753864.586184, + "created": 1791753863.419928 + } + ], + "persistence": "not_checked", + "current_state": "not_checked", + "task": "task_a5de7021e73f434e8713e86c651d73f7", + "snapshot_digest": "022a90f5f38fb30054c9a799a0ed885ebd524d702629cb1b0776e0746644c7b4", + "ledger_span": { + "first_created": 1791753863.419928, + "last_updated": 1791753864.586184, + "count": 1 + }, + "accepted_host_claim": "completed", + "as_of": 1791753865.3063118, + "trusted_summary": "1 recorded operations confirmed applied; 0 attempts confirmed no input. These are historical effects, not certification of the host job. Full task ledger: 1 records, 2026-10-11T21:24:23.419928+00:00 through 2026-10-11T21:24:24.586184+00:00. Current document state and disk persistence are not certified by this report." + }, + "oracle_actual": "Invoice report\n17.25 USD", + "owned_worker_finished": true, + "limits": "Existing host OS/system frameworks/permissions. Fresh dependency/private-home qualification." +} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/server-42481-1791753862176581000.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/server-42481-1791753862176581000.json new file mode 100644 index 0000000..4865ad8 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/server-42481-1791753862176581000.json @@ -0,0 +1 @@ +{"role": "server", "operation": null, "pid": 42481, "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", "modules": {"jevdual": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py"], "jevdual.capability.execution_worker": null, "cua_driver": null, "mcp": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py"], "ApplicationServices": null, "Quartz": null, "Foundation": null}, "cua_library": null, "home": "/Users/tom/footwork-u2/footwork/output/install-v1/private-home", "user_site_disabled": true, "browser_telemetry_disabled": true, "browser_cloud_sync_disabled": true, "as_of_ns": 1791753862176581000} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/server-42481-1791753862535644000.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/server-42481-1791753862535644000.json new file mode 100644 index 0000000..12e5068 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/server-42481-1791753862535644000.json @@ -0,0 +1 @@ +{"role": "server", "operation": null, "pid": 42481, "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", "modules": {"jevdual": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py"], "jevdual.capability.execution_worker": null, "cua_driver": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py"], "mcp": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py"], "ApplicationServices": null, "Quartz": null, "Foundation": null}, "cua_library": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/libcua_driver_sdk.dylib", "home": "/Users/tom/footwork-u2/footwork/output/install-v1/private-home", "user_site_disabled": true, "browser_telemetry_disabled": true, "browser_cloud_sync_disabled": true, "as_of_ns": 1791753862535644000} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/server-42481-1791753863241269000.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/server-42481-1791753863241269000.json new file mode 100644 index 0000000..cdd66ca --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/server-42481-1791753863241269000.json @@ -0,0 +1 @@ +{"role": "server", "operation": null, "pid": 42481, "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", "modules": {"jevdual": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py"], "jevdual.capability.execution_worker": null, "cua_driver": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py"], "mcp": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py"], "ApplicationServices": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py"], "Quartz": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py"], "Foundation": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Foundation/__init__.py"]}, "cua_library": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/libcua_driver_sdk.dylib", "home": "/Users/tom/footwork-u2/footwork/output/install-v1/private-home", "user_site_disabled": true, "browser_telemetry_disabled": true, "browser_cloud_sync_disabled": true, "as_of_ns": 1791753863241269000} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/server-42640-1791753865106768000.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/server-42640-1791753865106768000.json new file mode 100644 index 0000000..a7a1ba9 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/server-42640-1791753865106768000.json @@ -0,0 +1 @@ +{"role": "server", "operation": null, "pid": 42640, "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", "modules": {"jevdual": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py"], "jevdual.capability.execution_worker": null, "cua_driver": null, "mcp": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py"], "ApplicationServices": null, "Quartz": null, "Foundation": null}, "cua_library": null, "home": "/Users/tom/footwork-u2/footwork/output/install-v1/private-home", "user_site_disabled": true, "browser_telemetry_disabled": true, "browser_cloud_sync_disabled": true, "as_of_ns": 1791753865106768000} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/worker-42605-1791753864068183000.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/worker-42605-1791753864068183000.json new file mode 100644 index 0000000..0fc594e --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/runtime-context/worker-42605-1791753864068183000.json @@ -0,0 +1 @@ +{"role": "worker", "operation": "op_19f9d53bc4e6", "pid": 42605, "python": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/bin/python", "prefix": "/Users/tom/footwork-u2/footwork/output/install-v1/venv", "modules": {"jevdual": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py"], "jevdual.capability.execution_worker": null, "cua_driver": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py"], "mcp": null, "ApplicationServices": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py"], "Quartz": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py"], "Foundation": ["/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/Foundation/__init__.py"]}, "cua_library": "/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/libcua_driver_sdk.dylib", "home": "/Users/tom/footwork-u2/footwork/output/install-v1/private-home", "user_site_disabled": true, "browser_telemetry_disabled": true, "browser_cloud_sync_disabled": true, "as_of_ns": 1791753864068183000} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/scope.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/scope.json new file mode 100644 index 0000000..c24c2ab --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/scope.json @@ -0,0 +1 @@ +{"task": "task_a5de7021e73f434e8713e86c651d73f7", "source": {"pid": 24766, "window_id": 237, "url": "http://127.0.0.1:49175/"}, "destination": {"pid": 2817, "window_id": 235, "document": "/Users/tom/footwork-u2/footwork/output/fixture-v2/fixture-v2-invoice.txt"}} \ No newline at end of file diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/worker-observations.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/worker-observations.json new file mode 100644 index 0000000..2489199 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/footwork/output/native-v2/worker-observations.json @@ -0,0 +1,23 @@ +[ + { + "registration": { + "nonce": "956396088a8145bdba8a6442d25010b5", + "operation_id": "op_19f9d53bc4e6", + "packet_digest": "cfca1696986e68585305a237764bef3804c998ee31d8558c91e1950c35067376", + "pgid": 42605, + "pid": 42605, + "request_digest": "6375e2777f8b4f59302959bb0c1b5c53537b1957c052f105f5e306520c0847a7", + "start": "0x1.ab2ffa1db2863p+30", + "version": 1 + }, + "cmdline": [ + "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + "-I", + "-m", + "jevdual.capability.execution_worker", + "--packet", + "/Users/tom/footwork-u2/footwork/output/job-v2/operations.sqlite.executions/op_19f9d53bc4e6/packet.json" + ], + "executable": "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python" + } +] diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/native-v2-console.txt b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/native-v2-console.txt new file mode 100644 index 0000000..c09d1d2 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/native-v2-console.txt @@ -0,0 +1,2 @@ +jevdual._core unavailable (cannot import name '_core' from partially initialized module 'jevdual' (most likely due to a circular import) (/Users/tom/footwork-u2/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py)); falling back to pure Python +{"passed": true, "backend": "pure-python", "installed_worker_observed": true} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/native-v2-exit.txt b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/native-v2-exit.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/guest-evidence/native-v2-exit.txt @@ -0,0 +1 @@ +0 diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/journal-readback.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/journal-readback.json new file mode 100644 index 0000000..759cd8e --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/journal-readback.json @@ -0,0 +1,16 @@ +{ + "read_only": true, + "count": 1, + "operations": [ + { + "id": "op_19f9d53bc4e6", + "task": "task_a5de7021e73f434e8713e86c651d73f7", + "client_token": "installed-once", + "state": "final", + "disposition": "applied", + "phase": "sent", + "created": 1791753863.419928, + "updated": 1791753864.586184 + } + ] +} diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/report.md b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/report.md new file mode 100644 index 0000000..1f87033 --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/report.md @@ -0,0 +1,27 @@ +# U2 development continuation: installed public path passes + +After ordinary Chrome first-launch verification completed, the unchanged frozen packet identified the fresh fixture-v2 windows. The generated host configuration and installed native trial both exited0. This is a post-grant development continuation, preregistered as UO5; the original fixture-v1 failure is retained separately and never regraded. + +The AX oracle read exactly `Invoice report\n17.25 USD`. The installed MCP server admitted one append; after server restart the same token returned the same operation with repeat=true. A read-only inspection of the exported SQLite journal found exactly one final/applied/sent row. The structured final report was accepted against that full task ledger, with persistence and current-state certification explicitly not_checked. See guest-evidence/footwork/output/native-v2/receipt.json and journal-readback.json. + +Actual server and detached-worker contexts show installed venv module/dylib origins, private HOME, disabled user site and telemetry. They ran with -I outside the packet source, with hostile cwd shadow packages. The receipt identifies frozen source33e1afb and the reviewed pure-Python wheel; no helper or product bytes changed during the continuation. + +Cleanup stopped exact fixture PID24765 through its SIGINT handler. Fixture PID24765, browser24766 and worker42605 were absent, and the loopback listener refused connection. Automation had been declined, so the fixture could not confirm its TextEdit close. The operator visually verified the exact fixture-v2 title and one appended value, then closed that document using native CUA. Save/discard and disk persistence were not checked. See guest-evidence/cleanup-v2.json. No other app/process was targeted. + +The raw28member archive was exported only after execution and cleanup through a bridge-only, fixed-source-IP/path/size/hash receiver. Guest and host SHA256 match; the receiver exited after the upload. The raw archive and journal remain under output/u2-export-v2; sanitized JSON/text evidence is retained here. + +Claude independently verified the archive, exits, oracle, replay identity, runtime contexts, journal row and cleanup receipt (mailbox1791754065071525000). Verdict: valid post-grant development pass, no new runtime blockers. + +## Limits and first-use decision + +This was a fresh SIP-enabled guest OS and fresh dependency install. An Accessibility-denied baseline was observed. Terminal was already enabled when Settings was reopened; the grant actor and time were not captured. Root restarted Terminal and observed the strict installed doctor pass. Screen Recording remained denied and was unnecessary for this AX-based trial. Python Local Network discovery and Terminal-to-TextEdit Automation were declined. Document cleanup required the operator. + +Full first-use U2 remains unqualified: the original first fixture timed out, the required grant transition was not captured, and automatic document cleanup did not pass. The receipt's generic U1 limits string must not be read as a full U2 qualification claim. A later qualification needs a separately approved, captured denied-to-granted baseline; this run performed no TCC reset. + +## Reference-backed next packet work + +1. Replace cleanup-only Apple Events with exact-document AX close and freshly observed save-sheet handling. Arc CUA6b353039111c08c2c64c5229083606941e364eaf demonstrates AXDocument/AXCloseButton/AXPress in benchmarks/workflows.py:533. Its substring match is insufficient for Footwork: retain exact normalized document-path and unique-window checks, and confirm disappearance before reporting cleanup. +2. Separate bounded browser-launch readiness from the unchanged identity-resolution deadline; record normal vendor first launch as a prerequisite and use typed failure receipts. Chrome DevTools MCPf08dbe152502d66e75fa07fb2588dc0feb42bc20 separates abandoned launch attempts and owned-browser cleanup in src/BrowserManager.ts:132 and:438. Do not extend identity authority merely because launch is slow. +3. Test disabling MediaRouter and DialMediaRouteProvider in the owned fixture to reduce unnecessary local discovery. Browser-usec75e8476e26d18b7617643bc2ae082fae8eae431 disables both in browser_use/browser/profile.py:58 and:70. Attribution to child Chrome discovery is still a hypothesis; this continuation does not establish it or qualify changed flags. + +These belong in a new development packet and tests, not changes to the frozen v3 measurement. Claude agreed with this scope in1791753660836044000 and1791754065071525000. diff --git a/jevdual/results/u2-independent-onboarding-20261011-v2-dev/summary.json b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/summary.json new file mode 100644 index 0000000..f8b89ac --- /dev/null +++ b/jevdual/results/u2-independent-onboarding-20261011-v2-dev/summary.json @@ -0,0 +1,39 @@ +{ + "status": "reviewed_post_grant_development_pass", + "full_first_use_qualified": false, + "recorded_at": "2026-10-11T17:28:00-04:00", + "source_commit": "33e1afb06710e2d2a57a0f7d1aea7ea0e19a878d", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7", + "config_exit": 0, + "native_exit": 0, + "operation_count": 1, + "same_token_restart_same_operation": true, + "native_oracle_passed": true, + "installed_worker_observed": true, + "structured_report_accepted": true, + "disk_persistence": "not_checked", + "cleanup": { + "owned_processes_absent": true, + "fixture_loopback_closed": true, + "document_close": "operator via native CUA", + "fixture_automatic_document_cleanup": "unconfirmed", + "automation_grant": "declined" + }, + "permissions": { + "initial_denial_observed": true, + "grant_actor_and_time": "not_observed", + "accessibility_after_restart": "granted", + "screen_recording": "not_granted" + }, + "archive": { + "path": "output/u2-export-v2/evidence-v2.zip", + "bytes": 18394, + "members": 28, + "sha256": "b7e6672e59e8aa5aaaab5c8722f62e577a99236cb5e2b29af67d28c18ddf6add", + "receiver_closed": true + }, + "claude_review": { + "message": "1791754065071525000", + "verdict": "valid post-grant development pass; no new blockers; full first-use remains unqualified" + } +} diff --git a/jevdual/results/u2-vm-provision-20261011/report.md b/jevdual/results/u2-vm-provision-20261011/report.md new file mode 100644 index 0000000..1b67bc4 --- /dev/null +++ b/jevdual/results/u2-vm-provision-20261011/report.md @@ -0,0 +1,31 @@ +# U2 disposable Mac provisioning handoff + +Recorded 2026-10-11 19:20 UTC. The fresh disposable guest `footwork-u2-20261011` was installed from an authenticated Apple restore image and is running at the human Setup Assistant boundary. This closes provisioning only. The independent permission journey remains unexecuted. + +Root bound the native Tart app to exact PID `25080` and window `footwork-u2-20261011`, observed the fresh welcome page, then the Language page with English selected and Apple's license-use notice. Evidence is the native CUA tool images in this chat; no screenshot file path is invented. Root has requested human license approval. The worker performed no guest GUI input, account setup, SSH enablement, TCC grant or SIP change. Root owns further GUI actions. + +Tart's runtime configuration reports 4 CPUs, 8192 MB memory, a 64 GB logical disk and 1280x800px display. Actual launch arguments disable clipboard, audio and USB passthrough, capture system keys, and attach only the validated asset ISO read-only. There are no host directory shares; a scoped check found no TCP listener on the Tart PID. Guest SIP and initial Accessibility status have not been measured. + +## Provenance and launcher choice + +Local Cua reference commit: `5458c77b04a6e5e7a099929e0c4d4366e83dd403`. Lume 0.6.1 release source: `3350f504014c85416009a90fcb339fe556ce02fc`. The notarized Lume asset passed official digest/signature checks, but release source confirms automatic host clipboard reads in native mode and its CLI supplies no VNC bind-address control. No Lume guest was created. These source findings were also checked against the exact release, not just the local checkout. + +Tart 2.40.1 source: `05edeac562bae804ea4f88592cfbc98cc6791b5d`. The [official asset](https://github.com/openai/tart/releases/tag/2.40.1) matches SHA256 `363e2701154a8155cbc1bb6d845430c9b42697d2a186bc49574471ca2877db46`. Strict/deep code-signature verification passed for Cirrus Labs, Inc., Team `9M2P8L4D89`. **Gatekeeper diagnostics rejected both bundle and inner CLI as Unnotarized Developer ID; no ticket is stapled.** Normal CLI, create and run nevertheless succeeded under unchanged OS policy. No quarantine/security override was performed. The launcher is distributed under FSL-1.1-ALv2. It is local qualification infrastructure and is not bundled in the MIT Footwork packet. + +Launcher, cache, temporary state, images and guest storage reside under `/Volumes/External SSD/footwork-u2-20261011`. No global installer or LaunchAgent was used. `TART_HOME` isolates runtime storage, and inherited trace export is disabled for this invocation. + +## Image and delivery integrity + +Apple Virtualization supplied macOS 26.6.2/build 25G83's restore URL. The downloaded image contains exactly 19,772,231,540 bytes and matches Apple's advertised SHA256 `885503b7f4b06609e9a512f2befd40f59730640a3f1233e3892d60affdd51c95`. Original parallel transfer failed with curl exit 18; its context remains. Five exact HTTP206 continuations recovered only missing bytes after Content-Range validation. Streamed assembly combined writes with hashing and fsync; the resulting file is 0444 with recorded device/inode/size/mtime and a private non-symlink parent before create. + +The qualification ISO SHA256 is `89f082fcdc943449f1994d70cd4bd30b08b88ecf6abceec7d42c33c206049f6d`. A standard-library Joliet parser independently verified its exact six-member inventory and every payload byte against the staged files. It contains only packet v3, Python 3.14.7 installer, uv 0.12.5 archive, Google Chrome DMG and two provenance/checksum files. It was built without mounting a host volume. **Guest volume/read-only/hash readback remains pending.** + +Packet v3 SHA256: `769c08c89061731368db33d78b180e7c8c96d531190f43bee44263642a76fd0f`. Python and uv match official hashes; Python's installer is signed/notarized by the Python Software Foundation. Chrome DMG checksum passes, but host read-only attach was denied; its inner app signature/version must still be checked in the guest. No prerequisite was installed on the host. + +## Evidence and remaining gates + +Structured values and source/runtime references are in `summary.json`. Ignored raw evidence remains in the sibling output directory: launcher/release provenance, original and resumed transfer headers/logs, image proof, ISO proof, installer log, final configuration/list, process state and runtime log. Do not commit raw logs or downloaded binaries. + +VM1 is inconclusive because Lume creation was never run. VM2 is held for provisioning to actual visible Setup Assistant only. VM4 is held for authenticated range recovery. VM5 is inconclusive: installation finished normally before the planned controller pause, so that extension was not executed. VM3 remains open; UO2 remains unexecuted. + +Next: the human completes the license/account boundary without sharing credentials. From interactive guest Terminal, root verifies OS/SIP and the attached volume/hashes, installs the reviewed prerequisites and wheel, then records the denied Accessibility baseline before any grant. The explicit grant/restart/readback transition and installed append/restart/report/cleanup oracle follow. The denial probe requires installed dependencies; installation does not grant Accessibility. Existing host grants, successful installation and a visible setup page do not qualify that journey. diff --git a/jevdual/results/u2-vm-provision-20261011/summary.json b/jevdual/results/u2-vm-provision-20261011/summary.json new file mode 100644 index 0000000..5720ebb --- /dev/null +++ b/jevdual/results/u2-vm-provision-20261011/summary.json @@ -0,0 +1,111 @@ +{ + "status": "provisioned_running_at_human_setup_gate", + "recorded_utc": "2026-10-11T19:20:22.512174+00:00", + "vm_name": "footwork-u2-20261011", + "runtime_pid": 25080, + "bundle_id": "com.github.cirruslabs.tart", + "window_title": "footwork-u2-20261011", + "runtime_configuration": { + "Size": "22.720", + "Memory": 8192, + "Running": true, + "State": "running", + "Disk": 64, + "DiskFormat": "raw", + "OS": "darwin", + "Display": "1280x800px", + "CPU": 4 + }, + "source_refs": { + "local_cua_commit": "5458c77b04a6e5e7a099929e0c4d4366e83dd403", + "lume_release_commit": "3350f504014c85416009a90fcb339fe556ce02fc", + "tart_version": "2.40.1", + "tart_commit": "05edeac562bae804ea4f88592cfbc98cc6791b5d" + }, + "artifacts": { + "apple_ipsw": { + "version": "26.6.2", + "build": "25G83", + "bytes": 19772231540, + "url": "https://updates.cdn-apple.com/2026SummerFCS/fullrestores/140-75212/A2A24B94-1FC1-45A3-93F7-C51B02AF1F4D/UniversalMac_26.6.2_25G83_Restore.ipsw", + "sha256": "885503b7f4b06609e9a512f2befd40f59730640a3f1233e3892d60affdd51c95", + "sha256_matches_apple_metadata": true, + "fsynced": true, + "read_only_mode": "0444", + "file_identity": { + "device": 16777240, + "inode": 39044176, + "bytes": 19772231540, + "mtime_ns": 1791744046377973440, + "mode": 33060 + } + }, + "tart_archive_sha256": "363e2701154a8155cbc1bb6d845430c9b42697d2a186bc49574471ca2877db46", + "packet_v3_sha256": "769c08c89061731368db33d78b180e7c8c96d531190f43bee44263642a76fd0f", + "iso_sha256": "89f082fcdc943449f1994d70cd4bd30b08b88ecf6abceec7d42c33c206049f6d" + }, + "launcher_integrity": { + "codesign_strict_deep": "valid, Cirrus Labs Inc Team9M2P8L4D89", + "spctl_bundle_and_inner_cli": "rejected, Unnotarized Developer ID", + "stapled_ticket": false, + "normal_cli_and_vm_create_run": "succeeded under unchanged OS policy", + "security_policy_or_quarantine_override": false + }, + "isolation": { + "flags": [ + "--no-clipboard", + "--no-audio", + "--no-usb-accessories", + "--capture-system-keys", + "--disk", + "/Volumes/External SSD/footwork-u2-20261011/qualification-assets.iso:ro" + ], + "host_folder_shares": [], + "no_global_launcher_install": true, + "no_host_tcc_change": true, + "no_guest_accounts_created": true, + "no_guest_ssh_enabled_by_workflow": true, + "no_guest_permission_grants": true, + "no_sip_change": true, + "tart_pid_tcp_listeners_observed": [] + }, + "gui_observation": { + "observer": "root agent through native CUA tool", + "evidence": "observed tool images in this chat; no screenshot file was generated by documented APIs", + "reported_stage": "fresh welcome then Setup Assistant Language page, English selected, Apple license-use notice", + "reported_time_approx_edt": "2026-10-11 15:16", + "license_approval": "requested from human, pending", + "worker_gui_actions": [] + }, + "qualification": { + "VM1": "inconclusive; Lume never created a guest", + "VM2": "held for provisioning to visible Setup Assistant only", + "VM3": "open; ISO source inventory and payload hashes verified, guest read-only volume/hash readback pending", + "VM4": "held; exact HTTP206 continuation and final Apple SHA passed", + "VM5": "inconclusive; timeout extension was not executed", + "UO2": "unexecuted; independent permission journey remains unqualified", + "guest_sip_runtime_status": "not verified", + "guest_accessibility_baseline": "not measured" + }, + "evidence_refs": [ + "launcher-provenance.json", + "tart-launcher-provenance.json", + "tart-tag-ref.json", + "prerequisite-provenance.json", + "iso-verification.json", + "apple-ipsw-provenance.json", + "tart-create.log", + "tart-set.log", + "tart-guest-config.json", + "tart-guest-list.json", + "provision-state.json", + "tart-guest-run.log", + "installer-liveness-current.json" + ], + "next": [ + "Human approves the actual Apple license step and enters a new local-account password personally.", + "From interactive guest Terminal, verify OS/SIP and ISO read-only volume/hashes; verify and install prerequisites, then cold-install the approved wheel.", + "Record strict doctor and fixture denial before any Accessibility grant; verify no fixture resources were created.", + "Human grants the exact responsible host; record restart/readback, then run installed append/restart/report and independent cleanup checks." + ] +} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v1/cleanup.json b/jevdual/results/u3-packet-v4-permission-20261012-v1/cleanup.json new file mode 100644 index 0000000..e46a799 --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v1/cleanup.json @@ -0,0 +1,25 @@ +{ + "document": { + "status": "error", + "reason": "ax_attribute_refused:AXRole:-25202", + "pid": 2817, + "file_before": { + "status": "observed", + "size_bytes": 15, + "sha256": "ac11060594d5bcde616f5e88156ea7c0f55e2cca74c2de52c4da375fbde33339" + }, + "file_after": { + "status": "observed", + "size_bytes": 24, + "sha256": "783172d2e6ac11f2c5450015856703ebe655b8c2aa86ca81b802ab1d8d210aee" + } + }, + "browser": { + "status": "confirmed", + "pid": 52622, + "returncode": 0 + }, + "listener": { + "status": "confirmed" + } +} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v1/doctor-after.json b/jevdual/results/u3-packet-v4-permission-20261012-v1/doctor-after.json new file mode 100644 index 0000000..2a4cedd --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v1/doctor-after.json @@ -0,0 +1 @@ +{"platform": "darwin", "supported": true, "runtime": {"python_version": "3.14.7", "python_requirement": ">=3.11,<4.0", "python_compatible": true, "interpreter": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", "architecture": "arm64", "distribution": {"version": "0.0.1", "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "loaded_modules": {"jevdual": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", "jevdual.capability.setup": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/setup.py", "ApplicationServices": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py", "Quartz": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py"}}, "dependencies": {"browser-use": {"version": "0.13.10", "requirement": "==0.13.10", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "typesafe-sdk": {"version": "0.7.4", "requirement": "<1,>=0.7", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "orjson": {"version": "3.13.0", "requirement": ">=3.10", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "psutil": {"version": "7.2.2", "requirement": "==7.2.2", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "mcp": {"version": "2.1.1", "requirement": "==2.1.1", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "packaging": {"version": "26.3", "requirement": "<27,>=25", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "cua-driver": {"version": "0.28.2", "requirement": "==0.28.2", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-applicationservices": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-quartz": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-cocoa": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}}, "permissions": {"accessibility": "granted", "screen_recording": "not_granted"}, "scope": "not_checked", "journal": "not_checked", "warnings": ["Screen Recording is not qualified. Routes that capture pixels may require it."], "dependency_metadata": "installed distribution", "limitation": "macOS only: one loopback HTTP browser fixture and one identified TextEdit document. Window identity is checked when binding.", "provider_account_required": false, "rust_toolchain_required": false, "blockers": [], "prerequisite_status": "met", "qualification": "Prerequisites only. Window identity, current state, effects and job completion require separate checks."} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v1/doctor-before.json b/jevdual/results/u3-packet-v4-permission-20261012-v1/doctor-before.json new file mode 100644 index 0000000..d6dc989 --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v1/doctor-before.json @@ -0,0 +1 @@ +{"platform": "darwin", "supported": true, "runtime": {"python_version": "3.14.7", "python_requirement": ">=3.11,<4.0", "python_compatible": true, "interpreter": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", "architecture": "arm64", "distribution": {"version": "0.0.1", "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "loaded_modules": {"jevdual": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", "jevdual.capability.setup": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/setup.py", "ApplicationServices": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py", "Quartz": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py"}}, "dependencies": {"browser-use": {"version": "0.13.10", "requirement": "==0.13.10", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "typesafe-sdk": {"version": "0.7.4", "requirement": "<1,>=0.7", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "orjson": {"version": "3.13.0", "requirement": ">=3.10", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "psutil": {"version": "7.2.2", "requirement": "==7.2.2", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "mcp": {"version": "2.1.1", "requirement": "==2.1.1", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "packaging": {"version": "26.3", "requirement": "<27,>=25", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "cua-driver": {"version": "0.28.2", "requirement": "==0.28.2", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-applicationservices": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-quartz": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-cocoa": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages"}}, "permissions": {"accessibility": "not_granted", "screen_recording": "not_granted"}, "scope": "not_checked", "journal": "not_checked", "warnings": ["Screen Recording is not qualified. Routes that capture pixels may require it."], "dependency_metadata": "installed distribution", "limitation": "macOS only: one loopback HTTP browser fixture and one identified TextEdit document. Window identity is checked when binding.", "provider_account_required": false, "rust_toolchain_required": false, "blockers": ["accessibility_not_granted"], "prerequisite_status": "blocked", "qualification": "Prerequisites only. Window identity, current state, effects and job completion require separate checks."} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v1/install-receipt.json b/jevdual/results/u3-packet-v4-permission-20261012-v1/install-receipt.json new file mode 100644 index 0000000..37ed9b0 --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v1/install-receipt.json @@ -0,0 +1,1521 @@ +{ + "source_commit": "21599c641e4c54cad137b3c49e11142c5c14e596", + "host": { + "platform": "Darwin", + "machine": "arm64" + }, + "interpreter_requested": "/Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14", + "wheel": "/Users/tom/footwork-u4/footwork/artifacts/jevdual-0.0.1-py3-none-any.whl", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7", + "cache_initially_empty": true, + "system_site_packages": false, + "borrowed_dependencies": false, + "registry": "https://pypi.org/simple", + "binary_only": true, + "commands": [ + { + "stage": "venv", + "argv": [ + "/Users/tom/footwork-u2/tools/uv-aarch64-apple-darwin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/tom/footwork-u4/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "venv", + "--python", + "/Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14", + "/Users/tom/footwork-u4/footwork/output/install-v1/venv" + ], + "returncode": 0 + }, + { + "stage": "install", + "argv": [ + "/Users/tom/footwork-u2/tools/uv-aarch64-apple-darwin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/tom/footwork-u4/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "pip", + "install", + "--python", + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", + "--default-index", + "https://pypi.org/simple", + "--only-binary", + ":all:", + "/Users/tom/footwork-u4/footwork/artifacts/jevdual-0.0.1-py3-none-any.whl" + ], + "returncode": 0 + }, + { + "stage": "consistency", + "argv": [ + "/Users/tom/footwork-u2/tools/uv-aarch64-apple-darwin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/tom/footwork-u4/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "pip", + "check", + "--python", + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python" + ], + "returncode": 0 + }, + { + "stage": "inventory", + "argv": [ + "/Users/tom/footwork-u2/tools/uv-aarch64-apple-darwin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/tom/footwork-u4/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "pip", + "list", + "--python", + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", + "--format", + "json" + ], + "returncode": 0 + }, + { + "stage": "origins", + "argv": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", + "-I", + "-c", + "\nimport sys,json,importlib.metadata,importlib\nfrom pathlib import Path\nimport jevdual,jevdual.capability.execution_worker as worker\nfrom jevdual._native import BACKEND\nnames=(\"cua_driver\",\"psutil\",\"mcp\",\"ApplicationServices\",\"Quartz\",\"Foundation\",\"packaging\")\nmodules={n:str(importlib.import_module(n).__file__) for n in names}\nprint(json.dumps({\"version\":sys.version,\"prefix\":sys.prefix,\"base_prefix\":sys.base_prefix,\"python\":sys.executable,\"package\":jevdual.__file__,\"worker\":worker.__file__,\"backend\":BACKEND,\"module_origins\":modules,\"sys_path\":sys.path}))\n" + ], + "returncode": 0 + }, + { + "stage": "doctor", + "argv": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/footwork-capability", + "doctor" + ], + "returncode": 0 + }, + { + "stage": "wheel-tags", + "argv": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", + "-I", + "-c", + "import json,importlib.metadata;print(json.dumps([{\"name\":d.metadata[\"Name\"],\"version\":d.version,\"tags\":[l[5:] for l in (d.read_text(\"WHEEL\") or \"\").splitlines() if l.startswith(\"Tag: \")]} for d in importlib.metadata.distributions()]))" + ], + "returncode": 0 + } + ], + "passed": true, + "limits": "Fresh dependencies on existing host OS/interpreter, not a fresh machine or TCC permission database.", + "origins": { + "version": "3.14.7 (v3.14.7:823f0323ee6, Aug 5 2026, 07:07:01) [Clang 21.0.0 (clang-2100.1.1.101)]", + "prefix": "/Users/tom/footwork-u4/footwork/output/install-v1/venv", + "base_prefix": "/Library/Frameworks/Python.framework/Versions/3.14", + "python": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", + "package": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", + "worker": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/execution_worker.py", + "backend": "pure-python", + "module_origins": { + "cua_driver": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py", + "psutil": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/psutil/__init__.py", + "mcp": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py", + "ApplicationServices": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py", + "Quartz": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py", + "Foundation": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/Foundation/__init__.py", + "packaging": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/packaging/__init__.py" + }, + "sys_path": [ + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python314.zip", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/lib-dynload", + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + ] + }, + "doctor": { + "platform": "darwin", + "supported": true, + "runtime": { + "python_version": "3.14.7", + "python_requirement": ">=3.11,<4.0", + "python_compatible": true, + "interpreter": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", + "architecture": "arm64", + "distribution": { + "version": "0.0.1", + "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "loaded_modules": { + "jevdual": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", + "jevdual.capability.setup": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/setup.py", + "ApplicationServices": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py", + "Quartz": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py" + } + }, + "dependencies": { + "browser-use": { + "version": "0.13.10", + "requirement": "==0.13.10", + "compatible": true, + "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "typesafe-sdk": { + "version": "0.7.4", + "requirement": "<1,>=0.7", + "compatible": true, + "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "orjson": { + "version": "3.13.0", + "requirement": ">=3.10", + "compatible": true, + "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "psutil": { + "version": "7.2.2", + "requirement": "==7.2.2", + "compatible": true, + "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "mcp": { + "version": "2.1.1", + "requirement": "==2.1.1", + "compatible": true, + "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "packaging": { + "version": "26.3", + "requirement": "<27,>=25", + "compatible": true, + "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "cua-driver": { + "version": "0.28.2", + "requirement": "==0.28.2", + "compatible": true, + "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "pyobjc-framework-applicationservices": { + "version": "12.2.2", + "requirement": ">=12", + "compatible": true, + "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "pyobjc-framework-quartz": { + "version": "12.2.2", + "requirement": ">=12", + "compatible": true, + "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "pyobjc-framework-cocoa": { + "version": "12.2.2", + "requirement": ">=12", + "compatible": true, + "location": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + } + }, + "permissions": { + "accessibility": "granted", + "screen_recording": "not_granted" + }, + "scope": "not_checked", + "journal": "not_checked", + "warnings": [ + "Screen Recording is not qualified. Routes that capture pixels may require it." + ], + "dependency_metadata": "installed distribution", + "limitation": "macOS only: one loopback HTTP browser fixture and one identified TextEdit document. Window identity is checked when binding.", + "provider_account_required": false, + "rust_toolchain_required": false, + "blockers": [], + "prerequisite_status": "met", + "qualification": "Prerequisites only. Window identity, current state, effects and job completion require separate checks." + }, + "inventory": [ + { + "name": "aiofiles", + "version": "25.1.0" + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1" + }, + { + "name": "aiohttp", + "version": "3.14.3" + }, + { + "name": "aiosignal", + "version": "1.4.0" + }, + { + "name": "annotated-types", + "version": "0.8.0" + }, + { + "name": "anthropic", + "version": "0.76.0" + }, + { + "name": "anyio", + "version": "4.12.1" + }, + { + "name": "attrs", + "version": "26.1.0" + }, + { + "name": "backoff", + "version": "2.2.1" + }, + { + "name": "beautifulsoup4", + "version": "4.15.0" + }, + { + "name": "browser-harness", + "version": "0.1.13" + }, + { + "name": "browser-use", + "version": "0.13.10" + }, + { + "name": "browser-use-sdk", + "version": "3.4.2" + }, + { + "name": "bubus", + "version": "1.5.6" + }, + { + "name": "cdp-use", + "version": "1.4.5" + }, + { + "name": "certifi", + "version": "2026.7.22" + }, + { + "name": "cffi", + "version": "2.1.1" + }, + { + "name": "charset-normalizer", + "version": "3.5.2" + }, + { + "name": "click", + "version": "8.3.3" + }, + { + "name": "cloudpickle", + "version": "3.1.2" + }, + { + "name": "cryptography", + "version": "50.0.2" + }, + { + "name": "cua-driver", + "version": "0.28.2" + }, + { + "name": "cython", + "version": "3.3.0" + }, + { + "name": "distro", + "version": "1.9.0" + }, + { + "name": "docstring-parser", + "version": "0.18.0" + }, + { + "name": "fetch-use", + "version": "0.4.0" + }, + { + "name": "frozenlist", + "version": "1.8.0" + }, + { + "name": "google-api-core", + "version": "2.29.0" + }, + { + "name": "google-api-python-client", + "version": "2.188.0" + }, + { + "name": "google-auth", + "version": "2.48.0" + }, + { + "name": "google-auth-httplib2", + "version": "0.4.4" + }, + { + "name": "google-auth-oauthlib", + "version": "1.2.4" + }, + { + "name": "google-genai", + "version": "1.65.0" + }, + { + "name": "googleapis-common-protos", + "version": "1.75.5" + }, + { + "name": "groq", + "version": "1.0.0" + }, + { + "name": "h11", + "version": "0.16.0" + }, + { + "name": "httpcore", + "version": "1.0.9" + }, + { + "name": "httpcore2", + "version": "2.13.1" + }, + { + "name": "httplib2", + "version": "0.32.0" + }, + { + "name": "httpx", + "version": "0.28.1" + }, + { + "name": "httpx2", + "version": "2.13.1" + }, + { + "name": "idna", + "version": "3.20" + }, + { + "name": "inquirerpy", + "version": "0.3.4" + }, + { + "name": "jevdual", + "version": "0.0.1" + }, + { + "name": "jiter", + "version": "0.17.0" + }, + { + "name": "jsonschema", + "version": "4.26.0" + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1" + }, + { + "name": "lxml", + "version": "6.1.3" + }, + { + "name": "markdown-it-py", + "version": "4.2.0" + }, + { + "name": "markdownify", + "version": "1.2.2" + }, + { + "name": "mcp", + "version": "2.1.1" + }, + { + "name": "mcp-types", + "version": "2.1.1" + }, + { + "name": "mdurl", + "version": "0.1.2" + }, + { + "name": "multidict", + "version": "6.9.1" + }, + { + "name": "oauthlib", + "version": "4.0.0" + }, + { + "name": "ollama", + "version": "0.6.1" + }, + { + "name": "openai", + "version": "2.26.0" + }, + { + "name": "opentelemetry-api", + "version": "1.45.1" + }, + { + "name": "orjson", + "version": "3.13.0" + }, + { + "name": "packaging", + "version": "26.3" + }, + { + "name": "pfzy", + "version": "0.3.4" + }, + { + "name": "pillow", + "version": "12.3.0" + }, + { + "name": "portalocker", + "version": "4.4.0" + }, + { + "name": "posthog", + "version": "7.7.0" + }, + { + "name": "prompt-toolkit", + "version": "3.0.53" + }, + { + "name": "propcache", + "version": "0.5.4" + }, + { + "name": "proto-plus", + "version": "1.29.0" + }, + { + "name": "protobuf", + "version": "6.33.6" + }, + { + "name": "psutil", + "version": "7.2.2" + }, + { + "name": "pyasn1", + "version": "0.6.4" + }, + { + "name": "pyasn1-modules", + "version": "0.4.2" + }, + { + "name": "pycparser", + "version": "3.11" + }, + { + "name": "pydantic", + "version": "2.13.5" + }, + { + "name": "pydantic-core", + "version": "2.46.5" + }, + { + "name": "pydantic-settings", + "version": "2.15.0" + }, + { + "name": "pygments", + "version": "2.21.0" + }, + { + "name": "pyjwt", + "version": "2.15.1" + }, + { + "name": "pyobjc-core", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-applicationservices", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-cocoa", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-coretext", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-quartz", + "version": "12.2.2" + }, + { + "name": "pyotp", + "version": "2.9.0" + }, + { + "name": "pyparsing", + "version": "3.3.3" + }, + { + "name": "pypdf", + "version": "6.16.2" + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0" + }, + { + "name": "python-docx", + "version": "1.2.0" + }, + { + "name": "python-dotenv", + "version": "1.2.2" + }, + { + "name": "python-multipart", + "version": "0.0.32" + }, + { + "name": "referencing", + "version": "0.37.0" + }, + { + "name": "reportlab", + "version": "4.4.9" + }, + { + "name": "requests", + "version": "2.33.0" + }, + { + "name": "requests-oauthlib", + "version": "2.0.0" + }, + { + "name": "rich", + "version": "14.3.3" + }, + { + "name": "rpds-py", + "version": "2026.9.1" + }, + { + "name": "rsa", + "version": "4.9.1" + }, + { + "name": "screeninfo", + "version": "0.8.1" + }, + { + "name": "six", + "version": "1.17.0" + }, + { + "name": "sniffio", + "version": "1.3.1" + }, + { + "name": "soupsieve", + "version": "3.0.3" + }, + { + "name": "sse-starlette", + "version": "3.5.0" + }, + { + "name": "starlette", + "version": "1.7.0" + }, + { + "name": "tenacity", + "version": "9.1.4" + }, + { + "name": "tqdm", + "version": "4.70.1" + }, + { + "name": "truststore", + "version": "0.10.4" + }, + { + "name": "typesafe-sdk", + "version": "0.7.4" + }, + { + "name": "typing-extensions", + "version": "4.15.0" + }, + { + "name": "typing-inspection", + "version": "0.4.4" + }, + { + "name": "uritemplate", + "version": "4.2.0" + }, + { + "name": "urllib3", + "version": "2.8.0" + }, + { + "name": "uuid7", + "version": "0.1.0" + }, + { + "name": "uvicorn", + "version": "0.54.0" + }, + { + "name": "wcwidth", + "version": "0.9.2" + }, + { + "name": "websockets", + "version": "15.0.1" + }, + { + "name": "yarl", + "version": "1.25.1" + } + ], + "wheel_tags": [ + { + "name": "anyio", + "version": "4.12.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "uvicorn", + "version": "0.54.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "groq", + "version": "1.0.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "oauthlib", + "version": "4.0.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "cloudpickle", + "version": "3.1.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-framework-CoreText", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "portalocker", + "version": "4.4.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "lxml", + "version": "6.1.3", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "packaging", + "version": "26.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "Pygments", + "version": "2.21.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "inquirerpy", + "version": "0.3.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "browser-harness", + "version": "0.1.13", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "prompt_toolkit", + "version": "3.0.53", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "soupsieve", + "version": "3.0.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jiter", + "version": "0.17.0", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "typesafe-sdk", + "version": "0.7.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jevdual", + "version": "0.0.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-api-python-client", + "version": "2.188.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "yarl", + "version": "1.25.1", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "posthog", + "version": "7.7.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jsonschema", + "version": "4.26.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "orjson", + "version": "3.13.0", + "tags": [ + "cp314-cp314-macosx_15_0_arm64" + ] + }, + { + "name": "google-auth-oauthlib", + "version": "1.2.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "requests", + "version": "2.33.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "PyJWT", + "version": "2.15.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "opentelemetry-api", + "version": "1.45.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pydantic_core", + "version": "2.46.5", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "browser-use-sdk", + "version": "3.4.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyotp", + "version": "2.9.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "docstring_parser", + "version": "0.18.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "propcache", + "version": "0.5.4", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "mcp", + "version": "2.1.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "rich", + "version": "14.3.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "uritemplate", + "version": "4.2.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "reportlab", + "version": "4.4.9", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "tenacity", + "version": "9.1.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "aiofiles", + "version": "25.1.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-framework-Quartz", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "httpcore", + "version": "1.0.9", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "cryptography", + "version": "50.0.2", + "tags": [ + "cp311-abi3-macosx_11_0_arm64" + ] + }, + { + "name": "pyobjc-framework-Cocoa", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "wcwidth", + "version": "0.9.2", + "tags": [ + "cp310-abi3-macosx_11_0_arm64" + ] + }, + { + "name": "googleapis-common-protos", + "version": "1.75.5", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-genai", + "version": "1.65.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "tqdm", + "version": "4.70.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "urllib3", + "version": "2.8.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-dotenv", + "version": "1.2.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "h11", + "version": "0.16.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pydantic", + "version": "2.13.5", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "protobuf", + "version": "6.33.6", + "tags": [ + "cp39-abi3-macosx_10_9_universal2" + ] + }, + { + "name": "pydantic-settings", + "version": "2.15.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "sniffio", + "version": "1.3.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "rsa", + "version": "4.9.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyasn1", + "version": "0.6.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "screeninfo", + "version": "0.8.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "mdurl", + "version": "0.1.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-multipart", + "version": "0.0.32", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-framework-ApplicationServices", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "fetch-use", + "version": "0.4.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "openai", + "version": "2.26.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-core", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "click", + "version": "8.3.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "markdownify", + "version": "1.2.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "frozenlist", + "version": "1.8.0", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "cffi", + "version": "2.1.1", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "uuid7", + "version": "0.1.0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "typing_extensions", + "version": "4.15.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "aiosignal", + "version": "1.4.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "certifi", + "version": "2026.7.22", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "browser-use", + "version": "0.13.10", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "annotated-types", + "version": "0.8.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "referencing", + "version": "0.37.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-auth-httplib2", + "version": "0.4.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pycparser", + "version": "3.11", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "markdown-it-py", + "version": "4.2.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-auth", + "version": "2.48.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "bubus", + "version": "1.5.6", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "typing-inspection", + "version": "0.4.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "starlette", + "version": "1.7.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "distro", + "version": "1.9.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pfzy", + "version": "0.3.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "rpds-py", + "version": "2026.9.1", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "httplib2", + "version": "0.32.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-docx", + "version": "1.2.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "attrs", + "version": "26.1.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "ollama", + "version": "0.6.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "websockets", + "version": "15.0.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pypdf", + "version": "6.16.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "httpx", + "version": "0.28.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "httpx2", + "version": "2.13.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "sse-starlette", + "version": "3.5.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "backoff", + "version": "2.2.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyasn1_modules", + "version": "0.4.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-api-core", + "version": "2.29.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "beautifulsoup4", + "version": "4.15.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "psutil", + "version": "7.2.2", + "tags": [ + "cp36-abi3-macosx_11_0_arm64" + ] + }, + { + "name": "aiohttp", + "version": "3.14.3", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "cdp-use", + "version": "1.4.5", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "truststore", + "version": "0.10.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "charset-normalizer", + "version": "3.5.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "pillow", + "version": "12.3.0", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "Cython", + "version": "3.3.0", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "proto-plus", + "version": "1.29.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "httpcore2", + "version": "2.13.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyparsing", + "version": "3.3.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "cua-driver", + "version": "0.28.2", + "tags": [ + "py3-none-macosx_13_0_universal2" + ] + }, + { + "name": "six", + "version": "1.17.0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "requests-oauthlib", + "version": "2.0.0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "multidict", + "version": "6.9.1", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "anthropic", + "version": "0.76.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "mcp-types", + "version": "2.1.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "idna", + "version": "3.20", + "tags": [ + "py3-none-any" + ] + } + ], + "cache_bytes_after_install": 391982537, + "private_state": { + "private-home": "/Users/tom/footwork-u4/footwork/output/install-v1/private-home", + "private-config": "/Users/tom/footwork-u4/footwork/output/install-v1/private-config", + "private-cache": "/Users/tom/footwork-u4/footwork/output/install-v1/private-cache", + "private-temp": "/Users/tom/footwork-u4/footwork/output/install-v1/private-temp" + } +} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v1/native-receipt.json b/jevdual/results/u3-packet-v4-permission-20261012-v1/native-receipt.json new file mode 100644 index 0000000..084360c --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v1/native-receipt.json @@ -0,0 +1,372 @@ +{ + "passed": true, + "source_commit": "21599c641e4c54cad137b3c49e11142c5c14e596", + "origins": { + "artifact_version": "0.0.1", + "python": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u4/footwork/output/install-v1/venv", + "kernel_executable": "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + "kernel_argv0": "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + "package": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", + "worker": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/execution_worker.py", + "backend": "pure-python", + "sys_path": [ + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python314.zip", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/lib-dynload", + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages" + ] + }, + "actual_runtime_contexts": [ + { + "role": "worker", + "operation": "op_a3c8d3e60e2d", + "pid": 53493, + "python": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u4/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py" + ], + "mcp": null, + "ApplicationServices": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py" + ], + "Quartz": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py" + ], + "Foundation": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/Foundation/__init__.py" + ] + }, + "cua_library": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/libcua_driver_sdk.dylib", + "home": "/Users/tom/footwork-u4/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791831095436142000 + }, + { + "role": "server", + "operation": null, + "pid": 53368, + "python": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u4/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py" + ], + "mcp": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py" + ], + "ApplicationServices": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py" + ], + "Quartz": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py" + ], + "Foundation": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/Foundation/__init__.py" + ] + }, + "cua_library": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/libcua_driver_sdk.dylib", + "home": "/Users/tom/footwork-u4/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791831094548872000 + }, + { + "role": "server", + "operation": null, + "pid": 53368, + "python": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u4/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": null, + "mcp": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py" + ], + "ApplicationServices": null, + "Quartz": null, + "Foundation": null + }, + "cua_library": null, + "home": "/Users/tom/footwork-u4/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791831093299192000 + }, + { + "role": "server", + "operation": null, + "pid": 53523, + "python": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u4/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": null, + "mcp": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py" + ], + "ApplicationServices": null, + "Quartz": null, + "Foundation": null + }, + "cua_library": null, + "home": "/Users/tom/footwork-u4/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791831096739411000 + }, + { + "role": "server", + "operation": null, + "pid": 53368, + "python": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u4/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py" + ], + "mcp": [ + "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py" + ], + "ApplicationServices": null, + "Quartz": null, + "Foundation": null + }, + "cua_library": "/Users/tom/footwork-u4/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/libcua_driver_sdk.dylib", + "home": "/Users/tom/footwork-u4/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791831093759737000 + } + ], + "emitted_configuration_checked": true, + "initialized": { + "capabilities": { + "experimental": {}, + "tools": { + "listChanged": false + } + }, + "protocolVersion": "2025-06-18", + "serverInfo": { + "name": "footwork-capability", + "version": "0.0.1" + } + }, + "worker_proofs": [ + { + "registration": { + "nonce": "8c75f10165e6462babbdb4bdc61c669a", + "operation_id": "op_a3c8d3e60e2d", + "packet_digest": "0ebc4a7e56c0d4c4d1a118abf20e411f4a3c35cfe7181b58872bec7c37f92528", + "pgid": 53493, + "pid": 53493, + "request_digest": "30225dcd2278b572fc206259c7bcca93b56586cc42b1bdca0d0564bf1d20eb51", + "start": "0x1.ab34b0daf2c41p+30", + "version": 1 + }, + "cmdline": [ + "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + "-I", + "-m", + "jevdual.capability.execution_worker", + "--packet", + "/Users/tom/footwork-u4/footwork/output/job-v1/operations.sqlite.executions/op_a3c8d3e60e2d/packet.json" + ], + "executable": "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python" + } + ], + "action": { + "is_error": false, + "schema_version": "1", + "operation_id": "op_a3c8d3e60e2d", + "disposition": "applied", + "state": "final", + "phase": "sent", + "summary": "Historical append confirmed by retained execution evidence; current agreement is separate.", + "repeat": false, + "evidence": { + "execution_receipt": { + "disposition": "applied", + "error": null, + "evidence": { + "after": { + "length": 24, + "sha256": "783172d2e6ac11f2c5450015856703ebe655b8c2aa86ca81b802ab1d8d210aee" + }, + "before": { + "length": 15, + "sha256": "ac11060594d5bcde616f5e88156ea7c0f55e2cca74c2de52c4da375fbde33339" + }, + "driver_effect": "confirmed", + "identity": { + "boot": "11EBDE2F-6F02-4282-B73B-6DB59AABD9A1", + "device": 16777233, + "document": "/Users/tom/footwork-u4/footwork/output/fixture-v1/fixture-v1-invoice.txt", + "inode": 163250, + "pid": 2817, + "start": "0x1.ab2ff26e1fcadp+30" + }, + "identity_after": { + "boot": "11EBDE2F-6F02-4282-B73B-6DB59AABD9A1", + "device": 16777233, + "document": "/Users/tom/footwork-u4/footwork/output/fixture-v1/fixture-v1-invoice.txt", + "inode": 163250, + "pid": 2817, + "start": "0x1.ab2ff26e1fcadp+30" + }, + "readback": { + "kind": "exact_field_readback", + "passed": true, + "persistence": "not_checked" + } + }, + "nonce": "8c75f10165e6462babbdb4bdc61c669a", + "operation_id": "op_a3c8d3e60e2d", + "packet_digest": "0ebc4a7e56c0d4c4d1a118abf20e411f4a3c35cfe7181b58872bec7c37f92528", + "phase": "sent", + "request_digest": "30225dcd2278b572fc206259c7bcca93b56586cc42b1bdca0d0564bf1d20eb51", + "version": 1 + }, + "readback": { + "kind": "exact_field_readback", + "passed": true, + "persistence": "not_checked", + "outcome": "applied", + "historical": true + } + }, + "reporting": { + "historical_effect": "applied", + "disk_persistence": "not_checked", + "guidance": "Disk persistence was not checked. Do not claim the file is saved or unsaved. Report the retained execution result separately from current document agreement." + }, + "client_token": "installed-once" + }, + "repeat": { + "is_error": false, + "schema_version": "1", + "operation_id": "op_a3c8d3e60e2d", + "disposition": "applied", + "state": "final", + "phase": "sent", + "summary": "Historical append confirmed by retained execution evidence; current agreement is separate.", + "repeat": true, + "evidence": { + "execution_receipt": { + "disposition": "applied", + "error": null, + "evidence": { + "after": { + "length": 24, + "sha256": "783172d2e6ac11f2c5450015856703ebe655b8c2aa86ca81b802ab1d8d210aee" + }, + "before": { + "length": 15, + "sha256": "ac11060594d5bcde616f5e88156ea7c0f55e2cca74c2de52c4da375fbde33339" + }, + "driver_effect": "confirmed", + "identity": { + "boot": "11EBDE2F-6F02-4282-B73B-6DB59AABD9A1", + "device": 16777233, + "document": "/Users/tom/footwork-u4/footwork/output/fixture-v1/fixture-v1-invoice.txt", + "inode": 163250, + "pid": 2817, + "start": "0x1.ab2ff26e1fcadp+30" + }, + "identity_after": { + "boot": "11EBDE2F-6F02-4282-B73B-6DB59AABD9A1", + "device": 16777233, + "document": "/Users/tom/footwork-u4/footwork/output/fixture-v1/fixture-v1-invoice.txt", + "inode": 163250, + "pid": 2817, + "start": "0x1.ab2ff26e1fcadp+30" + }, + "readback": { + "kind": "exact_field_readback", + "passed": true, + "persistence": "not_checked" + } + }, + "nonce": "8c75f10165e6462babbdb4bdc61c669a", + "operation_id": "op_a3c8d3e60e2d", + "packet_digest": "0ebc4a7e56c0d4c4d1a118abf20e411f4a3c35cfe7181b58872bec7c37f92528", + "phase": "sent", + "request_digest": "30225dcd2278b572fc206259c7bcca93b56586cc42b1bdca0d0564bf1d20eb51", + "version": 1 + }, + "readback": { + "kind": "exact_field_readback", + "passed": true, + "persistence": "not_checked", + "outcome": "applied", + "historical": true + } + }, + "reporting": { + "historical_effect": "applied", + "disk_persistence": "not_checked", + "guidance": "Disk persistence was not checked. Do not claim the file is saved or unsaved. Report the retained execution result separately from current document agreement." + }, + "client_token": "installed-once" + }, + "report": { + "schema_version": "1", + "scope": "all_admitted_task_operations", + "validation": { + "accepted": true, + "reasons": [] + }, + "operation_completion": "completed", + "operations": [ + { + "operation": "op_a3c8d3e60e2d", + "state": "final", + "disposition": "applied", + "updated": 1791831096.0829408, + "created": 1791831094.731467 + } + ], + "persistence": "not_checked", + "current_state": "not_checked", + "task": "task_e773ee572b84420b9c1eeed7173ead46", + "snapshot_digest": "09aecceb2931e9ecde82e51cfa1010701352a5884f119109171d253305391b17", + "ledger_span": { + "first_created": 1791831094.731467, + "last_updated": 1791831096.0829408, + "count": 1 + }, + "accepted_host_claim": "completed", + "as_of": 1791831096.956186, + "trusted_summary": "1 recorded operations confirmed applied; 0 attempts confirmed no input. These are historical effects, not certification of the host job. Full task ledger: 1 records, 2026-10-12T18:51:34.731467+00:00 through 2026-10-12T18:51:36.082941+00:00. Current document state and disk persistence are not certified by this report." + }, + "oracle_actual": "Invoice report\n17.25 USD", + "owned_worker_finished": true, + "limits": "Existing host OS/system frameworks/permissions. Fresh dependency/private-home qualification." +} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v1/report.md b/jevdual/results/u3-packet-v4-permission-20261012-v1/report.md new file mode 100644 index 0000000..5ad2337 --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v1/report.md @@ -0,0 +1,5 @@ +U3v1 failed its required automatic document confirmation gate. Frozen packet eef36325/source21599c6 cold-installed seven stages, refused without Accessibility before resource creation, captured the user-approved Terminal OFF→ON transition, and passed strict doctor after Terminal restart. The installed native trial passed its exact append oracle, same-token server restart/replay, one durable final/applied/sent operation and accepted host completion report. The worker used the installed environment and private HOME. Screen Recording remained not granted. + +One SIGINT automatically removed the document from view; cleanup returned ax_attribute_refused:AXRole:-25202 and exit6. Browser/listener cleanup and independently observed owned process/listener absence passed. No manual document close or Automation grant occurred. File snapshots changed15→24bytes; disk persistence remains not_checked. The original run stays failed. A repair trial will have separate frozen source/evidence. + +Raw archive output/u3-export-v1/evidence-v1.zip retains86members; eight pre-grant baseline hashes independently match. An optional operator AX readback probe failed with NameError before querying windows and adds no evidence. Archive doctor paths carry a relative Users/tom prefix due to zip absolute input; no path traversal. Guest fixture document contents were not included by the misnamed invoice.txt archive glob; the native receipt contains the actual oracle and cleanup file snapshots. diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v1/startup.json b/jevdual/results/u3-packet-v4-permission-20261012-v1/startup.json new file mode 100644 index 0000000..f60a11e --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v1/startup.json @@ -0,0 +1,5 @@ +{ + "status": "ready", + "pid": 52622, + "port": 49236 +} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v1/summary.json b/jevdual/results/u3-packet-v4-permission-20261012-v1/summary.json new file mode 100644 index 0000000..3c771aa --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v1/summary.json @@ -0,0 +1,36 @@ +{ + "passed": false, + "classification": "fresh permission state on previously provisioned guest", + "packet_sha256": "eef3632506680d74b21b511cec905d73de2324f951e924b0716f2309490f590d", + "source_commit": "21599c641e4c54cad137b3c49e11142c5c14e596", + "raw_archive_sha256": "2d9a03ef17016aa7894e8679c4fd1fbe492e667e335bff8cd4cf01e38ee65427", + "baseline_hashes_match": { + "permission-reset.txt": true, + "doctor-before-exit.txt": true, + "doctor-before-stderr.txt": true, + "denied-fixture-console.txt": true, + "denied-fixture-exit.txt": true, + "denied-resource-check.txt": true, + "footwork/output/doctor-before.json": true, + "footwork/output/install-v1/receipt.json": true + }, + "durable_rows": [ + [ + "op_a3c8d3e60e2d", + "final", + "applied", + "sent" + ] + ], + "install": true, + "denied_baseline": true, + "observed_approved_grant": true, + "terminal_restart": true, + "strict_doctor_after": true, + "native": true, + "automatic_document_confirmation": false, + "fixture_exit": 6, + "manual_document_close": false, + "browser_listener_cleanup": true, + "disk_persistence": "not_checked" +} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v3/cleanup.json b/jevdual/results/u3-packet-v4-permission-20261012-v3/cleanup.json new file mode 100644 index 0000000..a4722b5 --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v3/cleanup.json @@ -0,0 +1,25 @@ +{ + "document": { + "status": "confirmed", + "reason": "window_disappeared", + "pid": 457, + "file_before": { + "status": "observed", + "size_bytes": 15, + "sha256": "ac11060594d5bcde616f5e88156ea7c0f55e2cca74c2de52c4da375fbde33339" + }, + "file_after": { + "status": "observed", + "size_bytes": 24, + "sha256": "783172d2e6ac11f2c5450015856703ebe655b8c2aa86ca81b802ab1d8d210aee" + } + }, + "browser": { + "status": "confirmed", + "pid": 1356, + "returncode": 0 + }, + "listener": { + "status": "confirmed" + } +} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v3/doctor-after.json b/jevdual/results/u3-packet-v4-permission-20261012-v3/doctor-after.json new file mode 100644 index 0000000..92e9e72 --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v3/doctor-after.json @@ -0,0 +1 @@ +{"platform": "darwin", "supported": true, "runtime": {"python_version": "3.14.7", "python_requirement": ">=3.11,<4.0", "python_compatible": true, "interpreter": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", "architecture": "arm64", "distribution": {"version": "0.0.1", "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "loaded_modules": {"jevdual": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", "jevdual.capability.setup": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/setup.py", "ApplicationServices": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py", "Quartz": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py"}}, "dependencies": {"browser-use": {"version": "0.13.10", "requirement": "==0.13.10", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "typesafe-sdk": {"version": "0.7.4", "requirement": "<1,>=0.7", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "orjson": {"version": "3.13.0", "requirement": ">=3.10", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "psutil": {"version": "7.2.2", "requirement": "==7.2.2", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "mcp": {"version": "2.1.1", "requirement": "==2.1.1", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "packaging": {"version": "26.3", "requirement": "<27,>=25", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "cua-driver": {"version": "0.28.2", "requirement": "==0.28.2", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-applicationservices": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-quartz": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-cocoa": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}}, "permissions": {"accessibility": "granted", "screen_recording": "not_granted"}, "scope": "not_checked", "journal": "not_checked", "warnings": ["Screen Recording is not qualified. Routes that capture pixels may require it."], "dependency_metadata": "installed distribution", "limitation": "macOS only: one loopback HTTP browser fixture and one identified TextEdit document. Window identity is checked when binding.", "provider_account_required": false, "rust_toolchain_required": false, "blockers": [], "prerequisite_status": "met", "qualification": "Prerequisites only. Window identity, current state, effects and job completion require separate checks."} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v3/doctor-before.json b/jevdual/results/u3-packet-v4-permission-20261012-v3/doctor-before.json new file mode 100644 index 0000000..ba74bd2 --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v3/doctor-before.json @@ -0,0 +1 @@ +{"platform": "darwin", "supported": true, "runtime": {"python_version": "3.14.7", "python_requirement": ">=3.11,<4.0", "python_compatible": true, "interpreter": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", "architecture": "arm64", "distribution": {"version": "0.0.1", "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "loaded_modules": {"jevdual": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", "jevdual.capability.setup": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/setup.py", "ApplicationServices": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py", "Quartz": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py"}}, "dependencies": {"browser-use": {"version": "0.13.10", "requirement": "==0.13.10", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "typesafe-sdk": {"version": "0.7.4", "requirement": "<1,>=0.7", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "orjson": {"version": "3.13.0", "requirement": ">=3.10", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "psutil": {"version": "7.2.2", "requirement": "==7.2.2", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "mcp": {"version": "2.1.1", "requirement": "==2.1.1", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "packaging": {"version": "26.3", "requirement": "<27,>=25", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "cua-driver": {"version": "0.28.2", "requirement": "==0.28.2", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-applicationservices": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-quartz": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}, "pyobjc-framework-cocoa": {"version": "12.2.2", "requirement": ">=12", "compatible": true, "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages"}}, "permissions": {"accessibility": "not_granted", "screen_recording": "not_granted"}, "scope": "not_checked", "journal": "not_checked", "warnings": ["Screen Recording is not qualified. Routes that capture pixels may require it."], "dependency_metadata": "installed distribution", "limitation": "macOS only: one loopback HTTP browser fixture and one identified TextEdit document. Window identity is checked when binding.", "provider_account_required": false, "rust_toolchain_required": false, "blockers": ["accessibility_not_granted"], "prerequisite_status": "blocked", "qualification": "Prerequisites only. Window identity, current state, effects and job completion require separate checks."} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v3/install-receipt.json b/jevdual/results/u3-packet-v4-permission-20261012-v3/install-receipt.json new file mode 100644 index 0000000..c8201ee --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v3/install-receipt.json @@ -0,0 +1,1521 @@ +{ + "source_commit": "445a348ff31243c2f3d90baae9ce209d89bd7eac", + "host": { + "platform": "Darwin", + "machine": "arm64" + }, + "interpreter_requested": "/Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14", + "wheel": "/Users/tom/footwork-u4-repair/footwork/artifacts/jevdual-0.0.1-py3-none-any.whl", + "wheel_sha256": "9bb0440d7bd981df7dc70bbd22f97861a7ed32a88bf6de7a18c2f46982fef3a7", + "cache_initially_empty": true, + "system_site_packages": false, + "borrowed_dependencies": false, + "registry": "https://pypi.org/simple", + "binary_only": true, + "commands": [ + { + "stage": "venv", + "argv": [ + "/Users/tom/footwork-u2/tools/uv-aarch64-apple-darwin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "venv", + "--python", + "/Library/Frameworks/Python.framework/Versions/3.14/bin/python3.14", + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv" + ], + "returncode": 0 + }, + { + "stage": "install", + "argv": [ + "/Users/tom/footwork-u2/tools/uv-aarch64-apple-darwin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "pip", + "install", + "--python", + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", + "--default-index", + "https://pypi.org/simple", + "--only-binary", + ":all:", + "/Users/tom/footwork-u4-repair/footwork/artifacts/jevdual-0.0.1-py3-none-any.whl" + ], + "returncode": 0 + }, + { + "stage": "consistency", + "argv": [ + "/Users/tom/footwork-u2/tools/uv-aarch64-apple-darwin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "pip", + "check", + "--python", + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python" + ], + "returncode": 0 + }, + { + "stage": "inventory", + "argv": [ + "/Users/tom/footwork-u2/tools/uv-aarch64-apple-darwin/uv", + "--no-config", + "--no-progress", + "--cache-dir", + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/empty-cache", + "--no-python-downloads", + "pip", + "list", + "--python", + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", + "--format", + "json" + ], + "returncode": 0 + }, + { + "stage": "origins", + "argv": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", + "-I", + "-c", + "\nimport sys,json,importlib.metadata,importlib\nfrom pathlib import Path\nimport jevdual,jevdual.capability.execution_worker as worker\nfrom jevdual._native import BACKEND\nnames=(\"cua_driver\",\"psutil\",\"mcp\",\"ApplicationServices\",\"Quartz\",\"Foundation\",\"packaging\")\nmodules={n:str(importlib.import_module(n).__file__) for n in names}\nprint(json.dumps({\"version\":sys.version,\"prefix\":sys.prefix,\"base_prefix\":sys.base_prefix,\"python\":sys.executable,\"package\":jevdual.__file__,\"worker\":worker.__file__,\"backend\":BACKEND,\"module_origins\":modules,\"sys_path\":sys.path}))\n" + ], + "returncode": 0 + }, + { + "stage": "doctor", + "argv": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/footwork-capability", + "doctor" + ], + "returncode": 0 + }, + { + "stage": "wheel-tags", + "argv": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", + "-I", + "-c", + "import json,importlib.metadata;print(json.dumps([{\"name\":d.metadata[\"Name\"],\"version\":d.version,\"tags\":[l[5:] for l in (d.read_text(\"WHEEL\") or \"\").splitlines() if l.startswith(\"Tag: \")]} for d in importlib.metadata.distributions()]))" + ], + "returncode": 0 + } + ], + "passed": true, + "limits": "Fresh dependencies on existing host OS/interpreter, not a fresh machine or TCC permission database.", + "origins": { + "version": "3.14.7 (v3.14.7:823f0323ee6, Aug 5 2026, 07:07:01) [Clang 21.0.0 (clang-2100.1.1.101)]", + "prefix": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv", + "base_prefix": "/Library/Frameworks/Python.framework/Versions/3.14", + "python": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", + "package": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", + "worker": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/execution_worker.py", + "backend": "pure-python", + "module_origins": { + "cua_driver": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py", + "psutil": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/psutil/__init__.py", + "mcp": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py", + "ApplicationServices": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py", + "Quartz": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py", + "Foundation": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/Foundation/__init__.py", + "packaging": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/packaging/__init__.py" + }, + "sys_path": [ + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python314.zip", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/lib-dynload", + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + ] + }, + "doctor": { + "platform": "darwin", + "supported": true, + "runtime": { + "python_version": "3.14.7", + "python_requirement": ">=3.11,<4.0", + "python_compatible": true, + "interpreter": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", + "architecture": "arm64", + "distribution": { + "version": "0.0.1", + "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "loaded_modules": { + "jevdual": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", + "jevdual.capability.setup": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/setup.py", + "ApplicationServices": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py", + "Quartz": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py" + } + }, + "dependencies": { + "browser-use": { + "version": "0.13.10", + "requirement": "==0.13.10", + "compatible": true, + "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "typesafe-sdk": { + "version": "0.7.4", + "requirement": "<1,>=0.7", + "compatible": true, + "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "orjson": { + "version": "3.13.0", + "requirement": ">=3.10", + "compatible": true, + "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "psutil": { + "version": "7.2.2", + "requirement": "==7.2.2", + "compatible": true, + "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "mcp": { + "version": "2.1.1", + "requirement": "==2.1.1", + "compatible": true, + "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "packaging": { + "version": "26.3", + "requirement": "<27,>=25", + "compatible": true, + "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "cua-driver": { + "version": "0.28.2", + "requirement": "==0.28.2", + "compatible": true, + "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "pyobjc-framework-applicationservices": { + "version": "12.2.2", + "requirement": ">=12", + "compatible": true, + "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "pyobjc-framework-quartz": { + "version": "12.2.2", + "requirement": ">=12", + "compatible": true, + "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + }, + "pyobjc-framework-cocoa": { + "version": "12.2.2", + "requirement": ">=12", + "compatible": true, + "location": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + } + }, + "permissions": { + "accessibility": "granted", + "screen_recording": "not_granted" + }, + "scope": "not_checked", + "journal": "not_checked", + "warnings": [ + "Screen Recording is not qualified. Routes that capture pixels may require it." + ], + "dependency_metadata": "installed distribution", + "limitation": "macOS only: one loopback HTTP browser fixture and one identified TextEdit document. Window identity is checked when binding.", + "provider_account_required": false, + "rust_toolchain_required": false, + "blockers": [], + "prerequisite_status": "met", + "qualification": "Prerequisites only. Window identity, current state, effects and job completion require separate checks." + }, + "inventory": [ + { + "name": "aiofiles", + "version": "25.1.0" + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1" + }, + { + "name": "aiohttp", + "version": "3.14.3" + }, + { + "name": "aiosignal", + "version": "1.4.0" + }, + { + "name": "annotated-types", + "version": "0.8.0" + }, + { + "name": "anthropic", + "version": "0.76.0" + }, + { + "name": "anyio", + "version": "4.12.1" + }, + { + "name": "attrs", + "version": "26.1.0" + }, + { + "name": "backoff", + "version": "2.2.1" + }, + { + "name": "beautifulsoup4", + "version": "4.15.0" + }, + { + "name": "browser-harness", + "version": "0.1.13" + }, + { + "name": "browser-use", + "version": "0.13.10" + }, + { + "name": "browser-use-sdk", + "version": "3.4.2" + }, + { + "name": "bubus", + "version": "1.5.6" + }, + { + "name": "cdp-use", + "version": "1.4.5" + }, + { + "name": "certifi", + "version": "2026.7.22" + }, + { + "name": "cffi", + "version": "2.1.1" + }, + { + "name": "charset-normalizer", + "version": "3.5.2" + }, + { + "name": "click", + "version": "8.3.3" + }, + { + "name": "cloudpickle", + "version": "3.1.2" + }, + { + "name": "cryptography", + "version": "50.0.2" + }, + { + "name": "cua-driver", + "version": "0.28.2" + }, + { + "name": "cython", + "version": "3.3.0" + }, + { + "name": "distro", + "version": "1.9.0" + }, + { + "name": "docstring-parser", + "version": "0.18.0" + }, + { + "name": "fetch-use", + "version": "0.4.0" + }, + { + "name": "frozenlist", + "version": "1.8.0" + }, + { + "name": "google-api-core", + "version": "2.29.0" + }, + { + "name": "google-api-python-client", + "version": "2.188.0" + }, + { + "name": "google-auth", + "version": "2.48.0" + }, + { + "name": "google-auth-httplib2", + "version": "0.4.4" + }, + { + "name": "google-auth-oauthlib", + "version": "1.2.4" + }, + { + "name": "google-genai", + "version": "1.65.0" + }, + { + "name": "googleapis-common-protos", + "version": "1.75.5" + }, + { + "name": "groq", + "version": "1.0.0" + }, + { + "name": "h11", + "version": "0.16.0" + }, + { + "name": "httpcore", + "version": "1.0.9" + }, + { + "name": "httpcore2", + "version": "2.13.1" + }, + { + "name": "httplib2", + "version": "0.32.0" + }, + { + "name": "httpx", + "version": "0.28.1" + }, + { + "name": "httpx2", + "version": "2.13.1" + }, + { + "name": "idna", + "version": "3.20" + }, + { + "name": "inquirerpy", + "version": "0.3.4" + }, + { + "name": "jevdual", + "version": "0.0.1" + }, + { + "name": "jiter", + "version": "0.17.0" + }, + { + "name": "jsonschema", + "version": "4.26.0" + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1" + }, + { + "name": "lxml", + "version": "6.1.3" + }, + { + "name": "markdown-it-py", + "version": "4.2.0" + }, + { + "name": "markdownify", + "version": "1.2.2" + }, + { + "name": "mcp", + "version": "2.1.1" + }, + { + "name": "mcp-types", + "version": "2.1.1" + }, + { + "name": "mdurl", + "version": "0.1.2" + }, + { + "name": "multidict", + "version": "6.9.1" + }, + { + "name": "oauthlib", + "version": "4.0.0" + }, + { + "name": "ollama", + "version": "0.6.1" + }, + { + "name": "openai", + "version": "2.26.0" + }, + { + "name": "opentelemetry-api", + "version": "1.45.1" + }, + { + "name": "orjson", + "version": "3.13.0" + }, + { + "name": "packaging", + "version": "26.3" + }, + { + "name": "pfzy", + "version": "0.3.4" + }, + { + "name": "pillow", + "version": "12.3.0" + }, + { + "name": "portalocker", + "version": "4.4.0" + }, + { + "name": "posthog", + "version": "7.7.0" + }, + { + "name": "prompt-toolkit", + "version": "3.0.53" + }, + { + "name": "propcache", + "version": "0.5.4" + }, + { + "name": "proto-plus", + "version": "1.29.0" + }, + { + "name": "protobuf", + "version": "6.33.6" + }, + { + "name": "psutil", + "version": "7.2.2" + }, + { + "name": "pyasn1", + "version": "0.6.4" + }, + { + "name": "pyasn1-modules", + "version": "0.4.2" + }, + { + "name": "pycparser", + "version": "3.11" + }, + { + "name": "pydantic", + "version": "2.13.5" + }, + { + "name": "pydantic-core", + "version": "2.46.5" + }, + { + "name": "pydantic-settings", + "version": "2.15.0" + }, + { + "name": "pygments", + "version": "2.21.0" + }, + { + "name": "pyjwt", + "version": "2.15.1" + }, + { + "name": "pyobjc-core", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-applicationservices", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-cocoa", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-coretext", + "version": "12.2.2" + }, + { + "name": "pyobjc-framework-quartz", + "version": "12.2.2" + }, + { + "name": "pyotp", + "version": "2.9.0" + }, + { + "name": "pyparsing", + "version": "3.3.3" + }, + { + "name": "pypdf", + "version": "6.16.2" + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0" + }, + { + "name": "python-docx", + "version": "1.2.0" + }, + { + "name": "python-dotenv", + "version": "1.2.2" + }, + { + "name": "python-multipart", + "version": "0.0.32" + }, + { + "name": "referencing", + "version": "0.37.0" + }, + { + "name": "reportlab", + "version": "4.4.9" + }, + { + "name": "requests", + "version": "2.33.0" + }, + { + "name": "requests-oauthlib", + "version": "2.0.0" + }, + { + "name": "rich", + "version": "14.3.3" + }, + { + "name": "rpds-py", + "version": "2026.9.1" + }, + { + "name": "rsa", + "version": "4.9.1" + }, + { + "name": "screeninfo", + "version": "0.8.1" + }, + { + "name": "six", + "version": "1.17.0" + }, + { + "name": "sniffio", + "version": "1.3.1" + }, + { + "name": "soupsieve", + "version": "3.0.3" + }, + { + "name": "sse-starlette", + "version": "3.5.0" + }, + { + "name": "starlette", + "version": "1.7.0" + }, + { + "name": "tenacity", + "version": "9.1.4" + }, + { + "name": "tqdm", + "version": "4.70.1" + }, + { + "name": "truststore", + "version": "0.10.4" + }, + { + "name": "typesafe-sdk", + "version": "0.7.4" + }, + { + "name": "typing-extensions", + "version": "4.15.0" + }, + { + "name": "typing-inspection", + "version": "0.4.4" + }, + { + "name": "uritemplate", + "version": "4.2.0" + }, + { + "name": "urllib3", + "version": "2.8.0" + }, + { + "name": "uuid7", + "version": "0.1.0" + }, + { + "name": "uvicorn", + "version": "0.54.0" + }, + { + "name": "wcwidth", + "version": "0.9.2" + }, + { + "name": "websockets", + "version": "15.0.1" + }, + { + "name": "yarl", + "version": "1.25.1" + } + ], + "wheel_tags": [ + { + "name": "anyio", + "version": "4.12.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "uvicorn", + "version": "0.54.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "groq", + "version": "1.0.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "oauthlib", + "version": "4.0.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "cloudpickle", + "version": "3.1.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-framework-CoreText", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "portalocker", + "version": "4.4.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "lxml", + "version": "6.1.3", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "packaging", + "version": "26.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "Pygments", + "version": "2.21.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "inquirerpy", + "version": "0.3.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "browser-harness", + "version": "0.1.13", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "prompt_toolkit", + "version": "3.0.53", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "soupsieve", + "version": "3.0.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jiter", + "version": "0.17.0", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "typesafe-sdk", + "version": "0.7.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jevdual", + "version": "0.0.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-api-python-client", + "version": "2.188.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "yarl", + "version": "1.25.1", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "posthog", + "version": "7.7.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "jsonschema", + "version": "4.26.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "orjson", + "version": "3.13.0", + "tags": [ + "cp314-cp314-macosx_15_0_arm64" + ] + }, + { + "name": "google-auth-oauthlib", + "version": "1.2.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "requests", + "version": "2.33.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "PyJWT", + "version": "2.15.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "opentelemetry-api", + "version": "1.45.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pydantic_core", + "version": "2.46.5", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "browser-use-sdk", + "version": "3.4.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyotp", + "version": "2.9.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "docstring_parser", + "version": "0.18.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "propcache", + "version": "0.5.4", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "mcp", + "version": "2.1.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "rich", + "version": "14.3.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "uritemplate", + "version": "4.2.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "reportlab", + "version": "4.4.9", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "tenacity", + "version": "9.1.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "aiofiles", + "version": "25.1.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-framework-Quartz", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "httpcore", + "version": "1.0.9", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "cryptography", + "version": "50.0.2", + "tags": [ + "cp311-abi3-macosx_11_0_arm64" + ] + }, + { + "name": "pyobjc-framework-Cocoa", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "wcwidth", + "version": "0.9.2", + "tags": [ + "cp310-abi3-macosx_11_0_arm64" + ] + }, + { + "name": "googleapis-common-protos", + "version": "1.75.5", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-genai", + "version": "1.65.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "tqdm", + "version": "4.70.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "urllib3", + "version": "2.8.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-dotenv", + "version": "1.2.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "h11", + "version": "0.16.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pydantic", + "version": "2.13.5", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "protobuf", + "version": "6.33.6", + "tags": [ + "cp39-abi3-macosx_10_9_universal2" + ] + }, + { + "name": "pydantic-settings", + "version": "2.15.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "sniffio", + "version": "1.3.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "rsa", + "version": "4.9.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyasn1", + "version": "0.6.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "screeninfo", + "version": "0.8.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "mdurl", + "version": "0.1.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-multipart", + "version": "0.0.32", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-framework-ApplicationServices", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "fetch-use", + "version": "0.4.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "openai", + "version": "2.26.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyobjc-core", + "version": "12.2.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "click", + "version": "8.3.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "markdownify", + "version": "1.2.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "frozenlist", + "version": "1.8.0", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "cffi", + "version": "2.1.1", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "uuid7", + "version": "0.1.0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "typing_extensions", + "version": "4.15.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "aiosignal", + "version": "1.4.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "certifi", + "version": "2026.7.22", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "browser-use", + "version": "0.13.10", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "annotated-types", + "version": "0.8.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "referencing", + "version": "0.37.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-auth-httplib2", + "version": "0.4.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pycparser", + "version": "3.11", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "markdown-it-py", + "version": "4.2.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-auth", + "version": "2.48.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "bubus", + "version": "1.5.6", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "typing-inspection", + "version": "0.4.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "starlette", + "version": "1.7.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "distro", + "version": "1.9.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pfzy", + "version": "0.3.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "rpds-py", + "version": "2026.9.1", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "httplib2", + "version": "0.32.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "python-docx", + "version": "1.2.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "attrs", + "version": "26.1.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "ollama", + "version": "0.6.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "websockets", + "version": "15.0.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pypdf", + "version": "6.16.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "httpx", + "version": "0.28.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "httpx2", + "version": "2.13.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "sse-starlette", + "version": "3.5.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "backoff", + "version": "2.2.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyasn1_modules", + "version": "0.4.2", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "google-api-core", + "version": "2.29.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "beautifulsoup4", + "version": "4.15.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "psutil", + "version": "7.2.2", + "tags": [ + "cp36-abi3-macosx_11_0_arm64" + ] + }, + { + "name": "aiohttp", + "version": "3.14.3", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "cdp-use", + "version": "1.4.5", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "truststore", + "version": "0.10.4", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "charset-normalizer", + "version": "3.5.2", + "tags": [ + "cp314-cp314-macosx_10_15_universal2" + ] + }, + { + "name": "pillow", + "version": "12.3.0", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "Cython", + "version": "3.3.0", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "proto-plus", + "version": "1.29.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "httpcore2", + "version": "2.13.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "pyparsing", + "version": "3.3.3", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "cua-driver", + "version": "0.28.2", + "tags": [ + "py3-none-macosx_13_0_universal2" + ] + }, + { + "name": "six", + "version": "1.17.0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "requests-oauthlib", + "version": "2.0.0", + "tags": [ + "py2-none-any", + "py3-none-any" + ] + }, + { + "name": "multidict", + "version": "6.9.1", + "tags": [ + "cp314-cp314-macosx_11_0_arm64" + ] + }, + { + "name": "anthropic", + "version": "0.76.0", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "mcp-types", + "version": "2.1.1", + "tags": [ + "py3-none-any" + ] + }, + { + "name": "idna", + "version": "3.20", + "tags": [ + "py3-none-any" + ] + } + ], + "cache_bytes_after_install": 391982600, + "private_state": { + "private-home": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/private-home", + "private-config": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/private-config", + "private-cache": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/private-cache", + "private-temp": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/private-temp" + } +} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v3/journey-evidence.json b/jevdual/results/u3-packet-v4-permission-20261012-v3/journey-evidence.json new file mode 100644 index 0000000..fe44845 --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v3/journey-evidence.json @@ -0,0 +1,17 @@ +{ + "reset-v3.txt": "Mon Oct 12 19:31:47 UTC 2026\nSuccessfully reset Accessibility approval status for com.apple.Terminal\n0", + "doctor-denied-v3-exit.txt": "1", + "denied-fixture-v3-exit.txt": "2", + "no-resources-v3.txt": "0", + "grant-v3.txt": "Codex click after19:33:55Z; human authenticated before19:35:16Z; OFF->ON captured\nUser approved exact Terminal grant; human entered guest authentication; admin session", + "terminal-restarted-v3.txt": "Mon Oct 12 19:40:10 UTC 2026", + "doctor-after-v3-exit.txt": "0", + "config-exit-v3.txt": "0", + "native-exit-v3.txt": "0", + "cleanup-signal-v3.txt": "Mon Oct 12 19:52:38 UTC 2026\n0", + "fixture-exit-v3.txt": "0", + "absence-v3.txt": "PID PPID COMM\n1\n % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\n 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\n 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\ncurl: (7) Failed to connect to 127.0.0.1 port 49152 after 1 ms: Couldn't connect to server\n7", + "worker-absence-v3.txt": "PID PPID COMM\n1", + "ui_evidence": "CUA transcript observes actual OFF, approved one switch click, password dialog completed by human, ON, Terminal quit and relaunch, native receipt, automatic document disappearance.", + "grant_time_precision": "Clock anchors bracket UI events; do not infer exact-second action timestamps." +} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v3/native-receipt.json b/jevdual/results/u3-packet-v4-permission-20261012-v3/native-receipt.json new file mode 100644 index 0000000..71958ff --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v3/native-receipt.json @@ -0,0 +1,372 @@ +{ + "passed": true, + "source_commit": "445a348ff31243c2f3d90baae9ce209d89bd7eac", + "origins": { + "artifact_version": "0.0.1", + "python": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv", + "kernel_executable": "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + "kernel_argv0": "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + "package": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py", + "worker": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/capability/execution_worker.py", + "backend": "pure-python", + "sys_path": [ + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python314.zip", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14", + "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/lib-dynload", + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages" + ] + }, + "actual_runtime_contexts": [ + { + "role": "worker", + "operation": "op_c16fcfb1b382", + "pid": 2248, + "python": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py" + ], + "mcp": null, + "ApplicationServices": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py" + ], + "Quartz": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py" + ], + "Foundation": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/Foundation/__init__.py" + ] + }, + "cua_library": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/libcua_driver_sdk.dylib", + "home": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791834474538243000 + }, + { + "role": "server", + "operation": null, + "pid": 2122, + "python": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py" + ], + "mcp": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py" + ], + "ApplicationServices": null, + "Quartz": null, + "Foundation": null + }, + "cua_library": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/libcua_driver_sdk.dylib", + "home": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791834471703405000 + }, + { + "role": "server", + "operation": null, + "pid": 2122, + "python": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": null, + "mcp": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py" + ], + "ApplicationServices": null, + "Quartz": null, + "Foundation": null + }, + "cua_library": null, + "home": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791834471279907000 + }, + { + "role": "server", + "operation": null, + "pid": 2283, + "python": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": null, + "mcp": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py" + ], + "ApplicationServices": null, + "Quartz": null, + "Foundation": null + }, + "cua_library": null, + "home": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791834475853045000 + }, + { + "role": "server", + "operation": null, + "pid": 2122, + "python": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/bin/python", + "prefix": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv", + "modules": { + "jevdual": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/jevdual/__init__.py" + ], + "jevdual.capability.execution_worker": null, + "cua_driver": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/__init__.py" + ], + "mcp": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/mcp/__init__.py" + ], + "ApplicationServices": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/ApplicationServices/__init__.py" + ], + "Quartz": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/Quartz/__init__.py" + ], + "Foundation": [ + "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/Foundation/__init__.py" + ] + }, + "cua_library": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/venv/lib/python3.14/site-packages/cua_driver/libcua_driver_sdk.dylib", + "home": "/Users/tom/footwork-u4-repair/footwork/output/install-v1/private-home", + "user_site_disabled": true, + "browser_telemetry_disabled": true, + "browser_cloud_sync_disabled": true, + "as_of_ns": 1791834473003239000 + } + ], + "emitted_configuration_checked": true, + "initialized": { + "capabilities": { + "experimental": {}, + "tools": { + "listChanged": false + } + }, + "protocolVersion": "2025-06-18", + "serverInfo": { + "name": "footwork-capability", + "version": "0.0.1" + } + }, + "worker_proofs": [ + { + "registration": { + "nonce": "0b96396764c349428849cc31a7663796", + "operation_id": "op_c16fcfb1b382", + "packet_digest": "cc48a8e135525b625691901e65e70e1bebc04bc5651c52f5f6aaa15e24b77d66", + "pgid": 2248, + "pid": 2248, + "request_digest": "adbb83357bb8578043a3f6c4bba35ee86db30e774245a04d3906bce092a389de", + "start": "0x1.ab34e5a52bcaep+30", + "version": 1 + }, + "cmdline": [ + "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python", + "-I", + "-m", + "jevdual.capability.execution_worker", + "--packet", + "/Users/tom/footwork-u4-repair/footwork/output/job-v3/operations.sqlite.executions/op_c16fcfb1b382/packet.json" + ], + "executable": "/Library/Frameworks/Python.framework/Versions/3.14/Resources/Python.app/Contents/MacOS/Python" + } + ], + "action": { + "is_error": false, + "schema_version": "1", + "operation_id": "op_c16fcfb1b382", + "disposition": "applied", + "state": "final", + "phase": "sent", + "summary": "Historical append confirmed by retained execution evidence; current agreement is separate.", + "repeat": false, + "evidence": { + "execution_receipt": { + "disposition": "applied", + "error": null, + "evidence": { + "after": { + "length": 24, + "sha256": "783172d2e6ac11f2c5450015856703ebe655b8c2aa86ca81b802ab1d8d210aee" + }, + "before": { + "length": 15, + "sha256": "ac11060594d5bcde616f5e88156ea7c0f55e2cca74c2de52c4da375fbde33339" + }, + "driver_effect": "confirmed", + "identity": { + "boot": "C48F0438-2151-43F5-92DE-99E433B66A72", + "device": 16777234, + "document": "/Users/tom/footwork-u4-repair/footwork/output/fixture-v3/fixture-v3-invoice.txt", + "inode": 197776, + "pid": 457, + "start": "0x1.ab34d2309867fp+30" + }, + "identity_after": { + "boot": "C48F0438-2151-43F5-92DE-99E433B66A72", + "device": 16777234, + "document": "/Users/tom/footwork-u4-repair/footwork/output/fixture-v3/fixture-v3-invoice.txt", + "inode": 197776, + "pid": 457, + "start": "0x1.ab34d2309867fp+30" + }, + "readback": { + "kind": "exact_field_readback", + "passed": true, + "persistence": "not_checked" + } + }, + "nonce": "0b96396764c349428849cc31a7663796", + "operation_id": "op_c16fcfb1b382", + "packet_digest": "cc48a8e135525b625691901e65e70e1bebc04bc5651c52f5f6aaa15e24b77d66", + "phase": "sent", + "request_digest": "adbb83357bb8578043a3f6c4bba35ee86db30e774245a04d3906bce092a389de", + "version": 1 + }, + "readback": { + "kind": "exact_field_readback", + "passed": true, + "persistence": "not_checked", + "outcome": "applied", + "historical": true + } + }, + "reporting": { + "historical_effect": "applied", + "disk_persistence": "not_checked", + "guidance": "Disk persistence was not checked. Do not claim the file is saved or unsaved. Report the retained execution result separately from current document agreement." + }, + "client_token": "installed-once" + }, + "repeat": { + "is_error": false, + "schema_version": "1", + "operation_id": "op_c16fcfb1b382", + "disposition": "applied", + "state": "final", + "phase": "sent", + "summary": "Historical append confirmed by retained execution evidence; current agreement is separate.", + "repeat": true, + "evidence": { + "execution_receipt": { + "disposition": "applied", + "error": null, + "evidence": { + "after": { + "length": 24, + "sha256": "783172d2e6ac11f2c5450015856703ebe655b8c2aa86ca81b802ab1d8d210aee" + }, + "before": { + "length": 15, + "sha256": "ac11060594d5bcde616f5e88156ea7c0f55e2cca74c2de52c4da375fbde33339" + }, + "driver_effect": "confirmed", + "identity": { + "boot": "C48F0438-2151-43F5-92DE-99E433B66A72", + "device": 16777234, + "document": "/Users/tom/footwork-u4-repair/footwork/output/fixture-v3/fixture-v3-invoice.txt", + "inode": 197776, + "pid": 457, + "start": "0x1.ab34d2309867fp+30" + }, + "identity_after": { + "boot": "C48F0438-2151-43F5-92DE-99E433B66A72", + "device": 16777234, + "document": "/Users/tom/footwork-u4-repair/footwork/output/fixture-v3/fixture-v3-invoice.txt", + "inode": 197776, + "pid": 457, + "start": "0x1.ab34d2309867fp+30" + }, + "readback": { + "kind": "exact_field_readback", + "passed": true, + "persistence": "not_checked" + } + }, + "nonce": "0b96396764c349428849cc31a7663796", + "operation_id": "op_c16fcfb1b382", + "packet_digest": "cc48a8e135525b625691901e65e70e1bebc04bc5651c52f5f6aaa15e24b77d66", + "phase": "sent", + "request_digest": "adbb83357bb8578043a3f6c4bba35ee86db30e774245a04d3906bce092a389de", + "version": 1 + }, + "readback": { + "kind": "exact_field_readback", + "passed": true, + "persistence": "not_checked", + "outcome": "applied", + "historical": true + } + }, + "reporting": { + "historical_effect": "applied", + "disk_persistence": "not_checked", + "guidance": "Disk persistence was not checked. Do not claim the file is saved or unsaved. Report the retained execution result separately from current document agreement." + }, + "client_token": "installed-once" + }, + "report": { + "schema_version": "1", + "scope": "all_admitted_task_operations", + "validation": { + "accepted": true, + "reasons": [] + }, + "operation_completion": "completed", + "operations": [ + { + "operation": "op_c16fcfb1b382", + "state": "final", + "disposition": "applied", + "updated": 1791834475.232835, + "created": 1791834473.2878752 + } + ], + "persistence": "not_checked", + "current_state": "not_checked", + "task": "task_c1db1311fda24fef9968d57bd2efb886", + "snapshot_digest": "1631b85181865742d164b3043f6e7213c6e0f08a9314ae221992d26810264039", + "ledger_span": { + "first_created": 1791834473.2878752, + "last_updated": 1791834475.232835, + "count": 1 + }, + "accepted_host_claim": "completed", + "as_of": 1791834476.0750048, + "trusted_summary": "1 recorded operations confirmed applied; 0 attempts confirmed no input. These are historical effects, not certification of the host job. Full task ledger: 1 records, 2026-10-12T19:47:53.287875+00:00 through 2026-10-12T19:47:55.232835+00:00. Current document state and disk persistence are not certified by this report." + }, + "oracle_actual": "Invoice report\n17.25 USD", + "owned_worker_finished": true, + "limits": "Existing host OS/system frameworks/permissions. Fresh dependency/private-home qualification." +} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v3/report.md b/jevdual/results/u3-packet-v4-permission-20261012-v3/report.md new file mode 100644 index 0000000..000a7ca --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v3/report.md @@ -0,0 +1,23 @@ +The repaired packet passed the scoped permission journey, including automatic cleanup. Claude independently reviewed the raw receipts and journal and approved the result; the reviewer reports running Opus 5.5. The final worker-absence check discharged its only condition. + +The measured artifact is packet `205577ee0848e9e68c8a15a9f25e1d308e820a054be449f631274ba7a9c7151d`, frozen from source `445a348ff31243c2f3d90baae9ce209d89bd7eac`. The wheel is unchanged. All eight helper hashes still match after execution. Raw evidence is retained in `output/u3-export-v3/evidence-v3.zip` (SHA256 `ca81a5c1b2f103e844f099bf2fa426623b099ef54e7f26af617dd94ac2a8914e`) and its two-member supplement (SHA256 `2eee22a345b48000a953c8f4199454a2506524daf5086f1bd1ad4c0e3b1697d7`). Both bounded receivers closed after transfer. Sanitized receipts accompany this report. + +| Required gate | Observed result | +| --- | --- | +| New isolated dependencies | Seven installation stages passed; empty cache, private HOME, installed origins | +| Supported Terminal permission reset | Reset succeeded at 19:31:47 UTC | +| Denied baseline before grant | Strict doctor exited 1; fixture refused with exit 2 before resource creation; eight baseline hashes match | +| Captured approved grant | Terminal OFF, one authorized agent click, actual password dialog completed by the human, then ON | +| Responsible host restart | Terminal visibly quit and relaunched; restart recorded at 19:40:10 UTC | +| Strict doctor after relaunch | Exit 0, Accessibility granted, Screen Recording not granted | +| Installed native effect | Exact `Invoice report` plus `17.25 USD` AX oracle; installed worker and private HOME observed | +| Durable replay and completion | Same-token server restart returned the same operation; one `final/applied/sent` journal row; host completion accepted | +| Automatic cleanup | One SIGINT; exact document `window_disappeared`; browser and listener confirmed; fixture exit 0 | +| Independent resource absence | Fixture 1355, browser 1356 and worker 2248 absent; listener 49152 refused connections | +| Independent review | Claude messages 1791834939955400000 and 1791835064888832000 approve all scoped gates | + +This qualifies a supported permission reset followed by the captured grant on one previously provisioned, SIP-enabled macOS 26.6.2 guest, using Terminal, Chrome 155 and Python 3.14.7. It does not qualify a never-granted cold OS, first Chrome launch, other responsible hosts, other OS versions, MDM or pixel capture. Grant timing is bracketed by clock observations, not an exact-second action trace. The agent clicked the switch; the human entered authentication. No manual document close or additional Automation/Screen Recording grant occurred. + +File snapshots changed from 15 to 24 bytes during fixture closure; disk persistence remains `not_checked`. The absent close-confirmation preference key does not establish the effective preference. Original U3v1 remains failed because its cleanup confirmation failed. UF5 remains inconclusive because the VM interruption separated denial from an uncaptured ON state. Final UF6 uses its own consecutive denial/grant records; earlier preliminary logging errors are retained separately. + +The repair retries only invalid AX attribute observations after an already issued close. Fresh readable window absence is still required, and no close is replayed. The earlier browser startup fix keeps readiness and unique-window identity in separate bounded phases. Boundary checks cover delayed startup, exit, ambiguity, stale reads and identity changes; the live run demonstrates ordinary readiness and exact cleanup, not exhaustive failure behavior. diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v3/review.json b/jevdual/results/u3-packet-v4-permission-20261012-v3/review.json new file mode 100644 index 0000000..4b07d6b --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v3/review.json @@ -0,0 +1,29 @@ +{ + "reviewer": "Claude through authorized mailbox", + "model_attestation": "claude-opus-5-5 (reviewer-reported)", + "initial_message": "1791834939955400000", + "final_message": "1791835064888832000", + "verdict": "approved; worker absence condition discharged", + "inspected": [ + "raw archive digest", + "packet/source identity", + "denied baseline and timing", + "grant actor and authentication", + "Terminal relaunch", + "installed server and worker origins", + "exact oracle", + "same-token replay", + "single read-only durable operation", + "accepted completion report", + "automatic cleanup", + "independent owned process/listener absence", + "eight unchanged helper hashes" + ], + "claim_limits": [ + "supported permission reset then captured grant on previously provisioned guest", + "Terminal/macOS26.6.2/Chrome155/Python3.14.7 only", + "disk persistence not checked", + "Screen Recording/pixel routes not qualified", + "earlier failed and interrupted runs not regraded" + ] +} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v3/startup.json b/jevdual/results/u3-packet-v4-permission-20261012-v3/startup.json new file mode 100644 index 0000000..957fcbb --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v3/startup.json @@ -0,0 +1,5 @@ +{ + "status": "ready", + "pid": 1356, + "port": 49153 +} diff --git a/jevdual/results/u3-packet-v4-permission-20261012-v3/summary.json b/jevdual/results/u3-packet-v4-permission-20261012-v3/summary.json new file mode 100644 index 0000000..44ae15b --- /dev/null +++ b/jevdual/results/u3-packet-v4-permission-20261012-v3/summary.json @@ -0,0 +1,75 @@ +{ + "passed": true, + "runtime_gates_passed": true, + "independent_review": "approved", + "classification": "fresh dependencies and permission state on previously provisioned guest", + "packet_sha256": "205577ee0848e9e68c8a15a9f25e1d308e820a054be449f631274ba7a9c7151d", + "source_commit": "445a348ff31243c2f3d90baae9ce209d89bd7eac", + "raw_archive_sha256": "ca81a5c1b2f103e844f099bf2fa426623b099ef54e7f26af617dd94ac2a8914e", + "baseline_hashes_match": { + "reset-v3.txt": true, + "doctor-denied-v3-exit.txt": true, + "doctor-denied-v3-stderr.txt": true, + "denied-fixture-v3-console.txt": true, + "denied-fixture-v3-exit.txt": true, + "footwork/output/doctor-denied-v3.json": true, + "footwork/output/install-v1/receipt.json": true, + "no-resources-v3.txt": true + }, + "durable_rows": [ + [ + "op_c16fcfb1b382", + "final", + "applied", + "sent" + ] + ], + "install": true, + "denied_baseline": true, + "observed_approved_grant": true, + "grant_actors": [ + "Codex switch click", + "human guest authentication" + ], + "terminal_restart": true, + "strict_doctor_after": true, + "native": true, + "automatic_document_confirmation": true, + "fixture_exit": 0, + "manual_document_close": false, + "browser_listener_cleanup": true, + "owned_worker_absent": true, + "disk_persistence": "not_checked", + "previous_runs": { + "U3v1": "failed", + "UF5": "inconclusive after VM interruption" + }, + "frozen_helper_hashes_match": { + "jevdual/scripts/capability_clean_install.py": true, + "jevdual/scripts/capability_fixture_config.py": true, + "jevdual/scripts/capability_fixture.py": true, + "jevdual/scripts/capability_host_trial.py": true, + "jevdual/scripts/capability_installed_trial.py": true, + "jevdual/scripts/capability_interrupt_trial.py": true, + "jevdual/scripts/capability_restart_trial.py": true, + "jevdual/scripts/verify_onboarding_packet.py": true + }, + "supplemental_archive_sha256": "2eee22a345b48000a953c8f4199454a2506524daf5086f1bd1ad4c0e3b1697d7", + "reviewer_model": "claude-opus-5-5 (reviewer-reported)", + "review_messages": [ + "1791834939955400000", + "1791835064888832000" + ], + "qualified_scope": "supported Terminal permission reset then captured grant on one previously provisioned SIP-enabled macOS26.6.2 guest", + "host": "Terminal", + "os": "macOS26.6.2/25G83", + "architecture": "arm64", + "python": "3.14.7", + "limitations": [ + "not never-granted cold OS or first Chrome launch", + "not other responsible hosts/OS/MDM", + "not pixel capture", + "disk persistence not checked", + "failed U3v1 and interrupted UF5 preserved" + ] +} diff --git a/jevdual/results/versioned-application-fulfillment-20261010/browser-fulfilled.jpg b/jevdual/results/versioned-application-fulfillment-20261010/browser-fulfilled.jpg new file mode 100644 index 0000000..f1224e5 Binary files /dev/null and b/jevdual/results/versioned-application-fulfillment-20261010/browser-fulfilled.jpg differ diff --git a/jevdual/results/versioned-application-fulfillment-20261010/browser-result.json b/jevdual/results/versioned-application-fulfillment-20261010/browser-result.json new file mode 100644 index 0000000..1e15a26 --- /dev/null +++ b/jevdual/results/versioned-application-fulfillment-20261010/browser-result.json @@ -0,0 +1,58 @@ +{ + "source_commit": "bd896e1786406dc06cab65dcb9255934fd5ad196", + "classification": "actual isolated browser + root image-guided host proposals + native simulation + separate persisted SQLite readback", + "app_id": "609f629c251e40a29b2db363cb2d9f32", + "order_id": "desk-stage-001", + "leased_revision": 1, + "fulfilled_revision": 2, + "effect_count": 1, + "moves": 4, + "admitted_actions": 5, + "native_steps": 3484, + "native_contact_samples": 9890, + "public_rgb_generations": [ + 0, + 1, + 2, + 3, + 4 + ], + "final_rgb_center_m": [ + 0.19583333333333333, + 0.005833333333333333 + ], + "application_receipt": { + "app_id": "609f629c251e40a29b2db363cb2d9f32", + "committed_time_unix": 1791675824.074699, + "completion_operation_id": "fulfill-3e04eadf0a0e49cdb0a129db756ed781", + "fulfilled_revision": 2, + "lease_id": "24d56730083145d59b07fa1f4bcf2d4e", + "leased_revision": 1, + "order_id": "desk-stage-001", + "physical_completion_id": "stage-22a96ebafcb4404eb13478fb966f848b", + "physical_proof": { + "accepted_incarnation": "172b048b98204a3487c6902368b424c0", + "artifact_sha256": "6b9e6edded2471349354bec3d632bf81e186ea9c191d582ea68f2c22615f4d9d", + "controller_incarnation": "172b048b98204a3487c6902368b424c0", + "generation": 4, + "historical": true, + "image_sha256": "0f943c8669de3e35d64936bc01afef80a792095df2233592d567086edf6584c9", + "operation_id": "stage-22a96ebafcb4404eb13478fb966f848b", + "source_kind": "mujoco_simulation", + "state_sha256": "95f9c3e2c3c6113f12f5eb486443b8010a9382b3f8972d3e2450b4a1130ca495", + "verified_time_unix": 1791675824.0734122 + }, + "state": "fulfilled", + "work_order_sha256": "3f93b8004097cd730b72a4d9a78523d42f0cf6057d5e1a4a0e1b8b8eb98baada" + }, + "browser_reload_persisted": true, + "evidence_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/b5-browser-20261010", + "screenshot": "browser-fulfilled.jpg", + "cleanup": { + "app_stopped": true, + "controller_process_stopped": true, + "controller_socket_absent": true, + "tab_closed": true, + "native_stop_claim": false + } +} diff --git a/jevdual/results/versioned-application-fulfillment-20261010/report.md b/jevdual/results/versioned-application-fulfillment-20261010/report.md new file mode 100644 index 0000000..08d6a61 --- /dev/null +++ b/jevdual/results/versioned-application-fulfillment-20261010/report.md @@ -0,0 +1,111 @@ +# SIM5: Versioned application and simulated physical fulfillment + +The corrected implementation completed the actual browser/physical/application loop and +all seven independent integration scenarios on frozen commit `bd896e1`. The affected +95 checks passed. This is a local diagnostic, not hardware, production browser governance +or autonomous policy qualification. Source, outcomes and verification are recorded in +`source-freeze.json`, `summary.json`, `browser-result.json` and `verification.json`. + +## What connects + +A Python/SQLite loopback app records an order and its revision. The browser binds one +immutable lease for that revision, including the exact task and stable physical/application +completion identities. A separate handoff CLI owns or reconnects the assigned controller +through public commands. The agent observes rendered RGB and supplies bounded proposals. +The app verifies the assigned staged history, current image/task binding and saved artifact, +then updates the same still-open revision and saves one fulfillment receipt in a transaction. + +Editing or cancelling advances the revision. A guard withholds subsequent submissions and +requests cooperative cancellation of an owned active operation. Previously admitted motion +can be partial. The guard must be called by the host; this package has no background watcher +or atomic transaction joining native motion and application state. + +## Development evidence + +The earlier controller/body/vision files retain their SIM4 hashes. Their 68 affected tests +passed: 18 process/controller tests and 50 workcell, image-guided, case and episode tests. +Commands and timings are recorded in `verification.json`. + +Root source review found that a cached browser checked the responding app identity after +POST, leaving same-origin replacement writes unbound to the original app. Prediction +`46aa8f4` preceded the real mutation regression. Both pre-fix subcases failed: an absent +expected app identity edited the row, and a valid foreign app identity cancelled it. Private +before/after rows and the failed parent result remain at +`tools/physical-workcell/output/a56bb315a/conformance.json`. + +The corrected server requires one exact `X-Footwork-App-ID` before every mutation. The +browser sends its retained app identity. Valid order/lease references and exact Origin/Host +are separate requirements. + +The first malformed-receipt attempt stopped before its intended check when an overlapping +UI change invalidated the bound source. A fresh fixture then exposed three falsely accepted +reconciliation variants after controlled private-store corruption: boolean leased revision, +cancelled current order and a current revision older than the receipt. Both logs remain under +`output/h5-receipt-before-type-fix*/`. The final adapter requires strict typed receipt fields, +the same canonical task and a fulfilled current order at or after the recorded revision. + +Final app conformance passed 16 tests in 38.746 seconds; final adapter conformance passed +11 tests in 37.086 seconds. Their exact source/evidence paths are in `verification.json`. +Earlier controller regressions account for the other 68 checks and retain the unchanged +SIM4 hashes. Ruff, JavaScript syntax and notebook checks passed. + +## Independent evidence + +Root drove an isolated in-app browser against the frozen local app. It read and bound +`desk-stage-001` revision 1. Root inspected native RGB generations 0 through 4 and chose +four bounded moves from the public visual packet. Native receipts recorded 9,890 contact +samples over 3,484 steps. The final image-derived block center was approximately +(0.196, 0.006) m inside the requested region. A fifth admitted completion action staged +the digest-bound software artifact and passed Footwork's current saved-byte check. + +The browser's **Verify and fulfill** action then independently checked physical evidence +and saved fulfilled revision 2. Separate HTTP and read-only SQLite verification found +exactly one application receipt for the same app/order/lease/operation. Browser reload +preserved fulfilled revision 2. The screenshot shows the result and actual rendered RGB; +it is evidence from the completed trial, not a current live camera feed. + +![Verified browser fulfillment](browser-fulfilled.jpg) + +The independent driver used public app/controller/handoff APIs, a deterministic RGB planner +and separate read-only SQLite scoring. It reused identified frozen transport helpers but +did not call unit-test scenarios. Its seven first attempts passed with unchanged source: + +| Scenario | Persisted outcome | Fulfillment effects | +| --- | --- | ---: | +| Normal RGB contact and fulfillment | Revision 4, fulfilled | 1 | +| Edit during admitted motion | Revision 4, open; stale completion refused | 0 | +| Cancel during admitted motion | Revision 4, cancelled | 0 | +| Lost HTTP reply after commit | Original receipt reconciled and repeated after controller stop | 1 | +| Wrong identities, conflicting IDs, client flags/paths, unsupported evidence | Revision 3, open; refused | 0 | +| Same-origin app and controller restart | Unchanged physical generation/state, retained history, revision 4 fulfilled | 1 | +| Changed current artifact with staged history | Revision 3, open; new fulfillment refused | 0 | + +Edit/cancel stopped after 179/186 native steps respectively; each verified retained state +matched a fresh public observation. The driver preserved the actual lost-reply socket event +and conflicting committed-ID refusal. Raw evidence remains in +`tools/physical-workcell/output/s5-independent-20261010-v1/qualification.json`, with its SHA256 +in `summary.json`. Root's separate browser evidence remains in `output/b5-browser-20261010/`. + +All trial-owned app/controller processes exited and owned controller sockets disappeared. +The temporary browser tab closed; persisted order state and screenshots remain. Process +exit is a cleanup observation, separate from the verified native cancellation stop checks. + +## Limits and next boundary + +Physical proof is checked before the application transaction. Its verification timestamp +and the application's commit timestamp describe that boundary; a later physical/artifact +change cannot be excluded atomically. Historical application receipts remain separate from +current physical integrity. Same-app restart preserves its exact port; origin rebinding and +automatic takeover of externally restarted controllers are unsupported. + +The controller still operates one known-color block and pusher in a bounded MuJoCo scene. +Native camera perception, general robot manipulation, multiple agents sharing real hardware, +production capability governance and demand from an operator remain untested. + +Each lease owns a separate simulated episode. Successive orders or edited targets on one +persistent physical body, with resource ownership independent of task identity, are the next +useful boundary. This run does not qualify that transition or automatically continue an +interrupted physical job under a new order revision. + +Preregistration: `../../docs/experiments/SIM5-versioned-application-fulfillment.md`. +Notebook: `../../docs/notebook/2026-10-10-versioned-application-fulfillment.md`. diff --git a/jevdual/results/versioned-application-fulfillment-20261010/source-freeze.json b/jevdual/results/versioned-application-fulfillment-20261010/source-freeze.json new file mode 100644 index 0000000..4f53e27 --- /dev/null +++ b/jevdual/results/versioned-application-fulfillment-20261010/source-freeze.json @@ -0,0 +1,30 @@ +{ + "source_commit": "bd896e1786406dc06cab65dcb9255934fd5ad196", + "corrected_prediction_commit": "71600c1", + "original_prediction_commit": "07346c0", + "freeze_time": "2026-10-10T19:41:25-04:00", + "screen_before_browser_trial": { + "IOConsoleLocked": false, + "on_console": true + }, + "python": "3.12.6", + "mujoco": "3.15.0", + "sha256": { + "order_app.py": "006939326f54c76fea99c6c73f98692efb5fdc27e4f0615181013f7d27f2c5f7", + "order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "order_app.html": "c8c286cad1d5d1aa795d48c8d9ae39f20292d7a3f08cb9f85fbc5789adfcc56a", + "app_handoff.py": "1af6b2bd78021d96632e51aa1bb5b5c8ccbb2ed84568fa05690369b2583bb187", + "controller.py": "29131e49c0b19ec59b957cf52a56aa573242ea3d3ee6851ebf4627c52a8c1bfd", + "workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "episode_runner.py": "530c8e65d2b4c613e3c02c8dcc5e9f8d56559e5f119b011bb3e138667b1474e7", + "episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "proposal.schema.json": "4f632336093c6b9d81741a4deff879b07eea7c9290eb34cbfa75ed84359b90e6", + "qualify_order_handoff.py": "bae7d9d668422d3f77b9390aa3e6fa4497fca2cbac90b5f5d1e6f65ec54eda08", + "test_order_app.py": "a8257e6b8572ace9721bddaeff037996e58a7e18637e674d00688d530434047a", + "test_app_handoff.py": "2ebabb984d75543adf5044551a78bc799ffb34157eeaea10cfaf9b7e13a71865", + "ORDER_APP.md": "75ae4263e16e1d6eda6974e6010141dc4eddbf2c766baf4391b42a2459e9b168", + "README.md": "1b01239948d1aa72496fc1f28ff379433217111d30831ccdf79c9ec46b141b5e" + } +} diff --git a/jevdual/results/versioned-application-fulfillment-20261010/summary.json b/jevdual/results/versioned-application-fulfillment-20261010/summary.json new file mode 100644 index 0000000..c093ffa --- /dev/null +++ b/jevdual/results/versioned-application-fulfillment-20261010/summary.json @@ -0,0 +1,112 @@ +{ + "source_commit": "bd896e1786406dc06cab65dcb9255934fd5ad196", + "attempted": 7, + "passed": 7, + "source_unchanged": true, + "raw_evidence": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/s5-independent-20261010-v1/qualification.json", + "raw_sha256": "647d6dab77eff0d5225d5df5802e58c83a076e2500d01da395c5911c4ee73df4", + "classification": "independent public API/adapter/native integration diagnostics; no AI benchmark", + "cases": [ + { + "case_id": "01-normal-http-native", + "passed": true, + "expected_outcome": "fulfilled", + "app_revision": 4, + "app_state": "fulfilled", + "effect_count": 1, + "native_steps_before_cancel": null, + "verified_stop": null, + "all_parent_handles_exited": true, + "adapter_stop_observed": true, + "socket_absent": true + }, + { + "case_id": "02-edit-during-motion", + "passed": true, + "expected_outcome": "stale_fulfillment_refused", + "app_revision": 4, + "app_state": "open", + "effect_count": 0, + "native_steps_before_cancel": 179, + "verified_stop": true, + "all_parent_handles_exited": true, + "adapter_stop_observed": true, + "socket_absent": true + }, + { + "case_id": "03-cancel-during-motion", + "passed": true, + "expected_outcome": "stale_fulfillment_refused", + "app_revision": 4, + "app_state": "cancelled", + "effect_count": 0, + "native_steps_before_cancel": 186, + "verified_stop": true, + "all_parent_handles_exited": true, + "adapter_stop_observed": true, + "socket_absent": true + }, + { + "case_id": "04-lost-app-reply", + "passed": true, + "expected_outcome": "one_effect_historical_reconciliation", + "app_revision": 4, + "app_state": "fulfilled", + "effect_count": 1, + "native_steps_before_cancel": null, + "verified_stop": null, + "all_parent_handles_exited": true, + "adapter_stop_observed": true, + "socket_absent": true + }, + { + "case_id": "05-identity-and-authority", + "passed": true, + "expected_outcome": "wrong_identity_flags_paths_and_unsupported_evidence_refused", + "app_revision": 3, + "app_state": "open", + "effect_count": 0, + "native_steps_before_cancel": null, + "verified_stop": null, + "all_parent_handles_exited": true, + "adapter_stop_observed": true, + "socket_absent": true + }, + { + "case_id": "06-restart-history", + "passed": true, + "expected_outcome": "same_identity_history_no_redispatch", + "app_revision": 4, + "app_state": "fulfilled", + "effect_count": 1, + "native_steps_before_cancel": null, + "verified_stop": null, + "all_parent_handles_exited": true, + "adapter_stop_observed": true, + "socket_absent": true + }, + { + "case_id": "07-current-artifact", + "passed": true, + "expected_outcome": "historical_stage_current_artifact_refuses_new_fulfillment", + "app_revision": 3, + "app_state": "open", + "effect_count": 0, + "native_steps_before_cancel": null, + "verified_stop": null, + "all_parent_handles_exited": true, + "adapter_stop_observed": true, + "socket_absent": true + } + ], + "actual_browser": { + "passed": true, + "effect_count": 1, + "fulfilled_revision": 2 + }, + "failures_preserved": [ + "tools/physical-workcell/output/a56bb315a/conformance.json", + "tools/physical-workcell/output/h5-receipt-before-type-fix/unittest.log", + "tools/physical-workcell/output/h5-receipt-before-type-fix-v2/unittest.log" + ] +} diff --git a/jevdual/results/versioned-application-fulfillment-20261010/verification.json b/jevdual/results/versioned-application-fulfillment-20261010/verification.json new file mode 100644 index 0000000..2159e7b --- /dev/null +++ b/jevdual/results/versioned-application-fulfillment-20261010/verification.json @@ -0,0 +1,59 @@ +{ + "phase": "complete", + "prior_controller_regressions": { + "controller_tests": 18, + "controller_seconds": 34.144, + "other_physical_tests": 50, + "other_seconds": 15.742, + "result": "passed" + }, + "prior_runtime_hashes_unchanged": true, + "commands": [ + { + "command": "tools/physical-workcell/.venv/bin/python -m unittest discover -s tools/physical-workcell -p test_controller.py", + "cwd": "/Users/thomasmain/projects/footwork" + }, + { + "command": ".venv/bin/python -m unittest test_workcell test_image_guided test_episode_cases test_episode_runner", + "cwd": "/Users/thomasmain/projects/footwork/tools/physical-workcell" + } + ], + "browser_trial": "passed", + "independent_trials": "7/7 passed", + "backend_conformance": { + "corrected_full_suite_tests": 16, + "seconds": 37.054, + "result": "passed", + "pre_fix_identity_regression": "tools/physical-workcell/output/a56bb315a/conformance.json", + "pre_fix_identity_regression_result": "failed; two effects before app identity checking", + "final_suite_tests": 16, + "final_seconds": 38.746, + "final_source_suite": "tools/physical-workcell/output/s5backend-final-f2190c5/suite-result.json" + }, + "adapter_conformance": { + "final_suite_tests": 11, + "seconds": 37.086, + "result": "passed", + "log": "tools/physical-workcell/output/h5-adapter-final-suite/unittest.log", + "previous_failed_attempts": [ + "tools/physical-workcell/output/h5-receipt-before-type-fix/unittest.log", + "tools/physical-workcell/output/h5-receipt-before-type-fix-v2/unittest.log" + ] + }, + "total_affected_checks": 95, + "lint": "Ruff passed", + "browser_cleanup": { + "app_stopped": true, + "controller_process_stopped": true, + "controller_socket_absent": true, + "tab_closed": true, + "native_stop_claim": false + }, + "notebook_check_exit_code": 0, + "result_redaction_files_changed": 0, + "runtime_source_unchanged": true, + "screen_at_close": { + "IOConsoleLocked": false, + "on_console": true + } +} diff --git a/jevdual/results/workbench-20261010/context-drift.json b/jevdual/results/workbench-20261010/context-drift.json new file mode 100644 index 0000000..0ba0273 --- /dev/null +++ b/jevdual/results/workbench-20261010/context-drift.json @@ -0,0 +1,10 @@ +{ + "state": "recorded", + "historical_recorded_outcome": "recorded", + "context_state": "changed", + "recorded_git_head": "419a6772ece9551bd4f136bd99291eae9b2082de", + "current_git_head_at_observation": "49904e361f37f4d87f985b4cd8d73a21d8b4527c", + "references_unchanged": true, + "artifact_checks_passed": true, + "changed_context_does_not_imply_changed_record": true +} diff --git a/jevdual/results/workbench-20261010/live-link.json b/jevdual/results/workbench-20261010/live-link.json new file mode 100644 index 0000000..f06b83a --- /dev/null +++ b/jevdual/results/workbench-20261010/live-link.json @@ -0,0 +1,88 @@ +{ + "source_commit": "419a677", + "run": "tools/workbench-notes/output/footwork/camera-experiment-001", + "project": { + "label": "footwork", + "namespace": "codex_app", + "project_id": "abe2a667-dedc-4f3f-9863-62f3d80188bd", + "root": "/Users/thomasmain/projects/footwork" + }, + "source_kind": "native_camera_frame", + "reviewed_note_sha256": "6d4fb067193c2c1f48d3e57c130fee9cf9540651c7b77946e020b13a8e71847c", + "source_note_bytes_unchanged": true, + "metadata_authorship": "caller_supplied", + "proposed_check_outcome": "not_run", + "fresh_process_repeat_status": "same manifest and artifacts; recorded with passing checks", + "project_filter_list_count": 1, + "independent_artifact_checks": { + "json": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 3286, + "expected_file": "/Users/thomasmain/projects/footwork/tools/workbench-notes/output/footwork/camera-experiment-001/expected-record.json", + "expected_sha256": "3225dd83166a1e05015e718addf7cbf7e2b5cabf556f1f972bb5f95805ea1192", + "limit_bytes": 1048576, + "observed_bytes": 3286, + "observed_sha256": "3225dd83166a1e05015e718addf7cbf7e2b5cabf556f1f972bb5f95805ea1192", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/workbench-notes/output/footwork/camera-experiment-001/record.json", + "source_sha256": "3225dd83166a1e05015e718addf7cbf7e2b5cabf556f1f972bb5f95805ea1192" + }, + "md": { + "claims": [ + "current_artifact_byte_equality" + ], + "detail": "current bytes equal the digest-bound expected source", + "expected_bytes": 2557, + "expected_file": "/Users/thomasmain/projects/footwork/tools/workbench-notes/output/footwork/camera-experiment-001/expected-record.md", + "expected_sha256": "963388580ad2de282bbb25bc90507e99e8d29ba027a555b92014119e3884f397", + "limit_bytes": 1048576, + "observed_bytes": 2557, + "observed_sha256": "963388580ad2de282bbb25bc90507e99e8d29ba027a555b92014119e3884f397", + "ok": true, + "path": "/Users/thomasmain/projects/footwork/tools/workbench-notes/output/footwork/camera-experiment-001/record.md", + "source_sha256": "963388580ad2de282bbb25bc90507e99e8d29ba027a555b92014119e3884f397" + } + }, + "context_at_initial_link": { + "current_git_head": "419a6772ece9551bd4f136bd99291eae9b2082de", + "head_state": "unchanged", + "recorded_git_head": "419a6772ece9551bd4f136bd99291eae9b2082de", + "references": [ + { + "bytes": 2091, + "line": 14, + "path": "tools/desk-inbox-experiment/recognize.swift", + "recorded_sha256": "340d9969f84f4bc892483c2a7fd4786f6bf4a6fef964f9cf6ba9b956389d2b85", + "sha256": "340d9969f84f4bc892483c2a7fd4786f6bf4a6fef964f9cf6ba9b956389d2b85", + "state": "unchanged" + }, + { + "bytes": 3781, + "line": 1, + "path": "jevdual/docs/experiments/DI1-desk-paper-inbox.md", + "recorded_sha256": "6d7ad55ccb422a10d6db9ab478d4d1226844030b8bb6dd9029998cf6857db0fd", + "sha256": "6d7ad55ccb422a10d6db9ab478d4d1226844030b8bb6dd9029998cf6857db0fd", + "state": "unchanged" + }, + { + "bytes": 5028, + "line": 1, + "path": "jevdual/results/desk-inbox-20261010/report.md", + "recorded_sha256": "1612860abe55216f7b0bda454eb5fb8ad16e485b4c49421b52ac2c1a3e481e90", + "sha256": "1612860abe55216f7b0bda454eb5fb8ad16e485b4c49421b52ac2c1a3e481e90", + "state": "unchanged" + } + ], + "state": "unchanged" + }, + "no_new_camera_ocr_or_action_execution": true, + "limits": [ + "Catalog is explicit caller-supplied metadata, not an authenticated project service", + "Context snapshot does not certify current future actions", + "No remote task/project write or physical actuation", + "Private note and image contents excluded from committed report" + ] +} diff --git a/jevdual/results/workbench-20261010/report.md b/jevdual/results/workbench-20261010/report.md new file mode 100644 index 0000000..d7426ba --- /dev/null +++ b/jevdual/results/workbench-20261010/report.md @@ -0,0 +1,15 @@ +# Project-linked workbench handoff + +The user's previously reviewed live paper note is now linked to the exact Codex Footwork project and canonical checkout. Frozen source 419a677 writes one JSON/Markdown packet; both pass independent public Footwork check-artifact readback. Fresh-process link/status repeat preserves the same manifest, source note and packets. Project-filtered listing returns one record. Source bytes equal the prior approved note, and caller interpretation is separate from physical evidence. + +The packet records repository HEAD and three bounded reference hashes, a question, a proposed experiment and an expected result. Its proposed-check outcome is not_run. It performs no camera capture, OCR, task creation or physical action. Local catalog strings are caller-supplied explicit metadata, not authenticated project-service identity. Images/note contents stay private under tools/workbench-notes/output/footwork/camera-experiment-001/ and are excluded from this report. + +All 15 package tests (plus 11 subtests) and Ruff pass. Cases cover exact IDs/duplicate labels, project filtering, source/metadata/digest conflicts, excluded/escaping refs, locks, partial publication/lost receipt/no replay, tamper/missing packets, malformed manifests, and context drift independent of saved-byte validity. Live evidence and hashes: live-link.json; source identity/commands: source.json. + +Route samples on unchanged camera/desk source 6e93c35: PNG read/hash median1.276ms (30 samples), direct note check0.178ms (30), fresh public Footwork check201.365ms (5), public desk status286.204ms (3), Swift process plus real Vision printed-fixture OCR451.520ms (3, warm module cache). Ranges and input hashes are retained in route-profile.json. This supports prioritizing process/helper startup over a Rust port of small-note plumbing. It is not a Rust speedup comparison, sustained-video benchmark or human-review timing. + +Context was unchanged at initial link. Subsequent documentation commits advance HEAD, so status should report context changed separately while preserving verified artifacts. This is historical source/context evidence rather than authority to execute a later action. + +The user subsequently prioritized physical applications mixed with computer actions, and chose simulation because no robot hardware is available. Workbench notes remain supporting provenance. The next active experiment is a MuJoCo body/controller loop with independent physical-state and computer-result checks; the earlier paper-review proposal is not automatically executed. + +See ../../docs/plans/workbench-notes-directions.md for project grounding and language boundaries. No remote workspace or other repository was written. diff --git a/jevdual/results/workbench-20261010/route-profile.json b/jevdual/results/workbench-20261010/route-profile.json new file mode 100644 index 0000000..b86c730 --- /dev/null +++ b/jevdual/results/workbench-20261010/route-profile.json @@ -0,0 +1,143 @@ +{ + "platform": { + "macos": "26.5.1", + "machine": "arm64", + "python": "3.12.6" + }, + "source_commit": "6e93c35", + "hashlib_implementation_module": "_hashlib", + "input_bytes": { + "note": 187, + "image": 2460846, + "ocr_fixture": 561756 + }, + "input_sha256": { + "note": "6d4fb067193c2c1f48d3e57c130fee9cf9540651c7b77946e020b13a8e71847c", + "image": "afd69ee24f1252dccbabd9956717106447927db2044ce596f769c53f61a5327c", + "ocr_fixture": "740cfe0c4055553b732356cbee75c7797919fc3f3624d9e8f9459a626609edeb" + }, + "source_sha256": { + "jevdual/python/jevdual/artifacts.py": "f2d52806bef26654aa733fc1a8a02079088a08c2c4d4f05f6594880fc648ef8a", + "tools/desk-inbox-experiment/inbox.py": "13aeee66b25cd11b6a741e6798206c5f154bcb4d1605fd41dd44f6b99776e51b", + "tools/desk-inbox-experiment/recognize.swift": "340d9969f84f4bc892483c2a7fd4786f6bf4a6fef964f9cf6ba9b956389d2b85" + }, + "routes": { + "read_and_sha256_image": { + "samples": 30, + "median_ms": 1.2763339909724891, + "min_ms": 0.9975000284612179, + "max_ms": 1.8007910111919045, + "all_ms": [ + 1.6405419446527958, + 1.8007910111919045, + 1.2044579489156604, + 1.3167919823899865, + 1.3429169775918126, + 1.3137080240994692, + 1.2108329683542252, + 1.1927500599995255, + 1.3616250362247229, + 1.4715829165652394, + 1.4194590039551258, + 1.2797090457752347, + 1.148959039710462, + 1.3415409484878182, + 1.3436670415103436, + 1.2729589361697435, + 1.152500044554472, + 1.1530420742928982, + 1.0919590713456273, + 1.3101670192554593, + 1.377625041641295, + 1.3415409484878182, + 1.1114169610664248, + 1.0919999331235886, + 1.0371250100433826, + 1.0492079891264439, + 1.0147079592570662, + 0.9975000284612179, + 1.0672910138964653, + 1.2933750404044986 + ] + }, + "exact_artifact_library_check": { + "samples": 30, + "median_ms": 0.1778959995135665, + "min_ms": 0.06574997678399086, + "max_ms": 0.7060830248519778, + "all_ms": [ + 0.7060830248519778, + 0.4146250430494547, + 0.3814579686149955, + 0.1859160838648677, + 0.06574997678399086, + 0.06858294364064932, + 0.4283749731257558, + 0.09424996096640825, + 0.08554104715585709, + 0.5015840288251638, + 0.22429192904382944, + 0.23779203183948994, + 0.12391700875014067, + 0.138165894895792, + 0.11266698129475117, + 0.11237501166760921, + 0.21816696971654892, + 0.38054201286286116, + 0.10308297351002693, + 0.11824991088360548, + 0.24733389727771282, + 0.23333297576755285, + 0.17966702580451965, + 0.17612497322261333, + 0.09966699872165918, + 0.09895802941173315, + 0.07825007196515799, + 0.2558750566095114, + 0.2854579361155629, + 0.08066603913903236 + ] + }, + "public_footwork_check": { + "samples": 5, + "median_ms": 201.3647089479491, + "min_ms": 191.8711659964174, + "max_ms": 327.03166699502617, + "all_ms": [ + 327.03166699502617, + 191.8711659964174, + 201.3647089479491, + 299.20416697859764, + 196.62716705352068 + ] + }, + "public_desk_status": { + "samples": 3, + "median_ms": 286.2039579777047, + "min_ms": 285.03337502479553, + "max_ms": 313.4212919976562, + "all_ms": [ + 313.4212919976562, + 285.03337502479553, + 286.2039579777047 + ] + }, + "swift_vision_warm_module_cache": { + "samples": 3, + "median_ms": 451.51970791630447, + "min_ms": 448.0627079028636, + "max_ms": 1255.148832919076, + "all_ms": [ + 1255.148832919076, + 451.51970791630447, + 448.0627079028636 + ] + } + }, + "limits": [ + "Local sequential samples; no Rust implementation compared", + "On-demand retained-frame workload; no sustained-video workload measured", + "Swift module cache already warm; process startup and OCR combined", + "No camera opens, provider calls, human-review timing or port speedup claim" + ] +} diff --git a/jevdual/results/workbench-20261010/source.json b/jevdual/results/workbench-20261010/source.json new file mode 100644 index 0000000..7ef3861 --- /dev/null +++ b/jevdual/results/workbench-20261010/source.json @@ -0,0 +1,17 @@ +{ + "workbench_source_commit": "419a677", + "workbench_source_sha256": "db46fb536257218b4c1727dff5d3c349f8fd581eeb378651d6cd414dad0420df", + "test_command": "uv run --no-sync python -m unittest discover -s ../tools/workbench-notes -p 'test_*.py' -v", + "test_cwd": "jevdual", + "tests_passed": 15, + "test_subtests": 11, + "test_exit_code": 0, + "ruff_exit_code": 0, + "profile_driver_sha256": "953e252baaf697ae6b35601893970be31dbe6c73acfedfb6a28ff80c13c982fd", + "observed_backend": { + "paint_order": "native", + "snapshot_lookup": "pure", + "element_hashes": "native", + "evidence_match": "native" + } +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/attempt-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/attempt-ledger.json new file mode 100644 index 0000000..22f2ab1 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/attempt-ledger.json @@ -0,0 +1,33 @@ +{ + "actual_attempts": [ + { + "root": "tools/physical-workcell/output/sim13-body-native-gap-v1", + "source": "38b9b146017c39e593cfedd251df223d754134c9", + "status": "independent scorer failure: no measurement DB before any measurement", + "calls": 1, + "retained_failure": "score-first-failure.log", + "capture_limitation": "v2 capture helper overwrote three v1 offline-capture files; original UI ledger and other v1 captures remain" + }, + { + "root": "tools/physical-workcell/output/sim13-body-native-gap-v2", + "source": "4e2dd74d27ec07f0b5a42039e0f30016402cf050", + "status": "not qualified: capture output root mismatch; recorder refused missing file", + "calls": 1, + "retained_failure": "capture-error.json" + }, + { + "root": "tools/physical-workcell/output/sim13-body-native-gap-v3", + "source": "4e2dd74d27ec07f0b5a42039e0f30016402cf050", + "status": "passed independent safe-block score", + "calls": 1 + } + ], + "other_qualified_attempts": [ + "tools/physical-workcell/output/sim13-host-inflight-v1", + "tools/physical-workcell/output/sim13-body-uncertain-v1", + "tools/physical-workcell/output/sim13-host-uncertain-v1" + ], + "fixture_failure": "tools/physical-workcell/output/sim13-crash-scorer-negative-v1.log", + "publication_gap": "publication-gap-limitation.json", + "total_actual_calls_including_unqualified_attempts": 43 +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-ledger.json new file mode 100644 index 0000000..8867c75 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-ledger.json @@ -0,0 +1,50 @@ +{ + "boundaries": [ + { + "case": "body-native-gap", + "command_id": "command-75be3033b3774dfb8ebee666241a36e8", + "finished_snapshot": "000004-finished.json", + "gate": { + "barrier": "after_native", + "bench_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "command_id": "command-75be3033b3774dfb8ebee666241a36e8", + "diagnostic_source_sha256": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "manifest_generation": 0, + "native_generation": 1, + "native_state_sha256": "3dd31d583a8b7ec3f4a7fa51f403baa50fda8f9839a705a1787505c87464e425", + "native_steps": 1550, + "native_time_s": 4.0999999999997705, + "operation_id": "agent-46e947a0bddd4d00a38bfeb91f91aea5", + "pid": 43977, + "proposal_id": "proposal-209f71c42f7e4b3883abbd052614be41", + "pusher_block_contact_samples": 3164, + "request_sha256": "8329c458648bfa1ef2823681afed093eb9224937e35caf13878d30145a34f1fa", + "version": 1 + }, + "new_epoch": 2, + "offline_snapshot": "000002-offline.json", + "old_epoch": 1, + "pre_snapshot": "000001-before.json", + "process": { + "boot": 1, + "children_after": [], + "children_before": [], + "exit_observed": true, + "group": 43977, + "initialized": true, + "log": "body-01.log", + "pid": 43977, + "port": 51379, + "returncode": -9, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-native-gap-v3/body", + "signal": "SIGKILL", + "stopped_time": 1791835058.545259 + }, + "request_sha256": "8329c458648bfa1ef2823681afed093eb9224937e35caf13878d30145a34f1fa", + "restored_snapshot": "000003-restored.json", + "role": "body" + } + ], + "case": "body-native-gap" +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-restored.png b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-restored.png new file mode 100644 index 0000000..1559db5 Binary files /dev/null and b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-restored.png differ diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-restored.txt b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-restored.txt new file mode 100644 index 0000000..74c0394 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-restored.txt @@ -0,0 +1,72 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51381/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51381/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 54 checkbox Native receipt gap Needs attention, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 55 text Native receipt gap + 56 text Needs attention + 14 button (disabled) Pause, ID: pause + 57 text Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 15 container Description: Body connection, ID: network-card + 58 text Body connected + 59 text Independent body has unresolved effects + 18 button Disconnect, ID: disconnect + 19 button Check receipt, ID: reconcile + 20 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 21 text Test the simulated connection + 22 container Camera observation + 23 text OVERHEAD CAMERA + 60 text Last observation · Native receipt gap + 53 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 26 text Orange part Blue pusher Green task region + 61 text Captured · generation 0 + 62 text OUTCOME UNCERTAIN + 63 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 30 container Job progress + 31 text 1 Prepare 2 Measure 3 Reset 4 Ready + 32 container Measurement + 33 heading Measurement, Value: 2, ID: measurement-heading + 34 text Measurement + 64 text Pending + 36 text The part’s visible footprint will be measured after its placement is verified. + 37 container Activity + 38 heading Activity, Value: 2, ID: activity-heading + 39 text Activity + 65 text 17 events + 41 content list activity + 66 container + 67 text 03:57 PM Body restarted 1 -> 2 + 68 container + 69 text 03:57 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 70 container + 71 text 03:57 PM Agent stopped for review Remote end closed connection without response + 72 container + 73 text 03:57 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 74 container + 75 text 03:57 PM Sending a guarded action + 76 container + 77 text 03:57 PM Fresh body check verified The next command uses current body evidence. + 78 container + 79 text 03:57 PM Action proposal accepted Native receipt gap + 80 container + 81 text 03:57 PM Checking the planned action + 82 container + 83 text 03:57 PM Fresh body check verified The next command uses current body evidence. + 84 container + 85 text 03:57 PM AI planning requested Native receipt gap + 86 container + 87 text 03:57 PM Camera observation received Native receipt gap + 88 container + 89 text 03:57 PM Requesting camera observation + 44 button (collapsed) Session details, Secondary Actions: Expand + 45 text Session details + 90 text 1 model requests · Reported tokens: 13,087 input / 175 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51381/ \ No newline at end of file diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-still-blocked.png b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-still-blocked.png new file mode 100644 index 0000000..59cd58a Binary files /dev/null and b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-still-blocked.png differ diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-still-blocked.txt b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-still-blocked.txt new file mode 100644 index 0000000..84b4797 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/crash-still-blocked.txt @@ -0,0 +1,72 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51381/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51381/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 54 checkbox Native receipt gap Needs attention, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 55 text Native receipt gap + 56 text Needs attention + 14 button (disabled) Pause, ID: pause + 57 text accepted intent has no verified terminal proof + 15 container Description: Body connection, ID: network-card + 58 text Body connected + 59 text Independent body has unresolved effects + 18 button Disconnect, ID: disconnect + 19 button Check receipt, ID: reconcile + 20 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 21 text Test the simulated connection + 22 container Camera observation + 23 text OVERHEAD CAMERA + 60 text Last observation · Native receipt gap + 53 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 26 text Orange part Blue pusher Green task region + 61 text Captured · generation 0 + 62 text OUTCOME UNCERTAIN + 63 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 30 container Job progress + 31 text 1 Prepare 2 Measure 3 Reset 4 Ready + 32 container Measurement + 33 heading Measurement, Value: 2, ID: measurement-heading + 34 text Measurement + 64 text Pending + 36 text The part’s visible footprint will be measured after its placement is verified. + 37 container Activity + 38 heading Activity, Value: 2, ID: activity-heading + 39 text Activity + 65 text 18 events + 41 content list activity + 66 container + 67 text 03:58 PM Agent stopped for review accepted intent has no verified terminal proof + 68 container + 69 text 03:57 PM Body restarted 1 -> 2 + 70 container + 71 text 03:57 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 72 container + 73 text 03:57 PM Agent stopped for review Remote end closed connection without response + 74 container + 75 text 03:57 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 76 container + 77 text 03:57 PM Sending a guarded action + 78 container + 79 text 03:57 PM Fresh body check verified The next command uses current body evidence. + 80 container + 81 text 03:57 PM Action proposal accepted Native receipt gap + 82 container + 83 text 03:57 PM Checking the planned action + 84 container + 85 text 03:57 PM Fresh body check verified The next command uses current body evidence. + 86 container + 87 text 03:57 PM AI planning requested Native receipt gap + 88 container + 89 text 03:57 PM Camera observation received Native receipt gap + 44 button (collapsed) Session details, Secondary Actions: Expand + 45 text Session details + 90 text 1 model requests · Reported tokens: 13,087 input / 175 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51381/ \ No newline at end of file diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/final-public.json b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/final-public.json new file mode 100644 index 0000000..b976c10 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/final-public.json @@ -0,0 +1,217 @@ +{ + "active_job_id": "9ec6b77d-78c4-4c3f-89ff-004c016ae4f9", + "activity": [ + { + "intent": "Native receipt gap", + "kind": "queued", + "outcome": "9ec6b77d-78c4-4c3f-89ff-004c016ae4f9", + "seq": 1, + "time": 1791835047.683784 + }, + { + "evidence": { + "adapter_id": "269e54e660064fca9f42f52cc08637cb", + "body_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "52a9c089bd013ee808472a9cc1de933fad029439e485bf9606240fdfb8487b5e" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "start_job", + "seq": 2, + "time": 1791835047.695808 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 3, + "time": 1791835047.712679 + }, + { + "intent": "Native receipt gap", + "kind": "rpc_applied", + "outcome": "start_job: command-663dbaafabb14da3a669b48bb9553570", + "seq": 4, + "time": 1791835047.728537 + }, + { + "evidence": { + "adapter_id": "269e54e660064fca9f42f52cc08637cb", + "body_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "7b8399348d9fa8e6e1e6573bf9d07bdc82482bcdccd7458307c5bc22723a1e4c" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 5, + "time": 1791835047.738585 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 6, + "time": 1791835047.749055 + }, + { + "intent": "Native receipt gap", + "kind": "rpc_applied", + "outcome": "observe: command-9c2e7e53730d4631af8b0a67d75cfae1", + "seq": 7, + "time": 1791835047.939627 + }, + { + "intent": "Native receipt gap", + "kind": "inference", + "outcome": "1", + "seq": 8, + "time": 1791835047.940761 + }, + { + "evidence": { + "adapter_id": "269e54e660064fca9f42f52cc08637cb", + "body_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "1b366e3018a1133393ae3a46e2f8b09a8e431d38771713f144ac24fb3a2ecf31" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 9, + "time": 1791835058.287901 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 10, + "time": 1791835058.300809 + }, + { + "intent": "Native receipt gap", + "kind": "rpc_applied", + "outcome": "propose: command-d95207ee8a044205a894f2c9b82a3090", + "seq": 11, + "time": 1791835058.3190558 + }, + { + "evidence": { + "adapter_id": "269e54e660064fca9f42f52cc08637cb", + "body_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "b884589566fdd766264a460448e106ce2b22d82e7ac991f82ec7571030b0c82c" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 12, + "time": 1791835058.3283992 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 13, + "time": 1791835058.340769 + }, + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 14, + "time": 1791835058.543595 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 15, + "time": 1791835058.706333 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "seq": 16, + "time": 1791835077.872034 + }, + { + "intent": "", + "kind": "body_epoch_changed", + "outcome": "1 -> 2", + "seq": 17, + "time": 1791835077.872892 + }, + { + "intent": "", + "kind": "halt", + "outcome": "accepted intent has no verified terminal proof", + "seq": 18, + "time": 1791835092.821545 + }, + { + "intent": "", + "kind": "halt", + "outcome": "accepted intent has no verified terminal proof", + "seq": 19, + "time": 1791835106.157421 + } + ], + "app_id": "b5e77e75097547d49daef66ce7d92528", + "connection": { + "adapter_id": "269e54e660064fca9f42f52cc08637cb", + "body_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "epoch": 2, + "fault": null, + "reason": "Independent body has unresolved effects", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "accepted intent has no verified terminal proof", + "job_id": "9ec6b77d-78c4-4c3f-89ff-004c016ae4f9", + "label": "Native receipt gap", + "last_intent": "Push the block right so its center enters the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "accepted intent has no verified terminal proof" + }, + "recovery": { + "command_id": "command-75be3033b3774dfb8ebee666241a36e8", + "operation": "dispatch", + "reason": "accepted intent has no verified terminal proof", + "state": "uncertain" + }, + "runner_status": "blocked", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 1, + "input_tokens": 13087, + "output_tokens": 175, + "reported_calls": 1, + "token_usage_complete": true + } +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/hard-crash-score.json b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/hard-crash-score.json new file mode 100644 index 0000000..de1d0cd --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/hard-crash-score.json @@ -0,0 +1,143 @@ +{ + "admissions": [ + { + "command_id": "command-663dbaafabb14da3a669b48bb9553570", + "passed": true, + "proof_event": 2 + }, + { + "command_id": "command-9c2e7e53730d4631af8b0a67d75cfae1", + "passed": true, + "proof_event": 5 + }, + { + "command_id": "command-d95207ee8a044205a894f2c9b82a3090", + "passed": true, + "proof_event": 9 + }, + { + "command_id": "command-75be3033b3774dfb8ebee666241a36e8", + "passed": true, + "proof_event": 12 + } + ], + "body_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "browser": { + "passed": true, + "required_names": [ + "crash-blocked", + "crash-offline", + "crash-restored", + "crash-still-blocked" + ] + }, + "calls": [ + { + "action": "move", + "call": 1, + "camera_generation": 0, + "challenge_id": "challenge-51e89e9c4f4c43a78061a6bc7741637f", + "command_id": "command-75be3033b3774dfb8ebee666241a36e8", + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "job_id": "9ec6b77d-78c4-4c3f-89ff-004c016ae4f9", + "model_pid": 44232, + "native_gap": true, + "passed": true, + "proposal_id": "proposal-209f71c42f7e4b3883abbd052614be41" + } + ], + "case": "body-native-gap", + "completed_cycles": 0, + "crash": { + "case": "body-native-gap", + "command_id": "command-75be3033b3774dfb8ebee666241a36e8", + "new_epoch": 2, + "old_epoch": 1, + "passed": true, + "reconciled_sequence": null, + "request_sha256": "8329c458648bfa1ef2823681afed093eb9224937e35caf13878d30145a34f1fa", + "restored_sequence": 17, + "resume_sequence": null, + "role": "body", + "snapshot_command_count": 4 + }, + "event_cutoff": 19, + "native": { + "after": { + "block_center_in_zone": false, + "block_xyz": [ + 0.06581381720865363, + -8.39496500218506e-11, + 0.03499805666564847 + ], + "external_force_cleared": true, + "generation": 1, + "native_time_s": 4.0999999999997705, + "pusher_home": false, + "pusher_xyz": [ + 0.010816304329139825, + 6.653769644544919e-18, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "3dd31d583a8b7ec3f4a7fa51f403baa50fda8f9839a705a1787505c87464e425" + }, + "before": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11999998822184511, + 1.630059551357049e-11, + 0.03499573045537004 + ], + "external_force_cleared": true, + "generation": 0, + "native_time_s": 1.0000000000000007, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 0.0, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "3a8c7d1b2c047630a067a0590f939c066b6c621349b6021d26609c85b2e4644b" + }, + "marker": { + "barrier": "after_native", + "bench_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "command_id": "command-75be3033b3774dfb8ebee666241a36e8", + "diagnostic_source_sha256": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "manifest_generation": 0, + "native_generation": 1, + "native_state_sha256": "3dd31d583a8b7ec3f4a7fa51f403baa50fda8f9839a705a1787505c87464e425", + "native_steps": 1550, + "native_time_s": 4.0999999999997705, + "operation_id": "agent-46e947a0bddd4d00a38bfeb91f91aea5", + "pid": 43977, + "proposal_id": "proposal-209f71c42f7e4b3883abbd052614be41", + "pusher_block_contact_samples": 3164, + "request_sha256": "8329c458648bfa1ef2823681afed093eb9224937e35caf13878d30145a34f1fa", + "version": 1 + }, + "native_displacement_m": 0.3108163043291398, + "passed": true, + "reason": "Advanced native state without indexed terminal proof remains unknown", + "recovered": false, + "safely_blocked": true + }, + "owned_processes_exited": true, + "passed": true, + "safely_blocked": true, + "scope": "selected process SIGKILL boundaries; actual image-only Codex browser; loopback simulation; no arbitrary power-loss guarantee", + "single_wire_post_every_command": true, + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050", + "source_unchanged": true, + "sqlite": { + "count": 0, + "database_created": false, + "reason": "Native gap occurred before the first computer measurement", + "role": "independent_read_only_sqlite_scorer", + "rows": [] + }, + "transport_command_count": 4, + "version": 1 +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/process-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/process-ledger.json new file mode 100644 index 0000000..a8e772b --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/process-ledger.json @@ -0,0 +1,63 @@ +{ + "app_id": "b5e77e75097547d49daef66ce7d92528", + "body_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "body_origin": "http://127.0.0.1:51379", + "browser_origin": "http://127.0.0.1:51381", + "case": "body-native-gap", + "owned_processes_exited": true, + "planner": "actual_codex_cli_images_over_TCP", + "processes": [ + { + "boot": 1, + "children_after": [], + "children_before": [], + "exit_observed": true, + "group": 43977, + "initialized": true, + "log": "body-01.log", + "pid": 43977, + "port": 51379, + "returncode": -9, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-native-gap-v3/body", + "signal": "SIGKILL", + "stopped_time": 1791835058.545259 + }, + { + "exit_observed": true, + "group": 43979, + "pid": 43979, + "returncode": 0, + "role": "initialization" + }, + { + "boot": 1, + "exit_observed": true, + "group": 43980, + "initialized": false, + "log": "workbench-01.log", + "pid": 43980, + "port": 51381, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-native-gap-v3/operator", + "stopped_time": 1791835114.376377 + }, + { + "boot": 2, + "exit_observed": true, + "group": 45066, + "initialized": false, + "log": "body-02.log", + "pid": 45066, + "port": 51379, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-native-gap-v3/body", + "stopped_time": 1791835114.471107 + } + ], + "ready": true, + "simulation": true, + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050" +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/source-freeze.json b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/source-freeze.json new file mode 100644 index 0000000..a73cb87 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/source-freeze.json @@ -0,0 +1,37 @@ +{ + "commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/body_client.py": "40388c238192d923888c9852b363314801dcf65c7266aadc8c377d3810aeb33d", + "tools/physical-workcell/body_network.py": "95fd21597bcdd9e982a698859cd1219ae4522062e332c50fea233629dbfb343c", + "tools/physical-workcell/body_protocol.py": "1d24066768105c28b5cf6f4d46458e4e1b9d86cbde45ed6ce466b30da7ff974f", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "40fe73e01d97ee1e5cfdb813650b11edadbea29534784ab10cc1ef4e7f4d5418", + "tools/physical-workcell/crash_body.py": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/network_bench_operator.html": "75ca82328967ea65cf3c3f7f7d60c109a4201099ae3a24d3834e0ecf19b4d897", + "tools/physical-workcell/network_bench_operator.py": "9079729ea4e8b56ccdf2a6d187b0c5d79a23a33688748fe55d92d10922500b40", + "tools/physical-workcell/network_fault_proxy.py": "44b0b52f7c213c0f5f93b8997ba72be4e750bc9f1a4f7f6d7b2a8c4f1233b595", + "tools/physical-workcell/network_operator_runtime.py": "001511aa25fef7f1078bce444b9a0f7d0a09d26d4e5fba9e9819ee3ed98cb6b9", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/qualify_network_body.py": "2800cf38afd21b86f8b035c46e994b6f96d966db4ccba1bba703e9e1a22f7c2b", + "tools/physical-workcell/qualify_network_workbench.py": "e5c72acda2ea8fae3658faba907448765df2f1e34e05749fbe85faed28ef003b", + "tools/physical-workcell/qualify_workbench_crashes.py": "da971a4190389414dd2114b86d2b7dd40adb2dba2c16e304782eab362244932e", + "tools/physical-workcell/qualify_workbench_restarts.py": "2bc3edc5ea39b69f3372d4274b757f06472409407a22cd3149dfa52fbf314dcb", + "tools/physical-workcell/record_network_workbench.py": "7604865caa309c7002c01a2384da84eb62d8b22dcd63ab256618c536bc5fedc4", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_network_body.py": "d1f7150a5ff23775664b5656fd21379502ba71175487fd7aadb2b51bffcdaf8c", + "tools/physical-workcell/score_network_workbench.py": "67a53586081fe425bd14b0a0fbbf3d7f0fc1e66daccda06765bb88f3d9d4d84c", + "tools/physical-workcell/score_workbench_crashes.py": "10bec02c29afdb9963006884fd9ce310a52be8ba0995698cc11f5d6e4475b998", + "tools/physical-workcell/score_workbench_restarts.py": "181cbf4ee1bbd5483f38edc4a84244fc3efa98dfd21b5c9751d43edfe8f9f5a5", + "tools/physical-workcell/test_workbench_crash_harness.py": "b5a1398f9ccec220731f30c9f082260e378743f6fa6c3d954f008c2e79aeaf6e", + "tools/physical-workcell/workbench_link.py": "79571f62c5a9b5fb843a852bc34a2a509b0dc6dc1a6e091fa9f948ab98fae420", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/ui-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/ui-ledger.json new file mode 100644 index 0000000..922ae4c --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-native-gap/ui-ledger.json @@ -0,0 +1,216 @@ +{ + "steps": [ + { + "browser_action": "Observed exact body hard-crash outage before restart", + "cached_public": null, + "client_commands": 4, + "name": "crash-offline", + "offline": false, + "pending_command_ids": [ + "command-75be3033b3774dfb8ebee666241a36e8" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-native-gap-v3/crash-offline.png", + "state": { + "active_job_id": "9ec6b77d-78c4-4c3f-89ff-004c016ae4f9", + "app_id": "b5e77e75097547d49daef66ce7d92528", + "connection": { + "adapter_id": "269e54e660064fca9f42f52cc08637cb", + "body_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "epoch": 1, + "fault": null, + "reason": "Remote end closed connection without response", + "state": "unavailable" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Remote end closed connection without response", + "job_id": "9ec6b77d-78c4-4c3f-89ff-004c016ae4f9", + "label": "Native receipt gap", + "last_intent": "Push the block right so its center enters the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-75be3033b3774dfb8ebee666241a36e8", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "paused" + }, + "time": 1791835077.2167869, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51381/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51381/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t54 checkbox Native receipt gap Paused, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t55 text Native receipt gap\n\t\t56 text Paused\n\t\t14 button (disabled) Resume, ID: pause\n\t\t57 text reply is uncertain; reconcile exact command without resending\n\t\t15 container Description: Body connection, ID: network-card\n\t\t\t58 text Body unavailable\n\t\t\t59 text Remote end closed connection without response\n\t\t\t18 button Reconnect, ID: disconnect\n\t\t\t19 button (disabled) Check receipt, ID: reconcile\n\t\t\t20 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t21 text Test the simulated connection\n\t\t22 container Camera observation\n\t\t\t23 text OVERHEAD CAMERA\n\t\t\t60 text Retained observation \u00b7 body offline\n\t\t\t53 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t26 text Orange part Blue pusher Green task region\n\t\t\t61 text Retained \u00b7 offline \u00b7 generation 0\n\t\t62 text DISCONNECTED\n\t\t63 text The body connection is unavailable. The last camera observation is retained.\n\t\t30 container Job progress\n\t\t\t31 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t32 container Measurement\n\t\t\t33 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t34 text Measurement\n\t\t\t64 text Pending\n\t\t\t36 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Restored ordinary body retained boot-two unresolved original command", + "cached_public": null, + "client_commands": 4, + "name": "crash-restored", + "offline": false, + "pending_command_ids": [ + "command-75be3033b3774dfb8ebee666241a36e8" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-native-gap-v3/crash-restored.png", + "state": { + "active_job_id": "9ec6b77d-78c4-4c3f-89ff-004c016ae4f9", + "app_id": "b5e77e75097547d49daef66ce7d92528", + "connection": { + "adapter_id": "269e54e660064fca9f42f52cc08637cb", + "body_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "epoch": 2, + "fault": null, + "reason": "Independent body has unresolved effects", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "job_id": "9ec6b77d-78c4-4c3f-89ff-004c016ae4f9", + "label": "Native receipt gap", + "last_intent": "Push the block right so its center enters the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-75be3033b3774dfb8ebee666241a36e8", + "operation": "dispatch", + "reason": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791835090.132874, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51381/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51381/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t54 checkbox Native receipt gap Needs attention, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t55 text Native receipt gap\n\t\t56 text Needs attention\n\t\t14 button (disabled) Pause, ID: pause\n\t\t57 text Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image\n\t\t15 container Description: Body connection, ID: network-card\n\t\t\t58 text Body connected\n\t\t\t59 text Independent body has unresolved effects\n\t\t\t18 button Disconnect, ID: disconnect\n\t\t\t19 button Check receipt, ID: reconcile\n\t\t\t20 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t21 text Test the simulated connection\n\t\t22 container Camera observation\n\t\t\t23 text OVERHEAD CAMERA\n\t\t\t60 text Last observation \u00b7 Native receipt gap\n\t\t\t53 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t26 text Orange part Blue pusher Green task region\n\t\t\t61 text Captured \u00b7 generation 0\n\t\t62 text OUTCOME UNCERTAIN\n\t\t63 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t30 container Job progress\n\t\t\t31 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t32 container Measurement\n\t\t\t33 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t34 text Measurement\n\t\t\t64 text Pending\n\t\t\t36 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Exact receipt remained unknown; no completion or new command", + "cached_public": null, + "client_commands": 4, + "name": "crash-blocked", + "offline": false, + "pending_command_ids": [ + "command-75be3033b3774dfb8ebee666241a36e8" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-native-gap-v3/crash-blocked.png", + "state": { + "active_job_id": "9ec6b77d-78c4-4c3f-89ff-004c016ae4f9", + "app_id": "b5e77e75097547d49daef66ce7d92528", + "connection": { + "adapter_id": "269e54e660064fca9f42f52cc08637cb", + "body_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "epoch": 2, + "fault": null, + "reason": "Independent body has unresolved effects", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "accepted intent has no verified terminal proof", + "job_id": "9ec6b77d-78c4-4c3f-89ff-004c016ae4f9", + "label": "Native receipt gap", + "last_intent": "Push the block right so its center enters the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-75be3033b3774dfb8ebee666241a36e8", + "operation": "dispatch", + "reason": "accepted intent has no verified terminal proof", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791835101.869333, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51381/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51381/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t54 checkbox Native receipt gap Needs attention, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t55 text Native receipt gap\n\t\t56 text Needs attention\n\t\t14 button (disabled) Pause, ID: pause\n\t\t57 text accepted intent has no verified terminal proof\n\t\t15 container Description: Body connection, ID: network-card\n\t\t\t58 text Body connected\n\t\t\t59 text Independent body has unresolved effects\n\t\t\t18 button Disconnect, ID: disconnect\n\t\t\t19 button Check receipt, ID: reconcile\n\t\t\t20 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t21 text Test the simulated connection\n\t\t22 container Camera observation\n\t\t\t23 text OVERHEAD CAMERA\n\t\t\t60 text Last observation \u00b7 Native receipt gap\n\t\t\t53 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t26 text Orange part Blue pusher Green task region\n\t\t\t61 text Captured \u00b7 generation 0\n\t\t62 text OUTCOME UNCERTAIN\n\t\t63 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t30 container Job progress\n\t\t\t31 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t32 container Measurement\n\t\t\t33 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t34 text Measurement\n\t\t\t64 text Pending\n\t\t\t36 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Another exact read left pending identity and four commands unchanged; continuation disabled", + "cached_public": null, + "client_commands": 4, + "name": "crash-still-blocked", + "offline": false, + "pending_command_ids": [ + "command-75be3033b3774dfb8ebee666241a36e8" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-native-gap-v3/crash-still-blocked.png", + "state": { + "active_job_id": "9ec6b77d-78c4-4c3f-89ff-004c016ae4f9", + "app_id": "b5e77e75097547d49daef66ce7d92528", + "connection": { + "adapter_id": "269e54e660064fca9f42f52cc08637cb", + "body_id": "f10bb5d611aa45b0a38c67c3bfa04706", + "epoch": 2, + "fault": null, + "reason": "Independent body has unresolved effects", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "accepted intent has no verified terminal proof", + "job_id": "9ec6b77d-78c4-4c3f-89ff-004c016ae4f9", + "label": "Native receipt gap", + "last_intent": "Push the block right so its center enters the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-75be3033b3774dfb8ebee666241a36e8", + "operation": "dispatch", + "reason": "accepted intent has no verified terminal proof", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791835113.817038, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51381/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51381/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t54 checkbox Native receipt gap Needs attention, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t55 text Native receipt gap\n\t\t56 text Needs attention\n\t\t14 button (disabled) Pause, ID: pause\n\t\t57 text accepted intent has no verified terminal proof\n\t\t15 container Description: Body connection, ID: network-card\n\t\t\t58 text Body connected\n\t\t\t59 text Independent body has unresolved effects\n\t\t\t18 button Disconnect, ID: disconnect\n\t\t\t19 button Check receipt, ID: reconcile\n\t\t\t20 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t21 text Test the simulated connection\n\t\t22 container Camera observation\n\t\t\t23 text OVERHEAD CAMERA\n\t\t\t60 text Last observation \u00b7 Native receipt gap\n\t\t\t53 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t26 text Orange part Blue pusher Green task region\n\t\t\t61 text Captured \u00b7 generation 0\n\t\t62 text OUTCOME UNCERTAIN\n\t\t63 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t30 container Job progress\n\t\t\t31 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t32 container Measurement\n\t\t\t33 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t34 text Measurement\n\t\t\t64 text Pending\n\t\t\t36 text The part\u2019s visible footprint will be measured after its placement is verified." + } + ] +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-completed.png b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-completed.png new file mode 100644 index 0000000..827c2f6 Binary files /dev/null and b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-completed.png differ diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-completed.txt b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-completed.txt new file mode 100644 index 0000000..a76501d --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-completed.txt @@ -0,0 +1,79 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51385/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51385/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 76 checkbox Body crash while uncertain Completed, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 77 text Body crash while uncertain + 78 text Running + 14 button Pause, ID: pause + 15 container Description: Body connection, ID: network-card + 79 text Body connected + 80 text Body identity verified by read-only TCP health + 18 button Disconnect, ID: disconnect + 19 button (disabled) Check receipt, ID: reconcile + 20 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 21 text Test the simulated connection + 22 container Camera observation + 23 text OVERHEAD CAMERA + 81 text Last camera observation + 25 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 26 text Orange part Blue pusher Green task region + 82 text Captured · generation 11 + 83 text COMPLETED + 84 text Measurement recorded and reset verified. The bench is ready for another job. + 30 container Job progress + 31 text 1 Prepare 2 Measure 3 Reset 4 Ready + 32 container Measurement + 33 heading Measurement, Value: 2, ID: measurement-heading + 34 text Measurement + 85 text Historical result + 36 container measurement-result + 86 text 1,729 + 38 text Orange pixels + 39 container + 87 text 4,802.8 + 41 text mm² + 42 text Apparent footprint + 43 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 44 button Repeat job, ID: repeat + 45 container Activity + 46 heading Activity, Value: 2, ID: activity-heading + 47 text Activity + 88 text 24 events + 49 content list activity + 89 container + 90 text 04:07 PM Action receipt checked Body crash while uncertain + 91 container + 92 text 04:07 PM Sending a guarded action + 93 container + 94 text 04:07 PM Fresh body check verified The next command uses current body evidence. + 95 container + 96 text 04:07 PM Action proposal accepted Body crash while uncertain + 97 container + 98 text 04:07 PM Checking the planned action + 99 container + 100 text 04:07 PM Fresh body check verified The next command uses current body evidence. + 101 container + 102 text 04:07 PM AI planning requested Body crash while uncertain + 103 container + 104 text 04:07 PM Camera observation received Body crash while uncertain + 105 container + 106 text 04:07 PM Requesting camera observation + 107 container + 108 text 04:07 PM Fresh body check verified The next command uses current body evidence. + 109 container + 110 text 04:07 PM Action receipt checked Body crash while uncertain + 111 container + 112 text 04:07 PM Sending a guarded action + 74 button (collapsed) Session details, Secondary Actions: Expand + 75 text Session details + 113 text 14 model requests · Reported tokens: 181,686 input / 2,058 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51385/ \ No newline at end of file diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-ledger.json new file mode 100644 index 0000000..78dd520 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-ledger.json @@ -0,0 +1,34 @@ +{ + "boundaries": [ + { + "case": "body-uncertain", + "command_id": "command-e5c36cc3c08b4a35bff768b4fd4b4c1a", + "finished_snapshot": "000004-finished.json", + "gate": null, + "new_epoch": 2, + "offline_snapshot": "000002-offline.json", + "old_epoch": 1, + "pre_snapshot": "000001-before.json", + "process": { + "boot": 1, + "children_after": [], + "children_before": [], + "exit_observed": true, + "group": 60445, + "initialized": true, + "log": "body-01.log", + "pid": 60445, + "port": 51383, + "returncode": -9, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-uncertain-v1/body", + "signal": "SIGKILL", + "stopped_time": 1791835460.344328 + }, + "request_sha256": "608c5e652af1f2e535301f640ff3c5b7aec6ef9db20d4a405940fd959b278e3d", + "restored_snapshot": "000003-restored.json", + "role": "body" + } + ], + "case": "body-uncertain" +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-recovered.png b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-recovered.png new file mode 100644 index 0000000..50a46dd Binary files /dev/null and b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-recovered.png differ diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-recovered.txt b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-recovered.txt new file mode 100644 index 0000000..daeaa4a --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-recovered.txt @@ -0,0 +1,72 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51385/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51385/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 69 checkbox Body crash while uncertain Paused, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 70 text Body crash while uncertain + 71 text Paused + 14 button Resume, ID: pause + 72 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue. + 16 container Description: Body connection, ID: network-card + 73 text Body connected + 74 text Body identity verified by read-only TCP health + 19 button Disconnect, ID: disconnect + 20 button (disabled) Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 75 text Last observation · Body crash while uncertain + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 76 text Captured · generation 0 + 77 text RECEIPT RECOVERED + 78 text The exact command receipt was recovered. The agent is paused until you resume. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 79 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 80 text 20 events + 42 content list activity + 81 container + 82 text 04:04 PM Command receipt recovered The agent remains paused until you resume. + 83 container + 84 text 04:04 PM Action receipt checked Body crash while uncertain + 85 container + 86 text 04:04 PM Body restarted 1 -> 2 + 87 container + 88 text 04:04 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 89 container + 90 text 04:04 PM Agent stopped for review Remote end closed connection without response + 91 container + 92 text 04:02 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 93 container + 94 text 04:02 PM Sending a guarded action + 95 container + 96 text 04:02 PM Fresh body check verified The next command uses current body evidence. + 97 container + 98 text 04:02 PM Action proposal accepted Body crash while uncertain + 99 container + 100 text 04:02 PM Checking the planned action + 101 container + 102 text 04:02 PM Fresh body check verified The next command uses current body evidence. + 103 container + 104 text 04:02 PM AI planning requested Body crash while uncertain + 67 button (collapsed) Session details, Secondary Actions: Expand + 68 text Session details + 105 text 1 model requests · Reported tokens: 12,628 input / 168 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51385/ \ No newline at end of file diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-restored.png b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-restored.png new file mode 100644 index 0000000..f7d9835 Binary files /dev/null and b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-restored.png differ diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-restored.txt b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-restored.txt new file mode 100644 index 0000000..f5dc4d7 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/crash-restored.txt @@ -0,0 +1,72 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51385/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51385/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 69 checkbox Body crash while uncertain Needs attention, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 70 text Body crash while uncertain + 71 text Needs attention + 14 button (disabled) Pause, ID: pause + 72 text Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 16 container Description: Body connection, ID: network-card + 73 text Body connected + 74 text Body identity verified by read-only TCP health + 19 button Disconnect, ID: disconnect + 20 button Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 75 text Last observation · Body crash while uncertain + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 76 text Captured · generation 0 + 77 text OUTCOME UNCERTAIN + 78 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 79 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 80 text 18 events + 42 content list activity + 81 container + 82 text 04:04 PM Body restarted 1 -> 2 + 83 container + 84 text 04:04 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 85 container + 86 text 04:04 PM Agent stopped for review Remote end closed connection without response + 87 container + 88 text 04:02 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 89 container + 90 text 04:02 PM Sending a guarded action + 91 container + 92 text 04:02 PM Fresh body check verified The next command uses current body evidence. + 93 container + 94 text 04:02 PM Action proposal accepted Body crash while uncertain + 95 container + 96 text 04:02 PM Checking the planned action + 97 container + 98 text 04:02 PM Fresh body check verified The next command uses current body evidence. + 99 container + 100 text 04:02 PM AI planning requested Body crash while uncertain + 101 container + 102 text 04:02 PM Camera observation received Body crash while uncertain + 103 container + 104 text 04:02 PM Requesting camera observation + 67 button (collapsed) Session details, Secondary Actions: Expand + 68 text Session details + 105 text 1 model requests · Reported tokens: 12,628 input / 168 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51385/ \ No newline at end of file diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/final-public.json b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/final-public.json new file mode 100644 index 0000000..04ecbbf --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/final-public.json @@ -0,0 +1,290 @@ +{ + "active_job_id": null, + "activity": [ + { + "intent": "Body crash while uncertain", + "kind": "rpc_applied", + "outcome": "propose: command-5871909fdd9b477486f804b01c5677b3", + "seq": 128, + "time": 1791835614.577631 + }, + { + "evidence": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 2, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "99ec2fe9445b2874e1fb113047489467a0e717ac18b6fc3994d49511a5baf247" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 129, + "time": 1791835614.632127 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 130, + "time": 1791835614.6712341 + }, + { + "intent": "Body crash while uncertain", + "kind": "rpc_applied", + "outcome": "dispatch: command-455decef6f9b4d48ab0114d35a2e95ed", + "seq": 131, + "time": 1791835614.917127 + }, + { + "evidence": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 2, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "5c013aaa63ab12c99c78cd5a441299a2e7f602b3985da0fdaa8621637e647c45" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 132, + "time": 1791835614.972838 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 133, + "time": 1791835615.011183 + }, + { + "intent": "Body crash while uncertain", + "kind": "rpc_applied", + "outcome": "observe: command-8e601cdcf0fa4f2b8748baadfa1c035c", + "seq": 134, + "time": 1791835615.182611 + }, + { + "intent": "Body crash while uncertain", + "kind": "inference", + "outcome": "13", + "seq": 135, + "time": 1791835615.184774 + }, + { + "evidence": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 2, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "692c097f899ead88c151dfcdc0b5edce71e09eac8a9b2e7993e541b513ef49f5" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 136, + "time": 1791835621.043094 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 137, + "time": 1791835621.0999281 + }, + { + "intent": "Body crash while uncertain", + "kind": "rpc_applied", + "outcome": "propose: command-1914503963ec4f7f8c10fb4eeeefd2d1", + "seq": 138, + "time": 1791835621.243658 + }, + { + "evidence": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 2, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "85fa0f2799cd81bab1328d1c1fb79b445f5dda750a354c48bba7b3baa544d57f" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 139, + "time": 1791835621.3027751 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 140, + "time": 1791835621.342958 + }, + { + "intent": "Body crash while uncertain", + "kind": "rpc_applied", + "outcome": "dispatch: command-b243249bded4499190b3f29b745e5f7f", + "seq": 141, + "time": 1791835621.591145 + }, + { + "evidence": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 2, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "e93937247b8a705485750d7db0d48286c55e0da4d48168f03a2509150e6b4ee5" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 142, + "time": 1791835621.659114 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 143, + "time": 1791835621.72001 + }, + { + "intent": "Body crash while uncertain", + "kind": "rpc_applied", + "outcome": "observe: command-d9b91cabf536484ba939a2cf36bebde2", + "seq": 144, + "time": 1791835622.023156 + }, + { + "intent": "Body crash while uncertain", + "kind": "inference", + "outcome": "14", + "seq": 145, + "time": 1791835622.026342 + }, + { + "evidence": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 2, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "72fb7524b0581a0456c82ad73004591676ebb5772c33cb82d9f48c2a79871bf1" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 146, + "time": 1791835627.806038 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 147, + "time": 1791835627.855956 + }, + { + "intent": "Body crash while uncertain", + "kind": "rpc_applied", + "outcome": "propose: command-806fbe2219b5474bba9aa975b0232c03", + "seq": 148, + "time": 1791835627.959668 + }, + { + "evidence": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 2, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "3b5b1ab31826196ed721322482d7ee376ca1ee60de36adb00955de69b9b5c3b4" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 149, + "time": 1791835628.068774 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 150, + "time": 1791835628.121517 + }, + { + "intent": "Body crash while uncertain", + "kind": "rpc_applied", + "outcome": "dispatch: command-74d3a5af7dbb4d61a227ea5e2787cf13", + "seq": 151, + "time": 1791835628.626026 + } + ], + "app_id": "e11a02dabf3c4d4ebc2f18306acee827", + "connection": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 2, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 11, + "image_url": "/api/frame?generation=11&sha256=f1f55d68054e3cef293a001bb81dce414a4d68e20906ec8016bef30710e279c2" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "label": "Body crash while uncertain", + "last_intent": "Verify the block center and pusher are both at their home positions.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "207f0a8bffac445d9174de448e2bb310", + "block_home_verified": true, + "generation": 11, + "image_sha256": "f1f55d68054e3cef293a001bb81dce414a4d68e20906ec8016bef30710e279c2", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "kind": "finish_reset", + "native_time_s": 23.213999999998013, + "operation_id": "agent-f2057f13b90b472bb82aac1ee127d0db", + "pusher_home_verified": true, + "reset_contact_samples": 3302, + "reset_native_steps": 8272, + "state_sha256": "fc6daf8af8b292628a339301286a35f53b6006ae10a86a01630c65d88855d589" + }, + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": true, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 14, + "input_tokens": 181686, + "output_tokens": 2058, + "reported_calls": 14, + "token_usage_complete": true + } +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/hard-crash-score.json b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/hard-crash-score.json new file mode 100644 index 0000000..09b6e65 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/hard-crash-score.json @@ -0,0 +1,572 @@ +{ + "admissions": [ + { + "command_id": "command-0a59c8a315d641e0a2b760b1523f6e49", + "passed": true, + "proof_event": 3 + }, + { + "command_id": "command-bf791ee0f3f74b87b16cb4365ad57bf8", + "passed": true, + "proof_event": 6 + }, + { + "command_id": "command-791a44ce1ae4438b88d9f2307745ebe0", + "passed": true, + "proof_event": 10 + }, + { + "command_id": "command-e5c36cc3c08b4a35bff768b4fd4b4c1a", + "passed": true, + "proof_event": 13 + }, + { + "command_id": "command-ccdbf0c17cb648a4b470130ac51fcc9e", + "passed": true, + "proof_event": 22 + }, + { + "command_id": "command-4eee7f112b954942b9d42242b6e31cb1", + "passed": true, + "proof_event": 26 + }, + { + "command_id": "command-25f7b9a2e3d84c5d984c10a56acb36e4", + "passed": true, + "proof_event": 29 + }, + { + "command_id": "command-faaa3d53f2ab467788fa08a7ccd7b48d", + "passed": true, + "proof_event": 32 + }, + { + "command_id": "command-6da063a81d954215a4d632c90a352ad1", + "passed": true, + "proof_event": 36 + }, + { + "command_id": "command-30f047a6fde14b8c86109c6d4f4a0334", + "passed": true, + "proof_event": 39 + }, + { + "command_id": "command-03b41cff5e2a46e69e366c90bc153e41", + "passed": true, + "proof_event": 42 + }, + { + "command_id": "command-55fc2cd8da054f70a49d2dc38abce5f8", + "passed": true, + "proof_event": 46 + }, + { + "command_id": "command-3f30158d54e04b6495a6689764db1d4d", + "passed": true, + "proof_event": 49 + }, + { + "command_id": "command-2c06bffb73354d2aae6cd433e5a943cf", + "passed": true, + "proof_event": 52 + }, + { + "command_id": "command-f436e633da19402f940c17989916a37d", + "passed": true, + "proof_event": 56 + }, + { + "command_id": "command-aced4d43df3f4098bdfb916d4263d61b", + "passed": true, + "proof_event": 59 + }, + { + "command_id": "command-e9a5820144b14e369dfca01df142f7ee", + "passed": true, + "proof_event": 62 + }, + { + "command_id": "command-5836afbf228a4c47944e6f63e4563222", + "passed": true, + "proof_event": 66 + }, + { + "command_id": "command-a8c9ce7c4a4541d3a964ef0e2a5890e8", + "passed": true, + "proof_event": 69 + }, + { + "command_id": "command-d0aef18a124d4ce9aae8906c5dd47e9d", + "passed": true, + "proof_event": 72 + }, + { + "command_id": "command-cadb4519fd804b5883c6027f759c87e7", + "passed": true, + "proof_event": 76 + }, + { + "command_id": "command-671722f701f840cd80ebc305546f4a48", + "passed": true, + "proof_event": 79 + }, + { + "command_id": "command-4f2167af0f044609a1ec056dff508b01", + "passed": true, + "proof_event": 82 + }, + { + "command_id": "command-14cac89a96db4bc4bf67188762000bfa", + "passed": true, + "proof_event": 86 + }, + { + "command_id": "command-127b2cdd8dcc4bdea6869a4b5dcc60b5", + "passed": true, + "proof_event": 89 + }, + { + "command_id": "command-bef8910fb9a7477b87213ed2355fe06d", + "passed": true, + "proof_event": 92 + }, + { + "command_id": "command-1213fe863e614673944076a3df780472", + "passed": true, + "proof_event": 96 + }, + { + "command_id": "command-808f063035d745d0a27c9bd70f549819", + "passed": true, + "proof_event": 99 + }, + { + "command_id": "command-e809afa15b6d4dd8870fb20040a8866f", + "passed": true, + "proof_event": 102 + }, + { + "command_id": "command-11f7c3fcc15c47b3a690ea66b38f655f", + "passed": true, + "proof_event": 106 + }, + { + "command_id": "command-28677b6665c94ff68d63a600f089e0e3", + "passed": true, + "proof_event": 109 + }, + { + "command_id": "command-29213f5a733541d6b628f36cc54db1a9", + "passed": true, + "proof_event": 112 + }, + { + "command_id": "command-69fcbf49199f4f3f9751afa0feffd08d", + "passed": true, + "proof_event": 116 + }, + { + "command_id": "command-db37619850db40df8cf50c2cd2bf7309", + "passed": true, + "proof_event": 119 + }, + { + "command_id": "command-9595d821c0eb48b59f87812e686ad159", + "passed": true, + "proof_event": 122 + }, + { + "command_id": "command-5871909fdd9b477486f804b01c5677b3", + "passed": true, + "proof_event": 126 + }, + { + "command_id": "command-455decef6f9b4d48ab0114d35a2e95ed", + "passed": true, + "proof_event": 129 + }, + { + "command_id": "command-8e601cdcf0fa4f2b8748baadfa1c035c", + "passed": true, + "proof_event": 132 + }, + { + "command_id": "command-1914503963ec4f7f8c10fb4eeeefd2d1", + "passed": true, + "proof_event": 136 + }, + { + "command_id": "command-b243249bded4499190b3f29b745e5f7f", + "passed": true, + "proof_event": 139 + }, + { + "command_id": "command-d9b91cabf536484ba939a2cf36bebde2", + "passed": true, + "proof_event": 142 + }, + { + "command_id": "command-806fbe2219b5474bba9aa975b0232c03", + "passed": true, + "proof_event": 146 + }, + { + "command_id": "command-74d3a5af7dbb4d61a227ea5e2787cf13", + "passed": true, + "proof_event": 149 + } + ], + "body_id": "207f0a8bffac445d9174de448e2bb310", + "browser": { + "passed": true, + "required_names": [ + "crash-completed", + "crash-offline", + "crash-recovered", + "crash-restored", + "crash-resumed" + ] + }, + "calls": [ + { + "action": "move", + "call": 1, + "camera_generation": 0, + "challenge_id": "challenge-811f4a0eec584287970d06ea11e429f8", + "command_id": "command-e5c36cc3c08b4a35bff768b4fd4b4c1a", + "image_sha256": "e04fce801726a6603b0861767416a62245306e22ba40245e9a838f7547461d22", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 63920, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-ab19ad173d344ca09783e089cb9c3f78" + }, + { + "action": "move", + "call": 2, + "camera_generation": 1, + "challenge_id": "challenge-ddd01cca68ad4c7f8e4b2dd76363a116", + "command_id": "command-25f7b9a2e3d84c5d984c10a56acb36e4", + "image_sha256": "7f9f3efb38b28e4ec679a00f9eaf473b9a346ea05404873e6bb4bbe71bd4aa6a", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 82407, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-fae63d4169ce4d36a6368173af858dd6" + }, + { + "action": "move", + "call": 3, + "camera_generation": 2, + "challenge_id": "challenge-ac5a6977841e41c0a20ec851c325e54a", + "command_id": "command-30f047a6fde14b8c86109c6d4f4a0334", + "image_sha256": "22ae5bec477c39b83aae68206ee8f6128596fc92c4d98b7b6d2d0baf1653f115", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 83243, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-f553eb3ca45c4858895da0ee8bf74ac5" + }, + { + "action": "measure", + "call": 4, + "camera_generation": 3, + "challenge_id": "challenge-b7ef6a08d27b4e9baf1a3a22a297e660", + "command_id": "command-3f30158d54e04b6495a6689764db1d4d", + "image_sha256": "06af2aacedac4eaff1b2afe91490ce8b690dcb3f92670044eb01611215edd35d", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 84002, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-e7e544d37a5b49b38185f50826fa8551" + }, + { + "action": "begin_reset", + "call": 5, + "camera_generation": 3, + "challenge_id": "challenge-b1e6190b2e9f4ef4a7057d90adc4c8df", + "command_id": "command-aced4d43df3f4098bdfb916d4263d61b", + "image_sha256": "06af2aacedac4eaff1b2afe91490ce8b690dcb3f92670044eb01611215edd35d", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 84699, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-f98a3dbbd4404bad810a1208f1f92d1c" + }, + { + "action": "move", + "call": 6, + "camera_generation": 3, + "challenge_id": "challenge-d47315ef639541f98727ca8a2a9b86e1", + "command_id": "command-a8c9ce7c4a4541d3a964ef0e2a5890e8", + "image_sha256": "06af2aacedac4eaff1b2afe91490ce8b690dcb3f92670044eb01611215edd35d", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 85241, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-0d542c324b33445a8454355205a997c9" + }, + { + "action": "move", + "call": 7, + "camera_generation": 4, + "challenge_id": "challenge-7847126f99534a1e88dd804d0ab658cf", + "command_id": "command-671722f701f840cd80ebc305546f4a48", + "image_sha256": "854e89bb06ca1f68aca9e8719119ddf6aa49a770ff2c3f52ee8b323e2541d230", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 85877, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-a9c9ba66cc3747b08c228ba1ea2a2d5e" + }, + { + "action": "move", + "call": 8, + "camera_generation": 5, + "challenge_id": "challenge-3fecff4dafab44bdae43cb205bd7dda4", + "command_id": "command-127b2cdd8dcc4bdea6869a4b5dcc60b5", + "image_sha256": "8cf77270027203512e7afdb6abeed3fc49259454040560bd2471c75a5b95d600", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 86441, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-f2529a5b01b14ab78e003dcd2667b656" + }, + { + "action": "move", + "call": 9, + "camera_generation": 6, + "challenge_id": "challenge-9fc8a46c087c43abbd9784078edd05f5", + "command_id": "command-808f063035d745d0a27c9bd70f549819", + "image_sha256": "80125412c6729d13bbdbef22a317e738cad506adb7a553d4d2012c84cb23fee9", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 87070, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-685f60657c414a07ac2bc560791b2129" + }, + { + "action": "move", + "call": 10, + "camera_generation": 7, + "challenge_id": "challenge-b52d7d9aec694be88d4d3bc970105eb9", + "command_id": "command-28677b6665c94ff68d63a600f089e0e3", + "image_sha256": "0312717552f5abdda66b1106d6d22e3c8cc8720ac78602e743b6cfa94d19c2a6", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 87624, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-f2886811ebd14a5989ebd72481851dd5" + }, + { + "action": "move", + "call": 11, + "camera_generation": 8, + "challenge_id": "challenge-1a9c18c0ec2d40338f6257b2c66ec34c", + "command_id": "command-db37619850db40df8cf50c2cd2bf7309", + "image_sha256": "c208d13430b58b0dbbfeca31b222cb964a377c19a5ffc08c6989433334b3c30f", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 88248, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-85c3aa38d324432393b4e0be59ddc07b" + }, + { + "action": "move", + "call": 12, + "camera_generation": 9, + "challenge_id": "challenge-7831308672284faa8e0c6962e9982527", + "command_id": "command-455decef6f9b4d48ab0114d35a2e95ed", + "image_sha256": "e13f7b49b651869ca480738c1f63ceef53cfd4e89ac197c8162bcc7c878525d7", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 88789, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-fa8296acd66940daacd3fa561286b2f4" + }, + { + "action": "move", + "call": 13, + "camera_generation": 10, + "challenge_id": "challenge-8e8f3356116a4f24b4633cfc5b0e15d3", + "command_id": "command-b243249bded4499190b3f29b745e5f7f", + "image_sha256": "00c47b9f8eff2c8cbf19797555c2c888bfe87803921af350484fc5299baaca5d", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 91376, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-2182754b0731493aba11e7df74511a01" + }, + { + "action": "finish_reset", + "call": 14, + "camera_generation": 11, + "challenge_id": "challenge-f94a4f7ba7c34a74a17e81358f7ca4e1", + "command_id": "command-74d3a5af7dbb4d61a227ea5e2787cf13", + "image_sha256": "f1f55d68054e3cef293a001bb81dce414a4d68e20906ec8016bef30710e279c2", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "model_pid": 91915, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-10fca22255104924a04eba9aed11ab9b" + } + ], + "case": "body-uncertain", + "completed_cycles": 1, + "crash": { + "case": "body-uncertain", + "command_id": "command-e5c36cc3c08b4a35bff768b4fd4b4c1a", + "new_epoch": 2, + "old_epoch": 1, + "passed": true, + "reconciled_sequence": 20, + "request_sha256": "608c5e652af1f2e535301f640ff3c5b7aec6ef9db20d4a405940fd959b278e3d", + "restored_sequence": 18, + "resume_sequence": 21, + "role": "body", + "snapshot_command_count": 4 + }, + "event_cutoff": 151, + "native": { + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "measurement_receipt": { + "bench_id": "207f0a8bffac445d9174de448e2bb310", + "generation": 3, + "image_sha256": "06af2aacedac4eaff1b2afe91490ce8b690dcb3f92670044eb01611215edd35d", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 6.6699999999994874, + "operation_id": "agent-a2dace1292464989a28a427840e0bb2f", + "source_kind": "mujoco_simulation", + "state_sha256": "c4ddd8a4034538f6b1e392c62f3b776393554676d4fa7b0dd0a6d8848e0c8912", + "version": 1, + "worker_pid": 84689 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.07581675727371044, + -1.529153509429955e-10, + 0.03499573045536979 + ], + "external_force_cleared": true, + "generation": 3, + "native_time_s": 6.6699999999994874, + "pusher_home": false, + "pusher_xyz": [ + -0.049999969400105926, + 8.07740615738075e-27, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "c4ddd8a4034538f6b1e392c62f3b776393554676d4fa7b0dd0a6d8848e0c8912" + }, + "model_calls": 14, + "passed": true, + "reset_receipt": { + "bench_id": "207f0a8bffac445d9174de448e2bb310", + "block_home_verified": true, + "generation": 11, + "image_sha256": "f1f55d68054e3cef293a001bb81dce414a4d68e20906ec8016bef30710e279c2", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "kind": "finish_reset", + "native_time_s": 23.213999999998013, + "operation_id": "agent-f2057f13b90b472bb82aac1ee127d0db", + "pusher_home_verified": true, + "reset_contact_samples": 3302, + "reset_native_steps": 8272, + "state_sha256": "fc6daf8af8b292628a339301286a35f53b6006ae10a86a01630c65d88855d589" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11585798023740286, + -2.316897833543708e-09, + 0.034995730455369925 + ], + "external_force_cleared": true, + "generation": 11, + "native_time_s": 23.213999999998013, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "fc6daf8af8b292628a339301286a35f53b6006ae10a86a01630c65d88855d589" + } + }, + "owned_processes_exited": true, + "passed": true, + "safely_blocked": false, + "scope": "selected process SIGKILL boundaries; actual image-only Codex browser; loopback simulation; no arbitrary power-loss guarantee", + "single_wire_post_every_command": true, + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050", + "source_unchanged": true, + "sqlite": { + "count": 1, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "207f0a8bffac445d9174de448e2bb310", + "generation": 3, + "image_sha256": "06af2aacedac4eaff1b2afe91490ce8b690dcb3f92670044eb01611215edd35d", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "operation_id": "agent-a2dace1292464989a28a427840e0bb2f", + "receipt": { + "bench_id": "207f0a8bffac445d9174de448e2bb310", + "generation": 3, + "image_sha256": "06af2aacedac4eaff1b2afe91490ce8b690dcb3f92670044eb01611215edd35d", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 6.6699999999994874, + "operation_id": "agent-a2dace1292464989a28a427840e0bb2f", + "source_kind": "mujoco_simulation", + "state_sha256": "c4ddd8a4034538f6b1e392c62f3b776393554676d4fa7b0dd0a6d8848e0c8912", + "version": 1, + "worker_pid": 84689 + }, + "state_sha256": "c4ddd8a4034538f6b1e392c62f3b776393554676d4fa7b0dd0a6d8848e0c8912" + } + ] + }, + "transport_command_count": 43, + "version": 1 +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/process-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/process-ledger.json new file mode 100644 index 0000000..fc8d4f4 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/process-ledger.json @@ -0,0 +1,63 @@ +{ + "app_id": "e11a02dabf3c4d4ebc2f18306acee827", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "body_origin": "http://127.0.0.1:51383", + "browser_origin": "http://127.0.0.1:51385", + "case": "body-uncertain", + "owned_processes_exited": true, + "planner": "actual_codex_cli_images_over_TCP", + "processes": [ + { + "boot": 1, + "children_after": [], + "children_before": [], + "exit_observed": true, + "group": 60445, + "initialized": true, + "log": "body-01.log", + "pid": 60445, + "port": 51383, + "returncode": -9, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-uncertain-v1/body", + "signal": "SIGKILL", + "stopped_time": 1791835460.344328 + }, + { + "exit_observed": true, + "group": 60453, + "pid": 60453, + "returncode": 0, + "role": "initialization" + }, + { + "boot": 1, + "exit_observed": true, + "group": 60457, + "initialized": false, + "log": "workbench-01.log", + "pid": 60457, + "port": 51385, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-uncertain-v1/operator", + "stopped_time": 1791835683.605002 + }, + { + "boot": 2, + "exit_observed": true, + "group": 79371, + "initialized": false, + "log": "body-02.log", + "pid": 79371, + "port": 51383, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-uncertain-v1/body", + "stopped_time": 1791835683.7799628 + } + ], + "ready": true, + "simulation": true, + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050" +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/source-freeze.json b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/source-freeze.json new file mode 100644 index 0000000..a73cb87 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/source-freeze.json @@ -0,0 +1,37 @@ +{ + "commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/body_client.py": "40388c238192d923888c9852b363314801dcf65c7266aadc8c377d3810aeb33d", + "tools/physical-workcell/body_network.py": "95fd21597bcdd9e982a698859cd1219ae4522062e332c50fea233629dbfb343c", + "tools/physical-workcell/body_protocol.py": "1d24066768105c28b5cf6f4d46458e4e1b9d86cbde45ed6ce466b30da7ff974f", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "40fe73e01d97ee1e5cfdb813650b11edadbea29534784ab10cc1ef4e7f4d5418", + "tools/physical-workcell/crash_body.py": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/network_bench_operator.html": "75ca82328967ea65cf3c3f7f7d60c109a4201099ae3a24d3834e0ecf19b4d897", + "tools/physical-workcell/network_bench_operator.py": "9079729ea4e8b56ccdf2a6d187b0c5d79a23a33688748fe55d92d10922500b40", + "tools/physical-workcell/network_fault_proxy.py": "44b0b52f7c213c0f5f93b8997ba72be4e750bc9f1a4f7f6d7b2a8c4f1233b595", + "tools/physical-workcell/network_operator_runtime.py": "001511aa25fef7f1078bce444b9a0f7d0a09d26d4e5fba9e9819ee3ed98cb6b9", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/qualify_network_body.py": "2800cf38afd21b86f8b035c46e994b6f96d966db4ccba1bba703e9e1a22f7c2b", + "tools/physical-workcell/qualify_network_workbench.py": "e5c72acda2ea8fae3658faba907448765df2f1e34e05749fbe85faed28ef003b", + "tools/physical-workcell/qualify_workbench_crashes.py": "da971a4190389414dd2114b86d2b7dd40adb2dba2c16e304782eab362244932e", + "tools/physical-workcell/qualify_workbench_restarts.py": "2bc3edc5ea39b69f3372d4274b757f06472409407a22cd3149dfa52fbf314dcb", + "tools/physical-workcell/record_network_workbench.py": "7604865caa309c7002c01a2384da84eb62d8b22dcd63ab256618c536bc5fedc4", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_network_body.py": "d1f7150a5ff23775664b5656fd21379502ba71175487fd7aadb2b51bffcdaf8c", + "tools/physical-workcell/score_network_workbench.py": "67a53586081fe425bd14b0a0fbbf3d7f0fc1e66daccda06765bb88f3d9d4d84c", + "tools/physical-workcell/score_workbench_crashes.py": "10bec02c29afdb9963006884fd9ce310a52be8ba0995698cc11f5d6e4475b998", + "tools/physical-workcell/score_workbench_restarts.py": "181cbf4ee1bbd5483f38edc4a84244fc3efa98dfd21b5c9751d43edfe8f9f5a5", + "tools/physical-workcell/test_workbench_crash_harness.py": "b5a1398f9ccec220731f30c9f082260e378743f6fa6c3d954f008c2e79aeaf6e", + "tools/physical-workcell/workbench_link.py": "79571f62c5a9b5fb843a852bc34a2a509b0dc6dc1a6e091fa9f948ab98fae420", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/ui-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/ui-ledger.json new file mode 100644 index 0000000..32baf1c --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/body-uncertain/ui-ledger.json @@ -0,0 +1,280 @@ +{ + "steps": [ + { + "browser_action": "Body SIGKILL while already uncertain; original command retained and controls disabled offline", + "cached_public": null, + "client_commands": 4, + "name": "crash-offline", + "offline": false, + "pending_command_ids": [ + "command-e5c36cc3c08b4a35bff768b4fd4b4c1a" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-uncertain-v1/crash-offline.png", + "state": { + "active_job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "app_id": "e11a02dabf3c4d4ebc2f18306acee827", + "connection": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 1, + "fault": null, + "reason": "Remote end closed connection without response", + "state": "unavailable" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=e04fce801726a6603b0861767416a62245306e22ba40245e9a838f7547461d22" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Remote end closed connection without response", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "label": "Body crash while uncertain", + "last_intent": "Approach the block from the left to establish pushing contact.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-e5c36cc3c08b4a35bff768b4fd4b4c1a", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "paused" + }, + "time": 1791835474.1391869, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51385/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51385/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t69 checkbox Body crash while uncertain Paused, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t70 text Body crash while uncertain\n\t\t71 text Paused\n\t\t14 button (disabled) Resume, ID: pause\n\t\t72 text reply is uncertain; reconcile exact command without resending\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t73 text Body unavailable\n\t\t\t74 text Remote end closed connection without response\n\t\t\t19 button Reconnect, ID: disconnect\n\t\t\t20 button (disabled) Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t75 text Retained observation \u00b7 body offline\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t76 text Retained \u00b7 offline \u00b7 generation 0\n\t\t77 text DISCONNECTED\n\t\t78 text The body connection is unavailable. The last camera observation is retained.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t79 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "New body boot two verified; original epoch-one command still uncertain with four commands", + "cached_public": null, + "client_commands": 4, + "name": "crash-restored", + "offline": false, + "pending_command_ids": [ + "command-e5c36cc3c08b4a35bff768b4fd4b4c1a" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-uncertain-v1/crash-restored.png", + "state": { + "active_job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "app_id": "e11a02dabf3c4d4ebc2f18306acee827", + "connection": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 2, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=e04fce801726a6603b0861767416a62245306e22ba40245e9a838f7547461d22" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "label": "Body crash while uncertain", + "last_intent": "Approach the block from the left to establish pushing contact.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-e5c36cc3c08b4a35bff768b4fd4b4c1a", + "operation": "dispatch", + "reason": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791835494.481312, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51385/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51385/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t69 checkbox Body crash while uncertain Needs attention, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t70 text Body crash while uncertain\n\t\t71 text Needs attention\n\t\t14 button (disabled) Pause, ID: pause\n\t\t72 text Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t73 text Body connected\n\t\t\t74 text Body identity verified by read-only TCP health\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t75 text Last observation \u00b7 Body crash while uncertain\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t76 text Captured \u00b7 generation 0\n\t\t77 text OUTCOME UNCERTAIN\n\t\t78 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t79 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Exact historical receipt recovered under boot two; original single-send command applied once while paused", + "cached_public": null, + "client_commands": 4, + "name": "crash-recovered", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-uncertain-v1/crash-recovered.png", + "state": { + "active_job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "app_id": "e11a02dabf3c4d4ebc2f18306acee827", + "connection": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 2, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=e04fce801726a6603b0861767416a62245306e22ba40245e9a838f7547461d22" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": null, + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "label": "Body crash while uncertain", + "last_intent": "Approach the block from the left to establish pushing contact.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-e5c36cc3c08b4a35bff768b4fd4b4c1a", + "operation": "dispatch", + "reason": "Exact historical stage recovered; explicit Resume observes fresh evidence", + "state": "recovered" + }, + "runner_status": "paused" + }, + "time": 1791835515.389875, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51385/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51385/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t69 checkbox Body crash while uncertain Paused, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t70 text Body crash while uncertain\n\t\t71 text Paused\n\t\t14 button Resume, ID: pause\n\t\t72 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue.\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t73 text Body connected\n\t\t\t74 text Body identity verified by read-only TCP health\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button (disabled) Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t75 text Last observation \u00b7 Body crash while uncertain\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t76 text Captured \u00b7 generation 0\n\t\t77 text RECEIPT RECOVERED\n\t\t78 text The exact command receipt was recovered. The agent is paused until you resume.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t79 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Explicit Resume after exact historical receipt; fresh boot-two observation precedes next model decision", + "cached_public": null, + "client_commands": 8, + "name": "crash-resumed", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-uncertain-v1/crash-resumed.png", + "state": { + "active_job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "app_id": "e11a02dabf3c4d4ebc2f18306acee827", + "connection": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 2, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 2, + "image_url": "/api/frame?generation=2&sha256=22ae5bec477c39b83aae68206ee8f6128596fc92c4d98b7b6d2d0baf1653f115" + }, + "jobs": [ + { + "call_count": 3, + "disturbance_test": false, + "error": null, + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "label": "Body crash while uncertain", + "last_intent": "Push the block right until its center reaches the target region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "B" + } + ], + "model_request_pending": true, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791835533.851417, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51385/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51385/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t69 checkbox Body crash while uncertain Paused, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t70 text Body crash while uncertain\n\t\t71 text Running\n\t\t14 button Pause, ID: pause\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t72 text Body connected\n\t\t\t73 text Current body boot verified for explicit Resume\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button (disabled) Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t74 text Last observation \u00b7 Body crash while uncertain\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t75 text Captured \u00b7 generation 0\n\t\t76 text AGENT\n\t\t77 text Approach the block from the left to establish pushing contact.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t78 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Completed measurement and contact reset after exact old-boot receipt recovery and explicit Resume", + "cached_public": null, + "client_commands": 43, + "name": "crash-completed", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-body-uncertain-v1/crash-completed.png", + "state": { + "active_job_id": null, + "app_id": "e11a02dabf3c4d4ebc2f18306acee827", + "connection": { + "adapter_id": "676c2d289c0442109c4fe197a1edec4a", + "body_id": "207f0a8bffac445d9174de448e2bb310", + "epoch": 2, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 11, + "image_url": "/api/frame?generation=11&sha256=f1f55d68054e3cef293a001bb81dce414a4d68e20906ec8016bef30710e279c2" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "label": "Body crash while uncertain", + "last_intent": "Verify the block center and pusher are both at their home positions.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "207f0a8bffac445d9174de448e2bb310", + "block_home_verified": true, + "generation": 11, + "image_sha256": "f1f55d68054e3cef293a001bb81dce414a4d68e20906ec8016bef30710e279c2", + "job_id": "86e7b28a-0f74-42e9-86a0-e0f9e767a21c", + "kind": "finish_reset", + "native_time_s": 23.213999999998013, + "operation_id": "agent-f2057f13b90b472bb82aac1ee127d0db", + "pusher_home_verified": true, + "reset_contact_samples": 3302, + "reset_native_steps": 8272, + "state_sha256": "fc6daf8af8b292628a339301286a35f53b6006ae10a86a01630c65d88855d589" + }, + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791835682.8787332, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51385/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51385/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t76 checkbox Body crash while uncertain Completed, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t77 text Body crash while uncertain\n\t\t78 text Running\n\t\t14 button Pause, ID: pause\n\t\t15 container Description: Body connection, ID: network-card\n\t\t\t79 text Body connected\n\t\t\t80 text Body identity verified by read-only TCP health\n\t\t\t18 button Disconnect, ID: disconnect\n\t\t\t19 button (disabled) Check receipt, ID: reconcile\n\t\t\t20 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t21 text Test the simulated connection\n\t\t22 container Camera observation\n\t\t\t23 text OVERHEAD CAMERA\n\t\t\t81 text Last camera observation\n\t\t\t25 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t26 text Orange part Blue pusher Green task region\n\t\t\t82 text Captured \u00b7 generation 11\n\t\t83 text COMPLETED\n\t\t84 text Measurement recorded and reset verified. The bench is ready for another job.\n\t\t30 container Job progress\n\t\t\t31 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t32 container Measurement\n\t\t\t33 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t34 text Measurement\n\t\t\t85 text Historical result\n\t\t\t36 container measurement-result\n\t\t\t\t86 text 1,729\n\t\t\t\t38 text Orange pixels\n\t\t\t\t39 container\n\t\t\t\t\t87 text 4,802.8\n\t\t\t\t\t41 text mm\u00b2\n\t\t\t\t42 text Apparent footprint\n\t\t\t\t43 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment.\n\t\t\t44 button Repeat job, ID: repeat" + } + ] +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/conformance.json b/jevdual/results/workbench-hard-crashes-20261012/conformance.json new file mode 100644 index 0000000..afd2ed9 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/conformance.json @@ -0,0 +1,26 @@ +{ + "native_launcher_checks": { + "count": 3, + "passed": true, + "log": "tools/physical-workcell/output/sim13-crash-body-first.log" + }, + "host_checks": { + "count": 2, + "passed": true, + "log": "tools/physical-workcell/output/sim13-host-crash-runtime-v1.log", + "includes_explicit_recovery_limitation": true + }, + "product_files_changed": false, + "actual_browser_trials": "all four independently passed", + "controller_checks": { + "count": 9, + "passed": true, + "log": "tools/physical-workcell/output/sim13-harness-tests-v3/unittest.log" + }, + "scorer_checks": { + "count": 8, + "passed": true, + "log": "tools/physical-workcell/output/sim13-crash-scorer-missing-db-final.log" + }, + "total_distinct_checks": 22 +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/handoff-full.png b/jevdual/results/workbench-hard-crashes-20261012/handoff-full.png new file mode 100644 index 0000000..14457f9 Binary files /dev/null and b/jevdual/results/workbench-hard-crashes-20261012/handoff-full.png differ diff --git a/jevdual/results/workbench-hard-crashes-20261012/handoff.json b/jevdual/results/workbench-hard-crashes-20261012/handoff.json new file mode 100644 index 0000000..c986c98 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/handoff.json @@ -0,0 +1,319 @@ +{ + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050", + "processes": [ + { + "role": "body", + "pid": 98585, + "group": 98585 + }, + { + "role": "relay", + "pid": 98589, + "group": 98589 + }, + { + "role": "workbench", + "pid": 98591, + "group": 98591 + } + ], + "identity": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "browser_origin": "http://127.0.0.1:51389/", + "relay_origin": "http://127.0.0.1:51820", + "body_root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-uncertain-v1/body", + "operator_root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-uncertain-v1/operator", + "relay_root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-handoff/relay", + "diagnostic_injection": false, + "relay_trial_deadline": false, + "scoring_preceded_handoff": true, + "public": { + "app_id": "676909dee6e1429abdcfa2e3698650cd", + "simulation": true, + "runner_status": "paused", + "jobs": [ + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "label": "Workbench crash while uncertain", + "last_intent": "Finish the reset now that the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "block_home_verified": true, + "generation": 10, + "image_sha256": "612118c97622874a02927de51dfa015cd96bfdc46dbc7c11146a229288798f12", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "kind": "finish_reset", + "native_time_s": 21.943999999998717, + "operation_id": "agent-f367be891109464b8c33dd8c992de912", + "pusher_home_verified": true, + "reset_contact_samples": 2986, + "reset_native_steps": 8205, + "state_sha256": "bcf29e5d41bc6334624f8f04b0bb0bdf4f3dda8e08e52bd9e10b31b37d71ac62" + }, + "status": "completed", + "target": "A" + } + ], + "active_job_id": null, + "model_request_pending": false, + "transport_pending": false, + "pending_operation": null, + "activity": [ + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 118, + "time": 1791835522.635673 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "dispatch: command-455db4ba7be24f0f86007a5e078d859c", + "seq": 119, + "time": 1791835522.881181 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "2cfb9f85218990633d443312264b1c0932395f2efdee1ab957b1afb4a6cc2a4a" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 120, + "time": 1791835522.935346 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 121, + "time": 1791835522.975528 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "observe: command-4fa58c52e18449049cd127551c10d65a", + "seq": 122, + "time": 1791835523.1529238 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "inference", + "outcome": "12", + "seq": 123, + "time": 1791835523.1555321 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "80985a144bda35c03b9eecc3714c9543a952fecafeaf5f978acee3abb5f9f65d" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 124, + "time": 1791835530.8459678 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 125, + "time": 1791835530.885638 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "propose: command-e508dc3e450d45ecb4718799a04de88a", + "seq": 126, + "time": 1791835530.965694 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "5acae7b5c983ea1999c46262c8c7d948e040bf21bcc8a02342f5beb0fd1bcfc3" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 127, + "time": 1791835531.021473 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 128, + "time": 1791835531.063702 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "dispatch: command-d13efa2ea6fd403c88c117fbb2965866", + "seq": 129, + "time": 1791835531.3077571 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "996feeb0e6749ea7e79dda4cd906c7f659bc829a4be722ab5fd3c3712ecc3b74" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 130, + "time": 1791835531.364406 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 131, + "time": 1791835531.4072561 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "observe: command-f9d526ca594e4ae2ad902fb383f55b43", + "seq": 132, + "time": 1791835531.589549 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "inference", + "outcome": "13", + "seq": 133, + "time": 1791835531.59181 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "1c66858fa03bcc744b6b022536d3233761b27dd324d32864a7d9350ffc3c42ce" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 134, + "time": 1791835537.6027908 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 135, + "time": 1791835537.647042 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "propose: command-36ff9689326e44828768e0d99e2605ab", + "seq": 136, + "time": 1791835537.7378528 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "e73ac7e746b7b7194cfb6daba9641212bf2205d50d0151528e4bf6385d29abdf" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 137, + "time": 1791835537.796623 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 138, + "time": 1791835537.8384042 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "dispatch: command-ab8fd2b2180747feaa57a5758a01254e", + "seq": 139, + "time": 1791835538.263912 + }, + { + "seq": 140, + "time": 1791835765.6960058, + "kind": "halt", + "intent": "", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image" + }, + { + "seq": 141, + "time": 1791835765.697831, + "kind": "body_epoch_changed", + "intent": "", + "outcome": "1 -> 2" + } + ], + "usage": { + "calls": 13, + "input_tokens": 170285, + "output_tokens": 2031, + "reported_calls": 13, + "token_usage_complete": true + }, + "readiness": { + "ready": false, + "reason": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=612118c97622874a02927de51dfa015cd96bfdc46dbc7c11146a229288798f12" + }, + "connection": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 2, + "fault": null, + "reason": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "state": "connected" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + } + } +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/handoff.txt b/jevdual/results/workbench-hard-crashes-20261012/handoff.txt new file mode 100644 index 0000000..3d63af9 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/handoff.txt @@ -0,0 +1,79 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51389/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51389/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 checkbox Workbench crash while uncertain Completed, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 12 text Workbench crash while uncertain + 13 text Paused + 14 button Resume, ID: pause + 15 container Description: Body connection, ID: network-card + 16 text Body connected + 17 text Body identity verified by read-only TCP health + 18 button Disconnect, ID: disconnect + 19 button (disabled) Check receipt, ID: reconcile + 20 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 21 text Test the simulated connection + 22 container Camera observation + 23 text OVERHEAD CAMERA + 24 text Last camera observation + 25 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 26 text Orange part Blue pusher Green task region + 27 text Captured · generation 10 + 28 text COMPLETED + 29 text Measurement recorded and reset verified. The bench is ready for another job. + 30 container Job progress + 31 text 1 Prepare 2 Measure 3 Reset 4 Ready + 32 container Measurement + 33 heading Measurement, Value: 2, ID: measurement-heading + 34 text Measurement + 35 text Historical result + 36 container measurement-result + 37 text 1,726 + 38 text Orange pixels + 39 container + 40 text 4,794.4 + 41 text mm² + 42 text Apparent footprint + 43 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 44 button Repeat job, ID: repeat + 45 container Activity + 46 heading Activity, Value: 2, ID: activity-heading + 47 text Activity + 48 text 24 events + 49 content list activity + 50 container + 51 text 04:09 PM Body restarted 1 -> 2 + 52 container + 53 text 04:09 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 54 container + 55 text 04:05 PM Action receipt checked Workbench crash while uncertain + 56 container + 57 text 04:05 PM Sending a guarded action + 58 container + 59 text 04:05 PM Fresh body check verified The next command uses current body evidence. + 60 container + 61 text 04:05 PM Action proposal accepted Workbench crash while uncertain + 62 container + 63 text 04:05 PM Checking the planned action + 64 container + 65 text 04:05 PM Fresh body check verified The next command uses current body evidence. + 66 container + 67 text 04:05 PM AI planning requested Workbench crash while uncertain + 68 container + 69 text 04:05 PM Camera observation received Workbench crash while uncertain + 70 container + 71 text 04:05 PM Requesting camera observation + 72 container + 73 text 04:05 PM Fresh body check verified The next command uses current body evidence. + 74 button (collapsed) Session details, Secondary Actions: Expand + 75 text Session details + 76 text 13 model requests · Reported tokens: 170,285 input / 2,031 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51389/ \ No newline at end of file diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-completed.png b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-completed.png new file mode 100644 index 0000000..7d2fe4c Binary files /dev/null and b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-completed.png differ diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-completed.txt b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-completed.txt new file mode 100644 index 0000000..40698ef --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-completed.txt @@ -0,0 +1,79 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51381/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51381/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 checkbox Workbench crash mid-command Completed, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 12 text Workbench crash mid-command + 13 text Running + 14 button Pause, ID: pause + 15 container Description: Body connection, ID: network-card + 16 text Body connected + 17 text Body identity verified by read-only TCP health + 18 button Disconnect, ID: disconnect + 19 button (disabled) Check receipt, ID: reconcile + 20 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 21 text Test the simulated connection + 22 container Camera observation + 23 text OVERHEAD CAMERA + 24 text Last camera observation + 25 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 26 text Orange part Blue pusher Green task region + 27 text Captured · generation 10 + 28 text COMPLETED + 29 text Measurement recorded and reset verified. The bench is ready for another job. + 30 container Job progress + 31 text 1 Prepare 2 Measure 3 Reset 4 Ready + 32 container Measurement + 33 heading Measurement, Value: 2, ID: measurement-heading + 34 text Measurement + 35 text Historical result + 36 container measurement-result + 37 text 1,726 + 38 text Orange pixels + 39 container + 40 text 4,794.4 + 41 text mm² + 42 text Apparent footprint + 43 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 44 button Repeat job, ID: repeat + 45 container Activity + 46 heading Activity, Value: 2, ID: activity-heading + 47 text Activity + 48 text 24 events + 49 content list activity + 50 container + 51 text 04:02 PM Action receipt checked Workbench crash mid-command + 52 container + 53 text 04:02 PM Sending a guarded action + 54 container + 55 text 04:02 PM Fresh body check verified The next command uses current body evidence. + 56 container + 57 text 04:02 PM Action proposal accepted Workbench crash mid-command + 58 container + 59 text 04:02 PM Checking the planned action + 60 container + 61 text 04:02 PM Fresh body check verified The next command uses current body evidence. + 62 container + 63 text 04:01 PM AI planning requested Workbench crash mid-command + 64 container + 65 text 04:01 PM Camera observation received Workbench crash mid-command + 66 container + 67 text 04:01 PM Requesting camera observation + 68 container + 69 text 04:01 PM Fresh body check verified The next command uses current body evidence. + 70 container + 71 text 04:01 PM Action receipt checked Workbench crash mid-command + 72 container + 73 text 04:01 PM Sending a guarded action + 74 button (collapsed) Session details, Secondary Actions: Expand + 75 text Session details + 76 text 13 model requests · Reported tokens: 169,591 input / 2,019 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51381/ \ No newline at end of file diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-ledger.json new file mode 100644 index 0000000..e29bafb --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-ledger.json @@ -0,0 +1,45 @@ +{ + "boundaries": [ + { + "case": "host-inflight", + "command_id": "command-2af21401c7314a818b232beb8fa76cd6", + "finished_snapshot": "000004-finished.json", + "gate": { + "barrier": "body_receipt_before_link_reply", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "command_id": "command-2af21401c7314a818b232beb8fa76cd6", + "epoch": 1, + "proposal_id": "proposal-a5b9f1eebd6d4aaf8f142caee11074d3", + "relay_sequence": 68, + "request_sha256": "7db7148e228e381277df038f4f18641a0fcf4051343687b5bd1dd4acdd275156", + "response_sha256": "ba9906a224d35543840c26921578a9d939dd364a8a38cd112d84e4753e2fd4db", + "time": 1791835155.247614, + "version": 1 + }, + "new_epoch": 1, + "offline_snapshot": "000002-offline.json", + "old_epoch": 1, + "pre_snapshot": "000001-before.json", + "process": { + "boot": 1, + "children_after": [], + "children_before": [], + "exit_observed": true, + "group": 47151, + "initialized": false, + "log": "workbench-01.log", + "pid": 47151, + "port": 51381, + "returncode": -9, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-inflight-v1/operator", + "signal": "SIGKILL", + "stopped_time": 1791835155.305744 + }, + "request_sha256": "7db7148e228e381277df038f4f18641a0fcf4051343687b5bd1dd4acdd275156", + "restored_snapshot": "000003-restored.json", + "role": "workbench" + } + ], + "case": "host-inflight" +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-recovered.png b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-recovered.png new file mode 100644 index 0000000..ee7cf33 Binary files /dev/null and b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-recovered.png differ diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-recovered.txt b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-recovered.txt new file mode 100644 index 0000000..2ba0120 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-recovered.txt @@ -0,0 +1,72 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51381/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51381/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 69 checkbox Workbench crash mid-command Paused, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 70 text Workbench crash mid-command + 71 text Paused + 14 button Resume, ID: pause + 72 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue. + 16 container Description: Body connection, ID: network-card + 73 text Body connected + 74 text Body identity verified by read-only TCP health + 19 button Disconnect, ID: disconnect + 20 button (disabled) Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 75 text Last observation · Workbench crash mid-command + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 76 text Captured · generation 0 + 77 text RECEIPT RECOVERED + 78 text The exact command receipt was recovered. The agent is paused until you resume. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 79 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 80 text 16 events + 42 content list activity + 81 container + 82 text 04:00 PM Command receipt recovered The agent remains paused until you resume. + 83 container + 84 text 04:00 PM Action receipt checked Workbench crash mid-command + 85 container + 86 text 03:59 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt + 87 container + 88 text 03:59 PM Sending a guarded action + 89 container + 90 text 03:59 PM Fresh body check verified The next command uses current body evidence. + 91 container + 92 text 03:59 PM Action proposal accepted Workbench crash mid-command + 93 container + 94 text 03:59 PM Checking the planned action + 95 container + 96 text 03:59 PM Fresh body check verified The next command uses current body evidence. + 97 container + 98 text 03:59 PM AI planning requested Workbench crash mid-command + 99 container + 100 text 03:59 PM Camera observation received Workbench crash mid-command + 101 container + 102 text 03:59 PM Requesting camera observation + 103 container + 104 text 03:59 PM Fresh body check verified The next command uses current body evidence. + 67 button (collapsed) Session details, Secondary Actions: Expand + 68 text Session details + 105 text 1 model requests · Reported tokens: 13,052 input / 261 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51381/ \ No newline at end of file diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-restored.png b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-restored.png new file mode 100644 index 0000000..01dd100 Binary files /dev/null and b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-restored.png differ diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-restored.txt b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-restored.txt new file mode 100644 index 0000000..12841be --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/crash-restored.txt @@ -0,0 +1,72 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51381/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51381/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 checkbox Workbench crash mid-command Needs attention, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 12 text Workbench crash mid-command + 13 text Needs attention + 14 button (disabled) Pause, ID: pause + 15 text Restarted with a retained command stage; check its exact receipt + 16 container Description: Body connection, ID: network-card + 17 text Body connected + 18 text Body identity verified by read-only TCP health + 19 button Disconnect, ID: disconnect + 20 button Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 25 text Last observation · Workbench crash mid-command + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 28 text Captured · generation 0 + 29 text OUTCOME UNCERTAIN + 30 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 36 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 41 text 14 events + 42 content list activity + 43 container + 44 text 03:59 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt + 45 container + 46 text 03:59 PM Sending a guarded action + 47 container + 48 text 03:59 PM Fresh body check verified The next command uses current body evidence. + 49 container + 50 text 03:59 PM Action proposal accepted Workbench crash mid-command + 51 container + 52 text 03:59 PM Checking the planned action + 53 container + 54 text 03:59 PM Fresh body check verified The next command uses current body evidence. + 55 container + 56 text 03:59 PM AI planning requested Workbench crash mid-command + 57 container + 58 text 03:59 PM Camera observation received Workbench crash mid-command + 59 container + 60 text 03:59 PM Requesting camera observation + 61 container + 62 text 03:59 PM Fresh body check verified The next command uses current body evidence. + 63 container + 64 text 03:59 PM Inspection started Workbench crash mid-command + 65 container + 66 text 03:59 PM Preparing inspection + 67 button (collapsed) Session details, Secondary Actions: Expand + 68 text Session details + 69 text 1 model requests · Reported tokens: 13,052 input / 261 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51381/ \ No newline at end of file diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-inflight/final-public.json b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/final-public.json new file mode 100644 index 0000000..688c2e6 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/final-public.json @@ -0,0 +1,290 @@ +{ + "active_job_id": null, + "activity": [ + { + "intent": "Workbench crash mid-command", + "kind": "rpc_applied", + "outcome": "propose: command-ee93d3f247d2406e9c16bc9cceb5baea", + "seq": 114, + "time": 1791835307.0026329 + }, + { + "evidence": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "e2f9d422da4f3927b2a6c065145d8a64dcde50661ef1b7ed5fecbca755585570" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 115, + "time": 1791835307.057111 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 116, + "time": 1791835307.096344 + }, + { + "intent": "Workbench crash mid-command", + "kind": "rpc_applied", + "outcome": "dispatch: command-5fb23ca55f264d78bf167b94b63cebb6", + "seq": 117, + "time": 1791835307.363761 + }, + { + "evidence": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "2166f11595605041ecf063921f5c75a661be367f3ccb097b69ea07e9972782d3" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 118, + "time": 1791835307.4151812 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 119, + "time": 1791835307.453429 + }, + { + "intent": "Workbench crash mid-command", + "kind": "rpc_applied", + "outcome": "observe: command-a796455ffdbd4c628d805688f990dc0c", + "seq": 120, + "time": 1791835307.617884 + }, + { + "intent": "Workbench crash mid-command", + "kind": "inference", + "outcome": "12", + "seq": 121, + "time": 1791835307.6196811 + }, + { + "evidence": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "926d9bb91bdfedf2517d706ceb5e07ff43e515b2de00d29d4aa93542f7e1d77f" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 122, + "time": 1791835315.3197892 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 123, + "time": 1791835315.358457 + }, + { + "intent": "Workbench crash mid-command", + "kind": "rpc_applied", + "outcome": "propose: command-75ca7196ce15408a991d354d6a54027b", + "seq": 124, + "time": 1791835315.432659 + }, + { + "evidence": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "5dc9b30e7bab56db8c09aec5c8f708244a1b42b753f3abb5534a01afa7b14d48" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 125, + "time": 1791835315.4861178 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 126, + "time": 1791835315.5229561 + }, + { + "intent": "Workbench crash mid-command", + "kind": "rpc_applied", + "outcome": "dispatch: command-88277848bc1a40ce99c55e9fbac30e0d", + "seq": 127, + "time": 1791835315.735436 + }, + { + "evidence": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "5482cf4616b4da8039670816f8768372efdfd9b4aa3dcc34adbbb0163e6603e6" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 128, + "time": 1791835315.794636 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 129, + "time": 1791835315.833333 + }, + { + "intent": "Workbench crash mid-command", + "kind": "rpc_applied", + "outcome": "observe: command-ecfa531b21f24c108926c097aec3375f", + "seq": 130, + "time": 1791835316.0019488 + }, + { + "intent": "Workbench crash mid-command", + "kind": "inference", + "outcome": "13", + "seq": 131, + "time": 1791835316.003954 + }, + { + "evidence": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "83785515d8712d59e2fe79abdbbe0919cbd469a8572a3f0e398896b7b9e0df45" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 132, + "time": 1791835322.645748 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 133, + "time": 1791835322.701047 + }, + { + "intent": "Workbench crash mid-command", + "kind": "rpc_applied", + "outcome": "propose: command-ab8a3f9667c94b57b3a26986f9575b9a", + "seq": 134, + "time": 1791835322.7896929 + }, + { + "evidence": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "1b377d68042b49486d16a6af5f28f4ab6cc015d43eb01f3fd9a679dcd4c28d7d" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 135, + "time": 1791835322.845699 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 136, + "time": 1791835322.886384 + }, + { + "intent": "Workbench crash mid-command", + "kind": "rpc_applied", + "outcome": "dispatch: command-31d14b46fbed46338c061e12ddec1569", + "seq": 137, + "time": 1791835323.1926458 + } + ], + "app_id": "89c50b76f8e840418305726a1a6ea473", + "connection": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87" + }, + "jobs": [ + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "label": "Workbench crash mid-command", + "last_intent": "Finish reset now that the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "kind": "finish_reset", + "native_time_s": 21.81399999999879, + "operation_id": "agent-1e41a624a0124550839549b25c0b46b0", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8140, + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + }, + "status": "completed", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": true, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 13, + "input_tokens": 169591, + "output_tokens": 2019, + "reported_calls": 13, + "token_usage_complete": true + } +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-inflight/hard-crash-score.json b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/hard-crash-score.json new file mode 100644 index 0000000..8166ea8 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/hard-crash-score.json @@ -0,0 +1,544 @@ +{ + "admissions": [ + { + "command_id": "command-18ebe241a89c446b8ab656aa265e5636", + "passed": true, + "proof_event": 2 + }, + { + "command_id": "command-cb6b36628d9c494a898fff2144441b53", + "passed": true, + "proof_event": 5 + }, + { + "command_id": "command-544a0a6743cc4dff808eb83acc72eb8a", + "passed": true, + "proof_event": 9 + }, + { + "command_id": "command-2af21401c7314a818b232beb8fa76cd6", + "passed": true, + "proof_event": 12 + }, + { + "command_id": "command-374f0d33a7074ca9a4c2a6ae96b23051", + "passed": true, + "proof_event": 18 + }, + { + "command_id": "command-581d5439b2b543ff9694fb848a3574ef", + "passed": true, + "proof_event": 22 + }, + { + "command_id": "command-21bae301f68b4daa967df7eb2740f25a", + "passed": true, + "proof_event": 25 + }, + { + "command_id": "command-622145ed89c64c5ebdc4deffc9bb4327", + "passed": true, + "proof_event": 28 + }, + { + "command_id": "command-99750dcde52541a1a18bd7a52fa5a56d", + "passed": true, + "proof_event": 32 + }, + { + "command_id": "command-2e248720c89b478aaf2b9e90a05d47c2", + "passed": true, + "proof_event": 35 + }, + { + "command_id": "command-6e855574ffc54b2089d482130fbbc6f7", + "passed": true, + "proof_event": 38 + }, + { + "command_id": "command-a55ea8503d004943b6fc14cfcafd8b6d", + "passed": true, + "proof_event": 42 + }, + { + "command_id": "command-bda0d0397b154860860b96862d2f2251", + "passed": true, + "proof_event": 45 + }, + { + "command_id": "command-dfe9b1d8d29d4932b2ae5cf111f38250", + "passed": true, + "proof_event": 48 + }, + { + "command_id": "command-a40a88a4bcae4cb79242458b35f51a76", + "passed": true, + "proof_event": 52 + }, + { + "command_id": "command-c0761a0a44224ffead15e99d100ec1eb", + "passed": true, + "proof_event": 55 + }, + { + "command_id": "command-5432b510458a4b56b30c8487b2f055ab", + "passed": true, + "proof_event": 58 + }, + { + "command_id": "command-c3086f2876384e748886e9c57241d5ea", + "passed": true, + "proof_event": 62 + }, + { + "command_id": "command-7fec3622fb2e4fee9073f2030ec8368a", + "passed": true, + "proof_event": 65 + }, + { + "command_id": "command-e6fa1885911f42e5929caed1c85578e5", + "passed": true, + "proof_event": 68 + }, + { + "command_id": "command-f1b8c026d22a41f69f204ddc0e10358e", + "passed": true, + "proof_event": 72 + }, + { + "command_id": "command-5564f3929a134f769349b7ed912dbe28", + "passed": true, + "proof_event": 75 + }, + { + "command_id": "command-1864919be51c4453a32ef9e41629b1b9", + "passed": true, + "proof_event": 78 + }, + { + "command_id": "command-16afdb9f80bd43fb9d06bfb7cbd451cc", + "passed": true, + "proof_event": 82 + }, + { + "command_id": "command-74f7ffea09ea4e5f80212aa8076d3242", + "passed": true, + "proof_event": 85 + }, + { + "command_id": "command-621b28853cbe44be87903770301361b7", + "passed": true, + "proof_event": 88 + }, + { + "command_id": "command-8e296a53a7c547639cf8e24b93a870dc", + "passed": true, + "proof_event": 92 + }, + { + "command_id": "command-1b0c643b32ad419ab0d62ff81ab08651", + "passed": true, + "proof_event": 95 + }, + { + "command_id": "command-dd6e531c789d4e528eda06c68bc7fd73", + "passed": true, + "proof_event": 98 + }, + { + "command_id": "command-81d2f2aa57f749a2b5962014ca922c63", + "passed": true, + "proof_event": 102 + }, + { + "command_id": "command-b6f56dc3e4d844d988385d99b3101f7d", + "passed": true, + "proof_event": 105 + }, + { + "command_id": "command-78c833e25fbf4d04b526d36fbeeba483", + "passed": true, + "proof_event": 108 + }, + { + "command_id": "command-ee93d3f247d2406e9c16bc9cceb5baea", + "passed": true, + "proof_event": 112 + }, + { + "command_id": "command-5fb23ca55f264d78bf167b94b63cebb6", + "passed": true, + "proof_event": 115 + }, + { + "command_id": "command-a796455ffdbd4c628d805688f990dc0c", + "passed": true, + "proof_event": 118 + }, + { + "command_id": "command-75ca7196ce15408a991d354d6a54027b", + "passed": true, + "proof_event": 122 + }, + { + "command_id": "command-88277848bc1a40ce99c55e9fbac30e0d", + "passed": true, + "proof_event": 125 + }, + { + "command_id": "command-ecfa531b21f24c108926c097aec3375f", + "passed": true, + "proof_event": 128 + }, + { + "command_id": "command-ab8a3f9667c94b57b3a26986f9575b9a", + "passed": true, + "proof_event": 132 + }, + { + "command_id": "command-31d14b46fbed46338c061e12ddec1569", + "passed": true, + "proof_event": 135 + } + ], + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "browser": { + "passed": true, + "required_names": [ + "crash-completed", + "crash-offline", + "crash-recovered", + "crash-restored", + "crash-resumed" + ] + }, + "calls": [ + { + "action": "move", + "call": 1, + "camera_generation": 0, + "challenge_id": "challenge-2366e5191cd149bd89ad99567ffe2a85", + "command_id": "command-2af21401c7314a818b232beb8fa76cd6", + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 48409, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-a5b9f1eebd6d4aaf8f142caee11074d3" + }, + { + "action": "move", + "call": 2, + "camera_generation": 1, + "challenge_id": "challenge-de16bd8ad389469fbdb7c261d8e9c6e9", + "command_id": "command-21bae301f68b4daa967df7eb2740f25a", + "image_sha256": "cb4221e600bbb24aec549f49924fc9224876a1ad8d04159fe0cc3798658d8357", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 55882, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-38066d3e977241d6870af8f4a5f3985f" + }, + { + "action": "measure", + "call": 3, + "camera_generation": 2, + "challenge_id": "challenge-6fa425993af04da7aaddc4ef5d29b9ac", + "command_id": "command-2e248720c89b478aaf2b9e90a05d47c2", + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 56585, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-5a711d8cc97a4e738d5609ead18494cd" + }, + { + "action": "begin_reset", + "call": 4, + "camera_generation": 2, + "challenge_id": "challenge-51d15b359a8942a29096ce6cdff8fe2f", + "command_id": "command-bda0d0397b154860860b96862d2f2251", + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 57307, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-cdc9093d3fce4d5a96c2f31c7586ae11" + }, + { + "action": "move", + "call": 5, + "camera_generation": 2, + "challenge_id": "challenge-2628b7324de6446a93d785a88ecd85ff", + "command_id": "command-c0761a0a44224ffead15e99d100ec1eb", + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 57811, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-dc3f3b524537463d9911ae6479933e8d" + }, + { + "action": "move", + "call": 6, + "camera_generation": 3, + "challenge_id": "challenge-1fe4ce7ab28944bea669592b89a07755", + "command_id": "command-7fec3622fb2e4fee9073f2030ec8368a", + "image_sha256": "aeb57b730bc64cb1ec47fbf9a42ef0acce36a927702e60d6df1d751e4d240d1e", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 58657, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-62271a2fa9254018896b449d21f1e21a" + }, + { + "action": "move", + "call": 7, + "camera_generation": 4, + "challenge_id": "challenge-3e147a4cafbb467888f5177316b13021", + "command_id": "command-5564f3929a134f769349b7ed912dbe28", + "image_sha256": "c62187cd908d71592bdd7193b83ec80666e6f350e90968204d2d87a7169d12dc", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 59307, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-9462f0f172ae4a3e8dded23c0bc41ca0" + }, + { + "action": "move", + "call": 8, + "camera_generation": 5, + "challenge_id": "challenge-4edb43dab1994ed2848a36e10575c38c", + "command_id": "command-74f7ffea09ea4e5f80212aa8076d3242", + "image_sha256": "e6f35d68a39eccb69ef08ffb81de2aed231bbe097a530bc31bb48ffda662a3f1", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 59841, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-4e28e400200e4ca283e60c2477cec2d4" + }, + { + "action": "move", + "call": 9, + "camera_generation": 6, + "challenge_id": "challenge-985df3cbb5364dafb0391c7b770e96ec", + "command_id": "command-1b0c643b32ad419ab0d62ff81ab08651", + "image_sha256": "6c4785ad11fbf959867ca24381e9caf3a051c777b86c8e5357697aa2cdea5f67", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 60481, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-bb504f25133248e3be5e9a9e97eb1861" + }, + { + "action": "move", + "call": 10, + "camera_generation": 7, + "challenge_id": "challenge-fb29bceea1d1405ca741eda4f5605ca9", + "command_id": "command-b6f56dc3e4d844d988385d99b3101f7d", + "image_sha256": "18e16fad85b15cc6b6c095191c6d8f1be9d3e1a605f05ae0ac11954d420d7c38", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 61178, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-33ee21df5233413aa9c3ed2d355ae4e6" + }, + { + "action": "move", + "call": 11, + "camera_generation": 8, + "challenge_id": "challenge-aa466b3468324fb381dc08751a2a8eb5", + "command_id": "command-5fb23ca55f264d78bf167b94b63cebb6", + "image_sha256": "18207abc055796d1b37f4b94528d52bfd960be835625169fc997360f0ec4d906", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 61724, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-dd15bb91d8f24618a6847580f41c81fa" + }, + { + "action": "move", + "call": 12, + "camera_generation": 9, + "challenge_id": "challenge-997c165af55e493cb62f56cbc5f5fd20", + "command_id": "command-88277848bc1a40ce99c55e9fbac30e0d", + "image_sha256": "cda2378ead3e165186825aa560540c8495d01f07acff067406513a16ee92db65", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 62258, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-701b890f9a3c4fcf9321378a2447c366" + }, + { + "action": "finish_reset", + "call": 13, + "camera_generation": 10, + "challenge_id": "challenge-cd9c6ec72ecb44efb28c38f162930e02", + "command_id": "command-31d14b46fbed46338c061e12ddec1569", + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "model_pid": 62832, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-459f99e34a9c4b1c83492411e3c7a5d5" + } + ], + "case": "host-inflight", + "completed_cycles": 1, + "crash": { + "case": "host-inflight", + "command_id": "command-2af21401c7314a818b232beb8fa76cd6", + "new_epoch": 1, + "old_epoch": 1, + "passed": true, + "reconciled_sequence": 16, + "request_sha256": "7db7148e228e381277df038f4f18641a0fcf4051343687b5bd1dd4acdd275156", + "restored_sequence": 14, + "resume_sequence": 17, + "role": "workbench", + "snapshot_command_count": 4 + }, + "event_cutoff": 137, + "native": { + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "measurement_receipt": { + "bench_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.533999999999613, + "operation_id": "agent-d2c79124d1354aa68079cee8dfe39802", + "source_kind": "mujoco_simulation", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3", + "version": 1, + "worker_pid": 57272 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06582868246696554, + -8.393868978677625e-11, + 0.034995730455369786 + ], + "external_force_cleared": true, + "generation": 2, + "native_time_s": 5.533999999999613, + "pusher_home": false, + "pusher_xyz": [ + -0.049999969408768885, + 8.057882667829012e-26, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3" + }, + "model_calls": 13, + "passed": true, + "reset_receipt": { + "bench_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "kind": "finish_reset", + "native_time_s": 21.81399999999879, + "operation_id": "agent-1e41a624a0124550839549b25c0b46b0", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8140, + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11582763630912812, + -2.3633644648884637e-09, + 0.03499573045536985 + ], + "external_force_cleared": true, + "generation": 10, + "native_time_s": 21.81399999999879, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + } + }, + "owned_processes_exited": true, + "passed": true, + "safely_blocked": false, + "scope": "selected process SIGKILL boundaries; actual image-only Codex browser; loopback simulation; no arbitrary power-loss guarantee", + "single_wire_post_every_command": true, + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050", + "source_unchanged": true, + "sqlite": { + "count": 1, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "operation_id": "agent-d2c79124d1354aa68079cee8dfe39802", + "receipt": { + "bench_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.533999999999613, + "operation_id": "agent-d2c79124d1354aa68079cee8dfe39802", + "source_kind": "mujoco_simulation", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3", + "version": 1, + "worker_pid": 57272 + }, + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3" + } + ] + }, + "transport_command_count": 40, + "version": 1 +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-inflight/process-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/process-ledger.json new file mode 100644 index 0000000..989b0b5 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/process-ledger.json @@ -0,0 +1,63 @@ +{ + "app_id": "89c50b76f8e840418305726a1a6ea473", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "body_origin": "http://127.0.0.1:51379", + "browser_origin": "http://127.0.0.1:51381", + "case": "host-inflight", + "owned_processes_exited": true, + "planner": "actual_codex_cli_images_over_TCP", + "processes": [ + { + "boot": 1, + "exit_observed": true, + "group": 47144, + "initialized": true, + "log": "body-01.log", + "pid": 47144, + "port": 51379, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-inflight-v1/body", + "stopped_time": 1791835576.369768 + }, + { + "exit_observed": true, + "group": 47150, + "pid": 47150, + "returncode": 0, + "role": "initialization" + }, + { + "boot": 1, + "children_after": [], + "children_before": [], + "exit_observed": true, + "group": 47151, + "initialized": false, + "log": "workbench-01.log", + "pid": 47151, + "port": 51381, + "returncode": -9, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-inflight-v1/operator", + "signal": "SIGKILL", + "stopped_time": 1791835155.305744 + }, + { + "boot": 2, + "exit_observed": true, + "group": 52144, + "initialized": false, + "log": "workbench-02.log", + "pid": 52144, + "port": 51381, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-inflight-v1/operator", + "stopped_time": 1791835576.215932 + } + ], + "ready": true, + "simulation": true, + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050" +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-inflight/source-freeze.json b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/source-freeze.json new file mode 100644 index 0000000..a73cb87 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/source-freeze.json @@ -0,0 +1,37 @@ +{ + "commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/body_client.py": "40388c238192d923888c9852b363314801dcf65c7266aadc8c377d3810aeb33d", + "tools/physical-workcell/body_network.py": "95fd21597bcdd9e982a698859cd1219ae4522062e332c50fea233629dbfb343c", + "tools/physical-workcell/body_protocol.py": "1d24066768105c28b5cf6f4d46458e4e1b9d86cbde45ed6ce466b30da7ff974f", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "40fe73e01d97ee1e5cfdb813650b11edadbea29534784ab10cc1ef4e7f4d5418", + "tools/physical-workcell/crash_body.py": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/network_bench_operator.html": "75ca82328967ea65cf3c3f7f7d60c109a4201099ae3a24d3834e0ecf19b4d897", + "tools/physical-workcell/network_bench_operator.py": "9079729ea4e8b56ccdf2a6d187b0c5d79a23a33688748fe55d92d10922500b40", + "tools/physical-workcell/network_fault_proxy.py": "44b0b52f7c213c0f5f93b8997ba72be4e750bc9f1a4f7f6d7b2a8c4f1233b595", + "tools/physical-workcell/network_operator_runtime.py": "001511aa25fef7f1078bce444b9a0f7d0a09d26d4e5fba9e9819ee3ed98cb6b9", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/qualify_network_body.py": "2800cf38afd21b86f8b035c46e994b6f96d966db4ccba1bba703e9e1a22f7c2b", + "tools/physical-workcell/qualify_network_workbench.py": "e5c72acda2ea8fae3658faba907448765df2f1e34e05749fbe85faed28ef003b", + "tools/physical-workcell/qualify_workbench_crashes.py": "da971a4190389414dd2114b86d2b7dd40adb2dba2c16e304782eab362244932e", + "tools/physical-workcell/qualify_workbench_restarts.py": "2bc3edc5ea39b69f3372d4274b757f06472409407a22cd3149dfa52fbf314dcb", + "tools/physical-workcell/record_network_workbench.py": "7604865caa309c7002c01a2384da84eb62d8b22dcd63ab256618c536bc5fedc4", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_network_body.py": "d1f7150a5ff23775664b5656fd21379502ba71175487fd7aadb2b51bffcdaf8c", + "tools/physical-workcell/score_network_workbench.py": "67a53586081fe425bd14b0a0fbbf3d7f0fc1e66daccda06765bb88f3d9d4d84c", + "tools/physical-workcell/score_workbench_crashes.py": "10bec02c29afdb9963006884fd9ce310a52be8ba0995698cc11f5d6e4475b998", + "tools/physical-workcell/score_workbench_restarts.py": "181cbf4ee1bbd5483f38edc4a84244fc3efa98dfd21b5c9751d43edfe8f9f5a5", + "tools/physical-workcell/test_workbench_crash_harness.py": "b5a1398f9ccec220731f30c9f082260e378743f6fa6c3d954f008c2e79aeaf6e", + "tools/physical-workcell/workbench_link.py": "79571f62c5a9b5fb843a852bc34a2a509b0dc6dc1a6e091fa9f948ab98fae420", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-inflight/ui-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/ui-ledger.json new file mode 100644 index 0000000..46b5cf1 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-inflight/ui-ledger.json @@ -0,0 +1,289 @@ +{ + "steps": [ + { + "browser_action": "Observed Failed to fetch and disabled controls after owned workbench SIGKILL; displayed data is cached", + "cached_public": { + "active_job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "app_id": "89c50b76f8e840418305726a1a6ea473", + "connection": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "fault": null, + "reason": "Fresh pinned ready proof verified before command admission", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": null, + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "label": "Workbench crash mid-command", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No uncertain command", + "state": "none" + }, + "runner_status": "running" + }, + "client_commands": 4, + "fetch_error": "[Errno 61] Connection refused", + "name": "crash-offline", + "offline": true, + "pending_command_ids": [ + "command-2af21401c7314a818b232beb8fa76cd6" + ], + "pins": { + "app_id": "89c50b76f8e840418305726a1a6ea473", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6" + }, + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-inflight-v1/crash-offline.png", + "snapshot_provenance": { + "name": "000002-offline.json", + "sha256": "ee59360f4f30dbb0bb0a0fcfc0b56e308ef3343ecbc0df0fb8f152e86babe7d2" + }, + "state": null, + "time": 1791835194.0031989, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51381/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51381/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t46 checkbox Workbench crash mid-command Working, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t47 text Workbench crash mid-command\n\t\t48 text Disconnected\n\t\t14 button (disabled) Pause, ID: pause\n\t\t49 text Failed to fetch\n\t\t15 container Description: Body connection, ID: network-card\n\t\t\t50 text Body connected\n\t\t\t51 text Body identity verified by read-only TCP health\n\t\t\t18 button (disabled) Disconnect, ID: disconnect\n\t\t\t19 button (disabled) Check receipt, ID: reconcile\n\t\t\t20 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t21 text Test the simulated connection\n\t\t22 container Camera observation\n\t\t\t23 text OVERHEAD CAMERA\n\t\t\t52 text Last observation \u00b7 Workbench crash mid-command\n\t\t\t53 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t26 text Orange part Blue pusher Green task region\n\t\t\t54 text Last captured \u00b7 generation 0 \u00b7 disconnected\n\t\t55 text AI IS PLANNING\n\t\t56 text Planning the next move from the latest camera image.\n\t\t30 container Job progress\n\t\t\t31 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t32 container Measurement\n\t\t\t33 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t34 text Measurement\n\t\t\t57 text Pending\n\t\t\t36 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Reloaded restarted same-identity workbench with original retained stage and no replay", + "cached_public": null, + "client_commands": 4, + "name": "crash-restored", + "offline": false, + "pending_command_ids": [ + "command-2af21401c7314a818b232beb8fa76cd6" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-inflight-v1/crash-restored.png", + "state": { + "active_job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "app_id": "89c50b76f8e840418305726a1a6ea473", + "connection": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Restarted with a retained command stage; check its exact receipt", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "label": "Workbench crash mid-command", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-2af21401c7314a818b232beb8fa76cd6", + "operation": "dispatch", + "reason": "Restarted with a retained command stage; check its exact receipt", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791835203.7040231, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51381/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51381/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 checkbox Workbench crash mid-command Needs attention, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t12 text Workbench crash mid-command\n\t\t13 text Needs attention\n\t\t14 button (disabled) Pause, ID: pause\n\t\t15 text Restarted with a retained command stage; check its exact receipt\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t17 text Body connected\n\t\t\t18 text Body identity verified by read-only TCP health\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t25 text Last observation \u00b7 Workbench crash mid-command\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t28 text Captured \u00b7 generation 0\n\t\t29 text OUTCOME UNCERTAIN\n\t\t30 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t36 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Read exact retained body receipt; four commands unchanged and agent paused", + "cached_public": null, + "client_commands": 4, + "name": "crash-recovered", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-inflight-v1/crash-recovered.png", + "state": { + "active_job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "app_id": "89c50b76f8e840418305726a1a6ea473", + "connection": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": null, + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "label": "Workbench crash mid-command", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-2af21401c7314a818b232beb8fa76cd6", + "operation": "dispatch", + "reason": "Exact historical stage recovered; explicit Resume observes fresh evidence", + "state": "recovered" + }, + "runner_status": "paused" + }, + "time": 1791835221.020743, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51381/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51381/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t69 checkbox Workbench crash mid-command Paused, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t70 text Workbench crash mid-command\n\t\t71 text Paused\n\t\t14 button Resume, ID: pause\n\t\t72 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue.\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t73 text Body connected\n\t\t\t74 text Body identity verified by read-only TCP health\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button (disabled) Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t75 text Last observation \u00b7 Workbench crash mid-command\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t76 text Captured \u00b7 generation 0\n\t\t77 text RECEIPT RECOVERED\n\t\t78 text The exact command receipt was recovered. The agent is paused until you resume.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t79 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Explicit Resume obtains fresh body evidence and next image; original dispatch not resent", + "cached_public": null, + "client_commands": 5, + "name": "crash-resumed", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-inflight-v1/crash-resumed.png", + "state": { + "active_job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "app_id": "89c50b76f8e840418305726a1a6ea473", + "connection": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 1, + "image_url": "/api/frame?generation=1&sha256=cb4221e600bbb24aec549f49924fc9224876a1ad8d04159fe0cc3798658d8357" + }, + "jobs": [ + { + "call_count": 2, + "disturbance_test": false, + "error": null, + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "label": "Workbench crash mid-command", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "A" + } + ], + "model_request_pending": true, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791835231.33159, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51381/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51381/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t69 checkbox Workbench crash mid-command Paused, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t70 text Workbench crash mid-command\n\t\t71 text Running\n\t\t14 button Pause, ID: pause\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t72 text Body connected\n\t\t\t73 text Current body boot verified for explicit Resume\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button (disabled) Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t74 text Last observation \u00b7 Workbench crash mid-command\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t75 text Captured \u00b7 generation 0\n\t\t76 text AGENT\n\t\t77 text Push the block right so its center reaches the measurement region.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t78 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Actual AI job completed measurement and contact reset after hard host crash recovery", + "cached_public": null, + "client_commands": 40, + "name": "crash-completed", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-inflight-v1/crash-completed.png", + "state": { + "active_job_id": null, + "app_id": "89c50b76f8e840418305726a1a6ea473", + "connection": { + "adapter_id": "2ed3651897e14d4bbcf370f071beff20", + "body_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87" + }, + "jobs": [ + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "label": "Workbench crash mid-command", + "last_intent": "Finish reset now that the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "fa2bfa1f197340cd93848d5096b4bfe6", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "78315007-1225-4aee-ad89-0fb2723c029e", + "kind": "finish_reset", + "native_time_s": 21.81399999999879, + "operation_id": "agent-1e41a624a0124550839549b25c0b46b0", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8140, + "state_sha256": "ff7af086a1eea92c53736c30a05c8102da4417d39f0423b48573218144fcb713" + }, + "status": "completed", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791835575.319531, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51381/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51381/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 checkbox Workbench crash mid-command Completed, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t12 text Workbench crash mid-command\n\t\t13 text Running\n\t\t14 button Pause, ID: pause\n\t\t15 container Description: Body connection, ID: network-card\n\t\t\t16 text Body connected\n\t\t\t17 text Body identity verified by read-only TCP health\n\t\t\t18 button Disconnect, ID: disconnect\n\t\t\t19 button (disabled) Check receipt, ID: reconcile\n\t\t\t20 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t21 text Test the simulated connection\n\t\t22 container Camera observation\n\t\t\t23 text OVERHEAD CAMERA\n\t\t\t24 text Last camera observation\n\t\t\t25 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t26 text Orange part Blue pusher Green task region\n\t\t\t27 text Captured \u00b7 generation 10\n\t\t28 text COMPLETED\n\t\t29 text Measurement recorded and reset verified. The bench is ready for another job.\n\t\t30 container Job progress\n\t\t\t31 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t32 container Measurement\n\t\t\t33 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t34 text Measurement\n\t\t\t35 text Historical result\n\t\t\t36 container measurement-result\n\t\t\t\t37 text 1,726\n\t\t\t\t38 text Orange pixels\n\t\t\t\t39 container\n\t\t\t\t\t40 text 4,794.4\n\t\t\t\t\t41 text mm\u00b2\n\t\t\t\t42 text Apparent footprint\n\t\t\t\t43 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment.\n\t\t\t44 button Repeat job, ID: repeat" + } + ] +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-recovery-conformance.json b/jevdual/results/workbench-hard-crashes-20261012/host-recovery-conformance.json new file mode 100644 index 0000000..1f2e059 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-recovery-conformance.json @@ -0,0 +1,1121 @@ +{ + "before_count": 4, + "case": "test_workbench_crash_runtime.WorkbenchCrashRuntimeTests.test_sigkill_host_after_known_body_effect_recovers_exact_receipt_without_replay", + "challenges": [ + { + "challenge_id": "challenge-b999f8f581a04dfdb0032544e739a4d7", + "generation": 0 + }, + { + "challenge_id": "challenge-43fe388fc4544ac0a1b88399629decbb", + "generation": 1 + } + ], + "children_at_dispatch_gate": [], + "command_id": "command-c2b7424f52d34ca0bc69c94f0cbb58a8", + "host_state": { + "active_job_id": "crash-one", + "activity": [ + { + "intent": "Owned host crash", + "kind": "queued", + "outcome": "crash-one", + "seq": 1, + "time": 1791833349.30804 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "e4f618c574d296bc87d0df4ef85a53d966ad506e39d16c706f9716de545cc815" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "start_job", + "seq": 2, + "time": 1791833349.3356001 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 3, + "time": 1791833349.3797069 + }, + { + "intent": "Owned host crash", + "kind": "rpc_applied", + "outcome": "start_job: command-03d2e4ff739e460eb40a5a84c39a0568", + "seq": 4, + "time": 1791833349.4458508 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "62a26824a64a1ad4836505d1ebf8c2e49d6c801868bbc0e00e6c193b2fb7b5c1" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 5, + "time": 1791833349.500184 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 6, + "time": 1791833349.5445702 + }, + { + "intent": "Owned host crash", + "kind": "rpc_applied", + "outcome": "observe: command-a34e5f40ea694de88a0d00cccb96c44e", + "seq": 7, + "time": 1791833349.9674509 + }, + { + "intent": "Owned host crash", + "kind": "inference", + "outcome": "1", + "seq": 8, + "time": 1791833349.9699688 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "5bd7eb93f05938c05571fa2b706fde225bb9e8da1ea77b40db8f30b9db8bc03c" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 9, + "time": 1791833349.985573 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 10, + "time": 1791833350.012773 + }, + { + "intent": "Owned host crash", + "kind": "rpc_applied", + "outcome": "propose: command-be09796e714a4770a1ea65b8d4f77984", + "seq": 11, + "time": 1791833350.052333 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "fbf6db7e5e5dba748d8e7256eb53dfcce926613a6528673bdb094443dcd0bf31" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 12, + "time": 1791833350.075737 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 13, + "time": 1791833350.100466 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Restarted with a retained command stage; check its exact receipt", + "seq": 14, + "time": 1791833350.6643 + }, + { + "intent": "Owned host crash", + "kind": "rpc_applied", + "outcome": "dispatch: command-c2b7424f52d34ca0bc69c94f0cbb58a8", + "seq": 15, + "time": 1791833350.7481759 + }, + { + "intent": "", + "kind": "reconciled", + "outcome": "command-c2b7424f52d34ca0bc69c94f0cbb58a8", + "seq": 16, + "time": 1791833350.749714 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 17, + "time": 1791833350.77245 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "2a3f4fc1742686e469b4e44df8883079f89f9953429c8e7aa4d4cbf2cde05fcc" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 18, + "time": 1791833350.7967832 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 19, + "time": 1791833350.830477 + }, + { + "intent": "Owned host crash", + "kind": "rpc_applied", + "outcome": "observe: command-5ecd4e56fd0c483ba2b5eebe5dbcf613", + "seq": 20, + "time": 1791833351.033206 + }, + { + "intent": "Owned host crash", + "kind": "inference", + "outcome": "2", + "seq": 21, + "time": 1791833351.035726 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Deterministic hard-crash conformance", + "seq": 22, + "time": 1791833351.039551 + } + ], + "app_id": "5005ae5a09f54caf921b0d50efc1e21d", + "applied_commands": [ + "command-03d2e4ff739e460eb40a5a84c39a0568", + "command-a34e5f40ea694de88a0d00cccb96c44e", + "command-be09796e714a4770a1ea65b8d4f77984", + "command-c2b7424f52d34ca0bc69c94f0cbb58a8", + "command-5ecd4e56fd0c483ba2b5eebe5dbcf613" + ], + "body_identity": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_origin": "http://127.0.0.1:64124", + "connection": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "fault": null, + "reason": "Fresh pinned ready proof verified before command admission", + "state": "connected" + }, + "frame": { + "generation": 1, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-crash-e1229762d6/host/client/images/challenge-43fe388fc4544ac0a1b88399629decbb-9f6339a75fcf48ca514a5e83f5fdc4ab26b70239e87cfc4a7d5d10be44830b32.png", + "sha256": "9f6339a75fcf48ca514a5e83f5fdc4ab26b70239e87cfc4a7d5d10be44830b32" + }, + "jobs": [ + { + "call_count": 2, + "disturbance_test": false, + "disturbed": false, + "error": "Deterministic hard-crash conformance", + "history": [ + { + "action": { + "action": "move", + "intent": "Deterministic hard-crash conformance", + "target_x": -0.25, + "target_y": 0 + }, + "outcome": "executed", + "reason": "" + } + ], + "id": "crash-one", + "label": "Owned host crash", + "last_intent": "Deterministic hard-crash conformance", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "A" + } + ], + "link_origin": "http://127.0.0.1:64125", + "link_port": 64125, + "opened": true, + "reason": "Stopped; independent network body retained", + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "stopped", + "sequence": 22, + "sources": { + "body_client.py": "40388c238192d923888c9852b363314801dcf65c7266aadc8c377d3810aeb33d", + "codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "codex_policy.py": "40fe73e01d97ee1e5cfdb813650b11edadbea29534784ab10cc1ef4e7f4d5418", + "network_bench_operator.html": "75ca82328967ea65cf3c3f7f7d60c109a4201099ae3a24d3834e0ecf19b4d897", + "network_bench_operator.py": "9079729ea4e8b56ccdf2a6d187b0c5d79a23a33688748fe55d92d10922500b40", + "network_operator_runtime.py": "001511aa25fef7f1078bce444b9a0f7d0a09d26d4e5fba9e9819ee3ed98cb6b9", + "order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "workbench_link.py": "79571f62c5a9b5fb843a852bc34a2a509b0dc6dc1a6e091fa9f948ab98fae420" + }, + "stage": null, + "usage": { + "calls": 2, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0 + }, + "version": 1 + }, + "original_body_receipt": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "command_id": "command-c2b7424f52d34ca0bc69c94f0cbb58a8", + "protocol": "footwork.body/1", + "reason": null, + "receipt_scope": "retained_command_result", + "recorded_epoch": 1, + "request_sha256": "4bb621a7caca66ad01ffe6be451549dc6333e24b784f85ca74b3a6b998eb3c91", + "result": { + "next_observation": { + "challenge_id": "challenge-3c70ac6d24c741a8bd4c37b4051a0ca2", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-crash-e1229762d6/body/bench/images/000001-570812160c507693.png", + "observation": { + "bench_id": "c825e8a587194a60943c682034d8a9e0", + "calibration": { + "bbox_convention": "exclusive_max_pixel_edges", + "height": 360, + "image_center_world_xy": [ + 0.0, + 0.0 + ], + "image_x_direction": "world_positive_x", + "image_y_direction": "world_negative_y", + "meters_per_pixel": 0.0016666666666666666, + "projection": "orthographic", + "valid": true, + "validity_reason": "bound orthographic calibration", + "vertical_span_m": 0.6, + "width": 640, + "world_frame": "sim_world" + }, + "generation": 1, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-crash-e1229762d6/body/bench/images/000001-570812160c507693.png", + "image_sha256": "9f6339a75fcf48ca514a5e83f5fdc4ab26b70239e87cfc4a7d5d10be44830b32", + "job_id": "crash-one", + "native_time_s": 2.3339999999999645, + "phase": "prepare", + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state_sha256": "05d9e7edda84f8e99f09ae66ce6d2dca314226cffcc88b6df658167d62d8e310", + "version": 1 + }, + "observation_sha256": "aa1c81325d9ebee8f598e346762467079ef66d2024ca79f98b1c41134dd01de8", + "sequence": 4, + "session_id": "ec0cf09291874cd1a0fe519ca9878e18", + "task": { + "bounds": { + "x_max": 0.18, + "x_min": -0.32, + "y_max": 0.15, + "y_min": -0.15 + }, + "home_zone": { + "x_max": -0.08, + "x_min": -0.16, + "y_max": 0.03, + "y_min": -0.03 + }, + "pusher_dimensions_m": { + "x": 0.04, + "y": 0.14 + }, + "zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "observation_scope": "snapshot_at_dispatch_result", + "outcome": "executed", + "proposal_id": "proposal-5c78c35087284888a2bfb0207d0d2eed", + "receipt": { + "bench_id": "c825e8a587194a60943c682034d8a9e0", + "command_speed_limit_m_s": 0.15, + "generation": 1, + "job_id": "crash-one", + "kind": "move", + "max_axis_actuator_force_n": 0.38456934164970424, + "native_steps": 667, + "native_time_s": 2.3339999999999645, + "operation_id": "agent-f7c5669b35e84a05bfc78cb5d33fa7fd", + "pusher_block_contact_samples": 0, + "state_sha256": "05d9e7edda84f8e99f09ae66ce6d2dca314226cffcc88b6df658167d62d8e310", + "target_xy": [ + -0.25, + 0.0 + ] + } + }, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state": "completed" + }, + "passed": true, + "policy": "explicitly_fake_diagnostic", + "processes": [ + { + "exit_code": -9, + "pid": 3840 + }, + { + "exit_code": 0, + "pid": 3871 + } + ], + "recovered": { + "active_job_id": "crash-one", + "activity": [ + { + "intent": "Owned host crash", + "kind": "queued", + "outcome": "crash-one", + "seq": 1, + "time": 1791833349.30804 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "e4f618c574d296bc87d0df4ef85a53d966ad506e39d16c706f9716de545cc815" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "start_job", + "seq": 2, + "time": 1791833349.3356001 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 3, + "time": 1791833349.3797069 + }, + { + "intent": "Owned host crash", + "kind": "rpc_applied", + "outcome": "start_job: command-03d2e4ff739e460eb40a5a84c39a0568", + "seq": 4, + "time": 1791833349.4458508 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "62a26824a64a1ad4836505d1ebf8c2e49d6c801868bbc0e00e6c193b2fb7b5c1" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 5, + "time": 1791833349.500184 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 6, + "time": 1791833349.5445702 + }, + { + "intent": "Owned host crash", + "kind": "rpc_applied", + "outcome": "observe: command-a34e5f40ea694de88a0d00cccb96c44e", + "seq": 7, + "time": 1791833349.9674509 + }, + { + "intent": "Owned host crash", + "kind": "inference", + "outcome": "1", + "seq": 8, + "time": 1791833349.9699688 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "5bd7eb93f05938c05571fa2b706fde225bb9e8da1ea77b40db8f30b9db8bc03c" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 9, + "time": 1791833349.985573 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 10, + "time": 1791833350.012773 + }, + { + "intent": "Owned host crash", + "kind": "rpc_applied", + "outcome": "propose: command-be09796e714a4770a1ea65b8d4f77984", + "seq": 11, + "time": 1791833350.052333 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "fbf6db7e5e5dba748d8e7256eb53dfcce926613a6528673bdb094443dcd0bf31" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 12, + "time": 1791833350.075737 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 13, + "time": 1791833350.100466 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Restarted with a retained command stage; check its exact receipt", + "seq": 14, + "time": 1791833350.6643 + } + ], + "app_id": "5005ae5a09f54caf921b0d50efc1e21d", + "connection": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Restarted with a retained command stage; check its exact receipt", + "job_id": "crash-one", + "label": "Owned host crash", + "last_intent": "Deterministic hard-crash conformance", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "Restarted with a retained command stage; check its exact receipt" + }, + "recovery": { + "command_id": "command-c2b7424f52d34ca0bc69c94f0cbb58a8", + "operation": "dispatch", + "reason": "Restarted with a retained command stage; check its exact receipt", + "state": "uncertain" + }, + "runner_status": "blocked", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0, + "token_usage_complete": false + } + }, + "relay": [ + { + "method": "GET", + "path": "/identity", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "bfd3c24404f92c534d2c51c8ec761aac769f7e74b0196be246a8d68f12790e94", + "status": 200 + }, + { + "method": "GET", + "path": "/identity", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "bfd3c24404f92c534d2c51c8ec761aac769f7e74b0196be246a8d68f12790e94", + "status": 200 + }, + { + "method": "GET", + "path": "/state", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "e4f618c574d296bc87d0df4ef85a53d966ad506e39d16c706f9716de545cc815", + "status": 200 + }, + { + "method": "GET", + "path": "/state", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "e4f618c574d296bc87d0df4ef85a53d966ad506e39d16c706f9716de545cc815", + "status": 200 + }, + { + "method": "GET", + "path": "/identity", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "bfd3c24404f92c534d2c51c8ec761aac769f7e74b0196be246a8d68f12790e94", + "status": 200 + }, + { + "method": "POST", + "path": "/lease", + "request": { + "client_id": "client-da07206d0f4e427a8f478eb3d661b900", + "duration_s": 30.0, + "expected_epoch": 1 + }, + "request_sha256": "390393d26bbbfaadc14c630028dcedc65987e21a62f2eff0d3a223f20b9f0c78", + "response_sha256": "f9182826c95c473cbbcb366a91cd51f3bfb7c5e9f1e53424f11b28d49cb4c1ff", + "status": 200 + }, + { + "method": "POST", + "path": "/commands", + "request": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "arguments": { + "job_id": "crash-one", + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "body_id": "c825e8a587194a60943c682034d8a9e0", + "client_id": "client-da07206d0f4e427a8f478eb3d661b900", + "command_id": "command-03d2e4ff739e460eb40a5a84c39a0568", + "epoch": 1, + "lease_id": "8eb668f30627421da8c5db08ba6e167c", + "operation": "start_job", + "protocol": "footwork.body/1", + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "request_sha256": "47c9e948e141f61db6abb5649205026f1aa38e6bd39171cec22e8b266313bb09", + "response_sha256": "5a6b4db9fca6759d1a5b1efd6ad1e5349c2f77f976f499d90b373970e5cbf127", + "status": 200 + }, + { + "method": "GET", + "path": "/state", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "62a26824a64a1ad4836505d1ebf8c2e49d6c801868bbc0e00e6c193b2fb7b5c1", + "status": 200 + }, + { + "method": "GET", + "path": "/identity", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "bfd3c24404f92c534d2c51c8ec761aac769f7e74b0196be246a8d68f12790e94", + "status": 200 + }, + { + "method": "POST", + "path": "/lease", + "request": { + "client_id": "client-da07206d0f4e427a8f478eb3d661b900", + "duration_s": 30.0, + "expected_epoch": 1 + }, + "request_sha256": "390393d26bbbfaadc14c630028dcedc65987e21a62f2eff0d3a223f20b9f0c78", + "response_sha256": "bf7413e4f21552abbe404933240b3a4e3f237ecc361a0e0ab3a104a45e56db4f", + "status": 200 + }, + { + "method": "POST", + "path": "/commands", + "request": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "arguments": {}, + "body_id": "c825e8a587194a60943c682034d8a9e0", + "client_id": "client-da07206d0f4e427a8f478eb3d661b900", + "command_id": "command-a34e5f40ea694de88a0d00cccb96c44e", + "epoch": 1, + "lease_id": "8d8a4b37f3c34b9dbeaa23b9e7397d96", + "operation": "observe", + "protocol": "footwork.body/1", + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "request_sha256": "2198bbada7932578c0150514205dfd089e3f738523bda232b91750d516313d79", + "response_sha256": "09712c09393a73d10d9d8a17229fa4e43d3df8974528eb390c2293c080229eb7", + "status": 200 + }, + { + "method": "GET", + "path": "/images/challenge-b999f8f581a04dfdb0032544e739a4d7", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "status": 200 + }, + { + "method": "GET", + "path": "/state", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "5bd7eb93f05938c05571fa2b706fde225bb9e8da1ea77b40db8f30b9db8bc03c", + "status": 200 + }, + { + "method": "GET", + "path": "/identity", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "bfd3c24404f92c534d2c51c8ec761aac769f7e74b0196be246a8d68f12790e94", + "status": 200 + }, + { + "method": "POST", + "path": "/lease", + "request": { + "client_id": "client-da07206d0f4e427a8f478eb3d661b900", + "duration_s": 30.0, + "expected_epoch": 1 + }, + "request_sha256": "390393d26bbbfaadc14c630028dcedc65987e21a62f2eff0d3a223f20b9f0c78", + "response_sha256": "0fab0a96bfb25070fb9b3ba7ab87728be31ce0a92563c63f5525f288b084865f", + "status": 200 + }, + { + "method": "POST", + "path": "/commands", + "request": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "arguments": { + "action": "move", + "challenge_id": "challenge-b999f8f581a04dfdb0032544e739a4d7", + "intent": "Deterministic hard-crash conformance", + "target_x": -0.25, + "target_y": 0.0 + }, + "body_id": "c825e8a587194a60943c682034d8a9e0", + "client_id": "client-da07206d0f4e427a8f478eb3d661b900", + "command_id": "command-be09796e714a4770a1ea65b8d4f77984", + "epoch": 1, + "lease_id": "07e1a945baea4598b6a6b4e025f2baa5", + "operation": "propose", + "protocol": "footwork.body/1", + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "request_sha256": "4d756ee8c549f4b279b07c0962eeb0cfaf5e0d96d92900f449ebd9d831f985b0", + "response_sha256": "83fc5c7ff272a682b94ec3dcb216074f5ef138d9cbac7ab17c13cf6635428309", + "status": 200 + }, + { + "method": "GET", + "path": "/state", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "fbf6db7e5e5dba748d8e7256eb53dfcce926613a6528673bdb094443dcd0bf31", + "status": 200 + }, + { + "method": "GET", + "path": "/identity", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "bfd3c24404f92c534d2c51c8ec761aac769f7e74b0196be246a8d68f12790e94", + "status": 200 + }, + { + "method": "POST", + "path": "/lease", + "request": { + "client_id": "client-da07206d0f4e427a8f478eb3d661b900", + "duration_s": 30.0, + "expected_epoch": 1 + }, + "request_sha256": "390393d26bbbfaadc14c630028dcedc65987e21a62f2eff0d3a223f20b9f0c78", + "response_sha256": "e0d3fea867c8ddd2785f5eec2fc6763f5d0eacf852201a58a5a278c1e720598f", + "status": 200 + }, + { + "method": "POST", + "path": "/commands", + "request": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "arguments": { + "proposal_id": "proposal-5c78c35087284888a2bfb0207d0d2eed" + }, + "body_id": "c825e8a587194a60943c682034d8a9e0", + "client_id": "client-da07206d0f4e427a8f478eb3d661b900", + "command_id": "command-c2b7424f52d34ca0bc69c94f0cbb58a8", + "epoch": 1, + "lease_id": "8cc0cbe49c6c49c999d2a67e7ace38c8", + "operation": "dispatch", + "protocol": "footwork.body/1", + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "request_sha256": "4bb621a7caca66ad01ffe6be451549dc6333e24b784f85ca74b3a6b998eb3c91", + "response_sha256": "c739e418eac5204fcfacb4d352b0175babf96d1d8494d51931b38585ce872c5c", + "status": 200 + }, + { + "method": "GET", + "path": "/identity", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "bfd3c24404f92c534d2c51c8ec761aac769f7e74b0196be246a8d68f12790e94", + "status": 200 + }, + { + "method": "GET", + "path": "/state", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "ab274e2e0cd265e0471237fdab72a162ea09663f096e87c65128c54d0f56c55c", + "status": 200 + }, + { + "method": "GET", + "path": "/commands/command-c2b7424f52d34ca0bc69c94f0cbb58a8?client_id=client-da07206d0f4e427a8f478eb3d661b900&lease_id=8cc0cbe49c6c49c999d2a67e7ace38c8&epoch=1&request_sha256=4bb621a7caca66ad01ffe6be451549dc6333e24b784f85ca74b3a6b998eb3c91", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "c739e418eac5204fcfacb4d352b0175babf96d1d8494d51931b38585ce872c5c", + "status": 200 + }, + { + "method": "GET", + "path": "/state", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "8d7713545f6438f7f9e2eaf795995da13d300e7da95adf3ed2301ebf73011026", + "status": 200 + }, + { + "method": "GET", + "path": "/state", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "2a3f4fc1742686e469b4e44df8883079f89f9953429c8e7aa4d4cbf2cde05fcc", + "status": 200 + }, + { + "method": "GET", + "path": "/identity", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "bfd3c24404f92c534d2c51c8ec761aac769f7e74b0196be246a8d68f12790e94", + "status": 200 + }, + { + "method": "POST", + "path": "/lease", + "request": { + "client_id": "client-da07206d0f4e427a8f478eb3d661b900", + "duration_s": 30.0, + "expected_epoch": 1 + }, + "request_sha256": "390393d26bbbfaadc14c630028dcedc65987e21a62f2eff0d3a223f20b9f0c78", + "response_sha256": "987ca5ad26a4a6643fdc9d19f8c5e7b59c377fd8ff62dd2ff097ff5da67f9c5b", + "status": 200 + }, + { + "method": "POST", + "path": "/commands", + "request": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "arguments": {}, + "body_id": "c825e8a587194a60943c682034d8a9e0", + "client_id": "client-da07206d0f4e427a8f478eb3d661b900", + "command_id": "command-5ecd4e56fd0c483ba2b5eebe5dbcf613", + "epoch": 1, + "lease_id": "011b4a9f2b9b4cebbc38da2f08fac7ce", + "operation": "observe", + "protocol": "footwork.body/1", + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "request_sha256": "fd900bb330347f99a41a1ff0fe3a0e19ceb2f8d997a3b95e1e03e1145f4041b3", + "response_sha256": "09704131ad52803e942fb6ee47136c1a07bc88b542682b94048eac2e33bf3b75", + "status": 200 + }, + { + "method": "GET", + "path": "/images/challenge-43fe388fc4544ac0a1b88399629decbb", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "9f6339a75fcf48ca514a5e83f5fdc4ab26b70239e87cfc4a7d5d10be44830b32", + "status": 200 + } + ], + "settled": { + "active_job_id": "crash-one", + "activity": [ + { + "intent": "Owned host crash", + "kind": "queued", + "outcome": "crash-one", + "seq": 1, + "time": 1791833349.30804 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "e4f618c574d296bc87d0df4ef85a53d966ad506e39d16c706f9716de545cc815" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "start_job", + "seq": 2, + "time": 1791833349.3356001 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 3, + "time": 1791833349.3797069 + }, + { + "intent": "Owned host crash", + "kind": "rpc_applied", + "outcome": "start_job: command-03d2e4ff739e460eb40a5a84c39a0568", + "seq": 4, + "time": 1791833349.4458508 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "62a26824a64a1ad4836505d1ebf8c2e49d6c801868bbc0e00e6c193b2fb7b5c1" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 5, + "time": 1791833349.500184 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 6, + "time": 1791833349.5445702 + }, + { + "intent": "Owned host crash", + "kind": "rpc_applied", + "outcome": "observe: command-a34e5f40ea694de88a0d00cccb96c44e", + "seq": 7, + "time": 1791833349.9674509 + }, + { + "intent": "Owned host crash", + "kind": "inference", + "outcome": "1", + "seq": 8, + "time": 1791833349.9699688 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "5bd7eb93f05938c05571fa2b706fde225bb9e8da1ea77b40db8f30b9db8bc03c" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 9, + "time": 1791833349.985573 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 10, + "time": 1791833350.012773 + }, + { + "intent": "Owned host crash", + "kind": "rpc_applied", + "outcome": "propose: command-be09796e714a4770a1ea65b8d4f77984", + "seq": 11, + "time": 1791833350.052333 + }, + { + "evidence": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "fbf6db7e5e5dba748d8e7256eb53dfcce926613a6528673bdb094443dcd0bf31" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 12, + "time": 1791833350.075737 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 13, + "time": 1791833350.100466 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Restarted with a retained command stage; check its exact receipt", + "seq": 14, + "time": 1791833350.6643 + }, + { + "intent": "Owned host crash", + "kind": "rpc_applied", + "outcome": "dispatch: command-c2b7424f52d34ca0bc69c94f0cbb58a8", + "seq": 15, + "time": 1791833350.7481759 + }, + { + "intent": "", + "kind": "reconciled", + "outcome": "command-c2b7424f52d34ca0bc69c94f0cbb58a8", + "seq": 16, + "time": 1791833350.749714 + } + ], + "app_id": "5005ae5a09f54caf921b0d50efc1e21d", + "connection": { + "adapter_id": "7207e9511a3241c291d7ebb313a1c98b", + "body_id": "c825e8a587194a60943c682034d8a9e0", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": null, + "job_id": "crash-one", + "label": "Owned host crash", + "last_intent": "Deterministic hard-crash conformance", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": false, + "reason": "Exact historical stage recovered; explicit Resume observes fresh evidence" + }, + "recovery": { + "command_id": "command-c2b7424f52d34ca0bc69c94f0cbb58a8", + "operation": "dispatch", + "reason": "Exact historical stage recovered; explicit Resume observes fresh evidence", + "state": "recovered" + }, + "runner_status": "paused", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0, + "token_usage_complete": false + } + } +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-completed.png b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-completed.png new file mode 100644 index 0000000..67aae76 Binary files /dev/null and b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-completed.png differ diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-completed.txt b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-completed.txt new file mode 100644 index 0000000..dafe937 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-completed.txt @@ -0,0 +1,79 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51389/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51389/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 checkbox Workbench crash while uncertain Completed, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 12 text Workbench crash while uncertain + 13 text Running + 14 button Pause, ID: pause + 15 container Description: Body connection, ID: network-card + 16 text Body connected + 17 text Body identity verified by read-only TCP health + 18 button Disconnect, ID: disconnect + 19 button (disabled) Check receipt, ID: reconcile + 20 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 21 text Test the simulated connection + 22 container Camera observation + 23 text OVERHEAD CAMERA + 24 text Last camera observation + 25 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 26 text Orange part Blue pusher Green task region + 27 text Captured · generation 10 + 28 text COMPLETED + 29 text Measurement recorded and reset verified. The bench is ready for another job. + 30 container Job progress + 31 text 1 Prepare 2 Measure 3 Reset 4 Ready + 32 container Measurement + 33 heading Measurement, Value: 2, ID: measurement-heading + 34 text Measurement + 35 text Historical result + 36 container measurement-result + 37 text 1,726 + 38 text Orange pixels + 39 container + 40 text 4,794.4 + 41 text mm² + 42 text Apparent footprint + 43 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 44 button Repeat job, ID: repeat + 45 container Activity + 46 heading Activity, Value: 2, ID: activity-heading + 47 text Activity + 48 text 24 events + 49 content list activity + 50 container + 51 text 04:05 PM Action receipt checked Workbench crash while uncertain + 52 container + 53 text 04:05 PM Sending a guarded action + 54 container + 55 text 04:05 PM Fresh body check verified The next command uses current body evidence. + 56 container + 57 text 04:05 PM Action proposal accepted Workbench crash while uncertain + 58 container + 59 text 04:05 PM Checking the planned action + 60 container + 61 text 04:05 PM Fresh body check verified The next command uses current body evidence. + 62 container + 63 text 04:05 PM AI planning requested Workbench crash while uncertain + 64 container + 65 text 04:05 PM Camera observation received Workbench crash while uncertain + 66 container + 67 text 04:05 PM Requesting camera observation + 68 container + 69 text 04:05 PM Fresh body check verified The next command uses current body evidence. + 70 container + 71 text 04:05 PM Action receipt checked Workbench crash while uncertain + 72 container + 73 text 04:05 PM Sending a guarded action + 74 button (collapsed) Session details, Secondary Actions: Expand + 75 text Session details + 76 text 13 model requests · Reported tokens: 170,285 input / 2,031 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51389/ \ No newline at end of file diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-ledger.json new file mode 100644 index 0000000..d51b46d --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-ledger.json @@ -0,0 +1,34 @@ +{ + "boundaries": [ + { + "case": "host-uncertain", + "command_id": "command-2fa2310dcde0409ba65e9430c4e08a9a", + "finished_snapshot": "000004-finished.json", + "gate": null, + "new_epoch": 1, + "offline_snapshot": "000002-offline.json", + "old_epoch": 1, + "pre_snapshot": "000001-before.json", + "process": { + "boot": 1, + "children_after": [], + "children_before": [], + "exit_observed": true, + "group": 60458, + "initialized": false, + "log": "workbench-01.log", + "pid": 60458, + "port": 51389, + "returncode": -9, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-uncertain-v1/operator", + "signal": "SIGKILL", + "stopped_time": 1791835389.350252 + }, + "request_sha256": "1693f3dd8b1f58c134b589b33687988cc30687c3f71f911016c7424c56b846c0", + "restored_snapshot": "000003-restored.json", + "role": "workbench" + } + ], + "case": "host-uncertain" +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-recovered.png b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-recovered.png new file mode 100644 index 0000000..9936e80 Binary files /dev/null and b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-recovered.png differ diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-recovered.txt b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-recovered.txt new file mode 100644 index 0000000..7d2d5d7 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-recovered.txt @@ -0,0 +1,72 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51389/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51389/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 69 checkbox Workbench crash while uncertain Paused, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 70 text Workbench crash while uncertain + 71 text Paused + 14 button Resume, ID: pause + 72 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue. + 16 container Description: Body connection, ID: network-card + 73 text Body connected + 74 text Body identity verified by read-only TCP health + 19 button Disconnect, ID: disconnect + 20 button (disabled) Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 75 text Last observation · Workbench crash while uncertain + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 76 text Captured · generation 0 + 77 text RECEIPT RECOVERED + 78 text The exact command receipt was recovered. The agent is paused until you resume. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 79 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 80 text 18 events + 42 content list activity + 81 container + 82 text 04:03 PM Command receipt recovered The agent remains paused until you resume. + 83 container + 84 text 04:03 PM Action receipt checked Workbench crash while uncertain + 85 container + 86 text 04:03 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt + 87 container + 88 text 04:02 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 89 container + 90 text 04:02 PM Sending a guarded action + 91 container + 92 text 04:02 PM Fresh body check verified The next command uses current body evidence. + 93 container + 94 text 04:02 PM Action proposal accepted Workbench crash while uncertain + 95 container + 96 text 04:02 PM Checking the planned action + 97 container + 98 text 04:02 PM Fresh body check verified The next command uses current body evidence. + 99 container + 100 text 04:02 PM AI planning requested Workbench crash while uncertain + 101 container + 102 text 04:02 PM Camera observation received Workbench crash while uncertain + 103 container + 104 text 04:02 PM Requesting camera observation + 67 button (collapsed) Session details, Secondary Actions: Expand + 68 text Session details + 105 text 1 model requests · Reported tokens: 13,071 input / 236 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51389/ \ No newline at end of file diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-restored.png b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-restored.png new file mode 100644 index 0000000..b21b526 Binary files /dev/null and b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-restored.png differ diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-restored.txt b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-restored.txt new file mode 100644 index 0000000..8303edd --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/crash-restored.txt @@ -0,0 +1,72 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51389/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51389/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 checkbox Workbench crash while uncertain Needs attention, Value: 1 + 9 container + 10 heading Bench, Value: 1 + 11 text Bench + 12 text Workbench crash while uncertain + 13 text Needs attention + 14 button (disabled) Pause, ID: pause + 15 text Restarted with a retained command stage; check its exact receipt + 16 container Description: Body connection, ID: network-card + 17 text Body connected + 18 text Body identity verified by read-only TCP health + 19 button Disconnect, ID: disconnect + 20 button Check receipt, ID: reconcile + 21 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 22 text Test the simulated connection + 23 container Camera observation + 24 text OVERHEAD CAMERA + 25 text Last observation · Workbench crash while uncertain + 26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 27 text Orange part Blue pusher Green task region + 28 text Captured · generation 0 + 29 text OUTCOME UNCERTAIN + 30 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 31 container Job progress + 32 text 1 Prepare 2 Measure 3 Reset 4 Ready + 33 container Measurement + 34 heading Measurement, Value: 2, ID: measurement-heading + 35 text Measurement + 36 text Pending + 37 text The part’s visible footprint will be measured after its placement is verified. + 38 container Activity + 39 heading Activity, Value: 2, ID: activity-heading + 40 text Activity + 41 text 16 events + 42 content list activity + 43 container + 44 text 04:03 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt + 45 container + 46 text 04:02 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 47 container + 48 text 04:02 PM Sending a guarded action + 49 container + 50 text 04:02 PM Fresh body check verified The next command uses current body evidence. + 51 container + 52 text 04:02 PM Action proposal accepted Workbench crash while uncertain + 53 container + 54 text 04:02 PM Checking the planned action + 55 container + 56 text 04:02 PM Fresh body check verified The next command uses current body evidence. + 57 container + 58 text 04:02 PM AI planning requested Workbench crash while uncertain + 59 container + 60 text 04:02 PM Camera observation received Workbench crash while uncertain + 61 container + 62 text 04:02 PM Requesting camera observation + 63 container + 64 text 04:02 PM Fresh body check verified The next command uses current body evidence. + 65 container + 66 text 04:02 PM Inspection started Workbench crash while uncertain + 67 button (collapsed) Session details, Secondary Actions: Expand + 68 text Session details + 69 text 1 model requests · Reported tokens: 13,071 input / 236 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51389/ \ No newline at end of file diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/final-public.json b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/final-public.json new file mode 100644 index 0000000..f343f34 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/final-public.json @@ -0,0 +1,290 @@ +{ + "active_job_id": null, + "activity": [ + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "propose: command-74b3b95dec2746d48110346646c76136", + "seq": 116, + "time": 1791835522.543715 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "523fb1fbe8aa5668536aaccfa1ad4f90bdd1f8a43abdcdc89979320c1f28f386" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 117, + "time": 1791835522.595297 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 118, + "time": 1791835522.635673 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "dispatch: command-455db4ba7be24f0f86007a5e078d859c", + "seq": 119, + "time": 1791835522.881181 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "2cfb9f85218990633d443312264b1c0932395f2efdee1ab957b1afb4a6cc2a4a" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 120, + "time": 1791835522.935346 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 121, + "time": 1791835522.975528 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "observe: command-4fa58c52e18449049cd127551c10d65a", + "seq": 122, + "time": 1791835523.1529238 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "inference", + "outcome": "12", + "seq": 123, + "time": 1791835523.1555321 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "80985a144bda35c03b9eecc3714c9543a952fecafeaf5f978acee3abb5f9f65d" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 124, + "time": 1791835530.8459678 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 125, + "time": 1791835530.885638 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "propose: command-e508dc3e450d45ecb4718799a04de88a", + "seq": 126, + "time": 1791835530.965694 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "5acae7b5c983ea1999c46262c8c7d948e040bf21bcc8a02342f5beb0fd1bcfc3" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 127, + "time": 1791835531.021473 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 128, + "time": 1791835531.063702 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "dispatch: command-d13efa2ea6fd403c88c117fbb2965866", + "seq": 129, + "time": 1791835531.3077571 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "996feeb0e6749ea7e79dda4cd906c7f659bc829a4be722ab5fd3c3712ecc3b74" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 130, + "time": 1791835531.364406 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 131, + "time": 1791835531.4072561 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "observe: command-f9d526ca594e4ae2ad902fb383f55b43", + "seq": 132, + "time": 1791835531.589549 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "inference", + "outcome": "13", + "seq": 133, + "time": 1791835531.59181 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "1c66858fa03bcc744b6b022536d3233761b27dd324d32864a7d9350ffc3c42ce" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 134, + "time": 1791835537.6027908 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 135, + "time": 1791835537.647042 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "propose: command-36ff9689326e44828768e0d99e2605ab", + "seq": 136, + "time": 1791835537.7378528 + }, + { + "evidence": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "e73ac7e746b7b7194cfb6daba9641212bf2205d50d0151528e4bf6385d29abdf" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 137, + "time": 1791835537.796623 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 138, + "time": 1791835537.8384042 + }, + { + "intent": "Workbench crash while uncertain", + "kind": "rpc_applied", + "outcome": "dispatch: command-ab8fd2b2180747feaa57a5758a01254e", + "seq": 139, + "time": 1791835538.263912 + } + ], + "app_id": "676909dee6e1429abdcfa2e3698650cd", + "connection": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=612118c97622874a02927de51dfa015cd96bfdc46dbc7c11146a229288798f12" + }, + "jobs": [ + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "label": "Workbench crash while uncertain", + "last_intent": "Finish the reset now that the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "block_home_verified": true, + "generation": 10, + "image_sha256": "612118c97622874a02927de51dfa015cd96bfdc46dbc7c11146a229288798f12", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "kind": "finish_reset", + "native_time_s": 21.943999999998717, + "operation_id": "agent-f367be891109464b8c33dd8c992de912", + "pusher_home_verified": true, + "reset_contact_samples": 2986, + "reset_native_steps": 8205, + "state_sha256": "bcf29e5d41bc6334624f8f04b0bb0bdf4f3dda8e08e52bd9e10b31b37d71ac62" + }, + "status": "completed", + "target": "A" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": true, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 13, + "input_tokens": 170285, + "output_tokens": 2031, + "reported_calls": 13, + "token_usage_complete": true + } +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/hard-crash-score.json b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/hard-crash-score.json new file mode 100644 index 0000000..a793627 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/hard-crash-score.json @@ -0,0 +1,544 @@ +{ + "admissions": [ + { + "command_id": "command-b5ab2ab4a67a42a4b68e47a0451c6f3d", + "passed": true, + "proof_event": 3 + }, + { + "command_id": "command-d861918ac29d4450a4c243ca8d00dbe8", + "passed": true, + "proof_event": 6 + }, + { + "command_id": "command-c9785072c3bc4661b774e2475031bb9b", + "passed": true, + "proof_event": 10 + }, + { + "command_id": "command-2fa2310dcde0409ba65e9430c4e08a9a", + "passed": true, + "proof_event": 13 + }, + { + "command_id": "command-e77a9f941b704deb81dcce409213e035", + "passed": true, + "proof_event": 20 + }, + { + "command_id": "command-80c2aefafd8e4bdf8e74f92ef73791ca", + "passed": true, + "proof_event": 24 + }, + { + "command_id": "command-e1b827b5a80e45a7bec01150c1f8bad1", + "passed": true, + "proof_event": 27 + }, + { + "command_id": "command-5931d2857b9c4665a505894214cab5d9", + "passed": true, + "proof_event": 30 + }, + { + "command_id": "command-768709b5c3b64a8c98d0adcdca3e8ddb", + "passed": true, + "proof_event": 34 + }, + { + "command_id": "command-e65c8b59ee5643f899bd34e44581a585", + "passed": true, + "proof_event": 37 + }, + { + "command_id": "command-12cef9173c3b4d1c842989dbd5e0db49", + "passed": true, + "proof_event": 40 + }, + { + "command_id": "command-39afdcb8609741649d9540250d935bec", + "passed": true, + "proof_event": 44 + }, + { + "command_id": "command-ab7d627538474080b82ad85b87fce1f4", + "passed": true, + "proof_event": 47 + }, + { + "command_id": "command-98ff1b300edc4fbbbe4093290e1139fe", + "passed": true, + "proof_event": 50 + }, + { + "command_id": "command-5e33f11524c14af9a8f201db539c4ee2", + "passed": true, + "proof_event": 54 + }, + { + "command_id": "command-02d71519b14449b0b86b1184566b36c3", + "passed": true, + "proof_event": 57 + }, + { + "command_id": "command-a4710df82b284a508e1a847f1320c4c2", + "passed": true, + "proof_event": 60 + }, + { + "command_id": "command-27a7ed8c8a6f43228fb80d12758c13f5", + "passed": true, + "proof_event": 64 + }, + { + "command_id": "command-dd4a9ad761d5444d910a84d5b8bf69ac", + "passed": true, + "proof_event": 67 + }, + { + "command_id": "command-478a3220cf6e42bbb26901b1c68334e8", + "passed": true, + "proof_event": 70 + }, + { + "command_id": "command-8dd7e64ef6f74937832ae99a4ed4f7a7", + "passed": true, + "proof_event": 74 + }, + { + "command_id": "command-508af382f60b4a0599da57e09f4ac8b4", + "passed": true, + "proof_event": 77 + }, + { + "command_id": "command-64602f59aa0245678f28409831413d9b", + "passed": true, + "proof_event": 80 + }, + { + "command_id": "command-599fd200f1a04ca5ab9e3eaca855a66b", + "passed": true, + "proof_event": 84 + }, + { + "command_id": "command-078ab24848f9423184a37c61c7d3afa1", + "passed": true, + "proof_event": 87 + }, + { + "command_id": "command-e95c9a09f7da4e8597b507c7be0602f2", + "passed": true, + "proof_event": 90 + }, + { + "command_id": "command-005180707ed049fd81c9d437d754d48b", + "passed": true, + "proof_event": 94 + }, + { + "command_id": "command-49fe8749ae434b97972183ffdb94d613", + "passed": true, + "proof_event": 97 + }, + { + "command_id": "command-5d114c12499247c79c5369254a22be8c", + "passed": true, + "proof_event": 100 + }, + { + "command_id": "command-e86bd687521e45dfa3401002d4dd2d74", + "passed": true, + "proof_event": 104 + }, + { + "command_id": "command-8d4bdbdac3cf4c00ba044b2998c5bdbc", + "passed": true, + "proof_event": 107 + }, + { + "command_id": "command-c88cf0895a824c5e963836b036911c72", + "passed": true, + "proof_event": 110 + }, + { + "command_id": "command-74b3b95dec2746d48110346646c76136", + "passed": true, + "proof_event": 114 + }, + { + "command_id": "command-455db4ba7be24f0f86007a5e078d859c", + "passed": true, + "proof_event": 117 + }, + { + "command_id": "command-4fa58c52e18449049cd127551c10d65a", + "passed": true, + "proof_event": 120 + }, + { + "command_id": "command-e508dc3e450d45ecb4718799a04de88a", + "passed": true, + "proof_event": 124 + }, + { + "command_id": "command-d13efa2ea6fd403c88c117fbb2965866", + "passed": true, + "proof_event": 127 + }, + { + "command_id": "command-f9d526ca594e4ae2ad902fb383f55b43", + "passed": true, + "proof_event": 130 + }, + { + "command_id": "command-36ff9689326e44828768e0d99e2605ab", + "passed": true, + "proof_event": 134 + }, + { + "command_id": "command-ab8fd2b2180747feaa57a5758a01254e", + "passed": true, + "proof_event": 137 + } + ], + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "browser": { + "passed": true, + "required_names": [ + "crash-completed", + "crash-offline", + "crash-recovered", + "crash-restored", + "crash-resumed" + ] + }, + "calls": [ + { + "action": "move", + "call": 1, + "camera_generation": 0, + "challenge_id": "challenge-a97dbf1c551b4034af14d69549482cda", + "command_id": "command-2fa2310dcde0409ba65e9430c4e08a9a", + "image_sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 68332, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-d486164bb6714d308aba235510a291ce" + }, + { + "action": "move", + "call": 2, + "camera_generation": 1, + "challenge_id": "challenge-99428f3c79b94a98b829fd3692aa3371", + "command_id": "command-e1b827b5a80e45a7bec01150c1f8bad1", + "image_sha256": "cb4221e600bbb24aec549f49924fc9224876a1ad8d04159fe0cc3798658d8357", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 73775, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-c6c9a49656c540fca60ae346c7b30a17" + }, + { + "action": "measure", + "call": 3, + "camera_generation": 2, + "challenge_id": "challenge-33aaf5ed4c92463ca0378683db52efce", + "command_id": "command-e65c8b59ee5643f899bd34e44581a585", + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 74329, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-3551f3a373c14744b210c169b3e89ec9" + }, + { + "action": "begin_reset", + "call": 4, + "camera_generation": 2, + "challenge_id": "challenge-08c7e2baa1044b4597e250d6e43ea760", + "command_id": "command-ab7d627538474080b82ad85b87fce1f4", + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 74861, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-3707e2e7e17548fbb0e161ef08b6986b" + }, + { + "action": "move", + "call": 5, + "camera_generation": 2, + "challenge_id": "challenge-b357bf9f5afe4942a38de7a40227fd19", + "command_id": "command-02d71519b14449b0b86b1184566b36c3", + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 75386, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-d9ec5e9e84e54150a8f0e02c21dff249" + }, + { + "action": "move", + "call": 6, + "camera_generation": 3, + "challenge_id": "challenge-0d5e1f62c686409097da57c5c6b3e556", + "command_id": "command-dd4a9ad761d5444d910a84d5b8bf69ac", + "image_sha256": "aeb57b730bc64cb1ec47fbf9a42ef0acce36a927702e60d6df1d751e4d240d1e", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 77914, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-4ba2dccae49c40c4a5fa1f9b0aafea35" + }, + { + "action": "move", + "call": 7, + "camera_generation": 4, + "challenge_id": "challenge-1c03c8ca6e68496daafd129881c2fbce", + "command_id": "command-508af382f60b4a0599da57e09f4ac8b4", + "image_sha256": "c62187cd908d71592bdd7193b83ec80666e6f350e90968204d2d87a7169d12dc", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 78895, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-cbac77bcd741483690ba4f13f697c573" + }, + { + "action": "move", + "call": 8, + "camera_generation": 5, + "challenge_id": "challenge-5ee5c898bc794f7387b502fae84e7b10", + "command_id": "command-078ab24848f9423184a37c61c7d3afa1", + "image_sha256": "e6f35d68a39eccb69ef08ffb81de2aed231bbe097a530bc31bb48ffda662a3f1", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 79616, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-ff9ada4626e743a59a766cb3edf9c032" + }, + { + "action": "move", + "call": 9, + "camera_generation": 6, + "challenge_id": "challenge-6ca56857e99f486f9367c3614fd86281", + "command_id": "command-49fe8749ae434b97972183ffdb94d613", + "image_sha256": "426953b21f432cadca001da281f59968cb61a858bd5df8c9c7e188b27f63d899", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 80198, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-34db9532532b4627ba740fee716556cc" + }, + { + "action": "move", + "call": 10, + "camera_generation": 7, + "challenge_id": "challenge-21cd80f8523846c281a7d2d89fea618e", + "command_id": "command-8d4bdbdac3cf4c00ba044b2998c5bdbc", + "image_sha256": "af56b49e6590dbca9bbb02294383fb67489e9cf81696ed61c3e36f3a22ed2bd2", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 80720, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-ee98bc837f5145738df1b4f27eb5f8f7" + }, + { + "action": "move", + "call": 11, + "camera_generation": 8, + "challenge_id": "challenge-8d80203d05914df0b31ae3b5d2f3d5b0", + "command_id": "command-455db4ba7be24f0f86007a5e078d859c", + "image_sha256": "ae6914c4cca4661288100733224d27134358b7e918f71199461123802e0ad4b3", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 81270, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-da7eb5bc3a854909b1c90707e7614ab0" + }, + { + "action": "move", + "call": 12, + "camera_generation": 9, + "challenge_id": "challenge-1d1a22dcf8434777a40325233d9f3243", + "command_id": "command-d13efa2ea6fd403c88c117fbb2965866", + "image_sha256": "5abd6afc69aa721d74bdd9226b4a65349264ae46454f349207a2f0d5811a0913", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 81909, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-cc001c061a294ffc8c41df59ec4f7b69" + }, + { + "action": "finish_reset", + "call": 13, + "camera_generation": 10, + "challenge_id": "challenge-f223cfaee2584ab78c40d4de8198a7fa", + "command_id": "command-ab8fd2b2180747feaa57a5758a01254e", + "image_sha256": "612118c97622874a02927de51dfa015cd96bfdc46dbc7c11146a229288798f12", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "model_pid": 82944, + "native_gap": false, + "passed": true, + "proposal_id": "proposal-8876a3d843964011989958dd78cca956" + } + ], + "case": "host-uncertain", + "completed_cycles": 1, + "crash": { + "case": "host-uncertain", + "command_id": "command-2fa2310dcde0409ba65e9430c4e08a9a", + "new_epoch": 1, + "old_epoch": 1, + "passed": true, + "reconciled_sequence": 18, + "request_sha256": "1693f3dd8b1f58c134b589b33687988cc30687c3f71f911016c7424c56b846c0", + "restored_sequence": 16, + "resume_sequence": 19, + "role": "workbench", + "snapshot_command_count": 4 + }, + "event_cutoff": 139, + "native": { + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "measurement_receipt": { + "bench_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.533999999999613, + "operation_id": "agent-4d0ed2500410448c83feda36cd01aea2", + "source_kind": "mujoco_simulation", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3", + "version": 1, + "worker_pid": 74843 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06582868246696554, + -8.393868978677625e-11, + 0.034995730455369786 + ], + "external_force_cleared": true, + "generation": 2, + "native_time_s": 5.533999999999613, + "pusher_home": false, + "pusher_xyz": [ + -0.049999969408768885, + 8.057882667829012e-26, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3" + }, + "model_calls": 13, + "passed": true, + "reset_receipt": { + "bench_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "block_home_verified": true, + "generation": 10, + "image_sha256": "612118c97622874a02927de51dfa015cd96bfdc46dbc7c11146a229288798f12", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "kind": "finish_reset", + "native_time_s": 21.943999999998717, + "operation_id": "agent-f367be891109464b8c33dd8c992de912", + "pusher_home_verified": true, + "reset_contact_samples": 2986, + "reset_native_steps": 8205, + "state_sha256": "bcf29e5d41bc6334624f8f04b0bb0bdf4f3dda8e08e52bd9e10b31b37d71ac62" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.09574292320179682, + -1.9256790800764786e-09, + 0.03499573045536984 + ], + "external_force_cleared": true, + "generation": 10, + "native_time_s": 21.943999999998717, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "bcf29e5d41bc6334624f8f04b0bb0bdf4f3dda8e08e52bd9e10b31b37d71ac62" + } + }, + "owned_processes_exited": true, + "passed": true, + "safely_blocked": false, + "scope": "selected process SIGKILL boundaries; actual image-only Codex browser; loopback simulation; no arbitrary power-loss guarantee", + "single_wire_post_every_command": true, + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050", + "source_unchanged": true, + "sqlite": { + "count": 1, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "operation_id": "agent-4d0ed2500410448c83feda36cd01aea2", + "receipt": { + "bench_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "generation": 2, + "image_sha256": "0036a91468be0843afde149e0b028384dc45405c715d049191b94fda59392463", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.533999999999613, + "operation_id": "agent-4d0ed2500410448c83feda36cd01aea2", + "source_kind": "mujoco_simulation", + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3", + "version": 1, + "worker_pid": 74843 + }, + "state_sha256": "ebb7a34ed4811733325084e937c4752a7fe9349c170fff8a2f3d5243bae73cf3" + } + ] + }, + "transport_command_count": 40, + "version": 1 +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/process-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/process-ledger.json new file mode 100644 index 0000000..a06fdbd --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/process-ledger.json @@ -0,0 +1,63 @@ +{ + "app_id": "676909dee6e1429abdcfa2e3698650cd", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "body_origin": "http://127.0.0.1:51387", + "browser_origin": "http://127.0.0.1:51389", + "case": "host-uncertain", + "owned_processes_exited": true, + "planner": "actual_codex_cli_images_over_TCP", + "processes": [ + { + "boot": 1, + "exit_observed": true, + "group": 60446, + "initialized": true, + "log": "body-01.log", + "pid": 60446, + "port": 51387, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-uncertain-v1/body", + "stopped_time": 1791835655.0880818 + }, + { + "exit_observed": true, + "group": 60454, + "pid": 60454, + "returncode": 0, + "role": "initialization" + }, + { + "boot": 1, + "children_after": [], + "children_before": [], + "exit_observed": true, + "group": 60458, + "initialized": false, + "log": "workbench-01.log", + "pid": 60458, + "port": 51389, + "returncode": -9, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-uncertain-v1/operator", + "signal": "SIGKILL", + "stopped_time": 1791835389.350252 + }, + { + "boot": 2, + "exit_observed": true, + "group": 72661, + "initialized": false, + "log": "workbench-02.log", + "pid": 72661, + "port": 51389, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-uncertain-v1/operator", + "stopped_time": 1791835654.993075 + } + ], + "ready": true, + "simulation": true, + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050" +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/source-freeze.json b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/source-freeze.json new file mode 100644 index 0000000..a73cb87 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/source-freeze.json @@ -0,0 +1,37 @@ +{ + "commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/body_client.py": "40388c238192d923888c9852b363314801dcf65c7266aadc8c377d3810aeb33d", + "tools/physical-workcell/body_network.py": "95fd21597bcdd9e982a698859cd1219ae4522062e332c50fea233629dbfb343c", + "tools/physical-workcell/body_protocol.py": "1d24066768105c28b5cf6f4d46458e4e1b9d86cbde45ed6ce466b30da7ff974f", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "40fe73e01d97ee1e5cfdb813650b11edadbea29534784ab10cc1ef4e7f4d5418", + "tools/physical-workcell/crash_body.py": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/network_bench_operator.html": "75ca82328967ea65cf3c3f7f7d60c109a4201099ae3a24d3834e0ecf19b4d897", + "tools/physical-workcell/network_bench_operator.py": "9079729ea4e8b56ccdf2a6d187b0c5d79a23a33688748fe55d92d10922500b40", + "tools/physical-workcell/network_fault_proxy.py": "44b0b52f7c213c0f5f93b8997ba72be4e750bc9f1a4f7f6d7b2a8c4f1233b595", + "tools/physical-workcell/network_operator_runtime.py": "001511aa25fef7f1078bce444b9a0f7d0a09d26d4e5fba9e9819ee3ed98cb6b9", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/qualify_network_body.py": "2800cf38afd21b86f8b035c46e994b6f96d966db4ccba1bba703e9e1a22f7c2b", + "tools/physical-workcell/qualify_network_workbench.py": "e5c72acda2ea8fae3658faba907448765df2f1e34e05749fbe85faed28ef003b", + "tools/physical-workcell/qualify_workbench_crashes.py": "da971a4190389414dd2114b86d2b7dd40adb2dba2c16e304782eab362244932e", + "tools/physical-workcell/qualify_workbench_restarts.py": "2bc3edc5ea39b69f3372d4274b757f06472409407a22cd3149dfa52fbf314dcb", + "tools/physical-workcell/record_network_workbench.py": "7604865caa309c7002c01a2384da84eb62d8b22dcd63ab256618c536bc5fedc4", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_network_body.py": "d1f7150a5ff23775664b5656fd21379502ba71175487fd7aadb2b51bffcdaf8c", + "tools/physical-workcell/score_network_workbench.py": "67a53586081fe425bd14b0a0fbbf3d7f0fc1e66daccda06765bb88f3d9d4d84c", + "tools/physical-workcell/score_workbench_crashes.py": "10bec02c29afdb9963006884fd9ce310a52be8ba0995698cc11f5d6e4475b998", + "tools/physical-workcell/score_workbench_restarts.py": "181cbf4ee1bbd5483f38edc4a84244fc3efa98dfd21b5c9751d43edfe8f9f5a5", + "tools/physical-workcell/test_workbench_crash_harness.py": "b5a1398f9ccec220731f30c9f082260e378743f6fa6c3d954f008c2e79aeaf6e", + "tools/physical-workcell/workbench_link.py": "79571f62c5a9b5fb843a852bc34a2a509b0dc6dc1a6e091fa9f948ab98fae420", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/ui-ledger.json b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/ui-ledger.json new file mode 100644 index 0000000..60f5b33 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/host-uncertain/ui-ledger.json @@ -0,0 +1,289 @@ +{ + "steps": [ + { + "browser_action": "Observed Failed to fetch after SIGKILL of host already blocked on exact command", + "cached_public": { + "active_job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "app_id": "676909dee6e1429abdcfa2e3698650cd", + "connection": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "label": "Workbench crash while uncertain", + "last_intent": "Push the block rightward so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-2fa2310dcde0409ba65e9430c4e08a9a", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "client_commands": 4, + "fetch_error": "[Errno 61] Connection refused", + "name": "crash-offline", + "offline": true, + "pending_command_ids": [ + "command-2fa2310dcde0409ba65e9430c4e08a9a" + ], + "pins": { + "app_id": "676909dee6e1429abdcfa2e3698650cd", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061" + }, + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-uncertain-v1/crash-offline.png", + "snapshot_provenance": { + "name": "000002-offline.json", + "sha256": "c9ee285b6ca0838a9ba0c2897175d64bae40fa71f1be9046f9bc117e5349f9ba" + }, + "state": null, + "time": 1791835403.7673721, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51389/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51389/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t63 checkbox Workbench crash while uncertain Needs attention, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t64 text Workbench crash while uncertain\n\t\t65 text Disconnected\n\t\t14 button (disabled) Pause, ID: pause\n\t\t66 text Failed to fetch\n\t\t15 container Description: Body connection, ID: network-card\n\t\t\t67 text Body connected\n\t\t\t68 text Body identity verified by read-only TCP health\n\t\t\t18 button (disabled) Disconnect, ID: disconnect\n\t\t\t19 button (disabled) Check receipt, ID: reconcile\n\t\t\t20 container\n\t\t\t\t21 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t22 text Test the simulated connection\n\t\t\t\t23 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t24 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t25 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t26 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t69 text No reply fault armed.\n\t\t28 container Camera observation\n\t\t\t29 text OVERHEAD CAMERA\n\t\t\t70 text Last observation \u00b7 Workbench crash while uncertain\n\t\t\t62 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t32 text Orange part Blue pusher Green task region\n\t\t\t71 text Last captured \u00b7 generation 0 \u00b7 disconnected\n\t\t72 text OUTCOME UNCERTAIN\n\t\t73 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t36 container Job progress\n\t\t\t37 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t38 container Measurement\n\t\t\t39 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t40 text Measurement\n\t\t\t74 text Pending\n\t\t\t42 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Reloaded after hard host crash; exact original uncertain token retained without replay", + "cached_public": null, + "client_commands": 4, + "name": "crash-restored", + "offline": false, + "pending_command_ids": [ + "command-2fa2310dcde0409ba65e9430c4e08a9a" + ], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-uncertain-v1/crash-restored.png", + "state": { + "active_job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "app_id": "676909dee6e1429abdcfa2e3698650cd", + "connection": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "Restarted with a retained command stage; check its exact receipt", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "label": "Workbench crash while uncertain", + "last_intent": "Push the block rightward so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-2fa2310dcde0409ba65e9430c4e08a9a", + "operation": "dispatch", + "reason": "Restarted with a retained command stage; check its exact receipt", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791835415.702548, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51389/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51389/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 checkbox Workbench crash while uncertain Needs attention, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t12 text Workbench crash while uncertain\n\t\t13 text Needs attention\n\t\t14 button (disabled) Pause, ID: pause\n\t\t15 text Restarted with a retained command stage; check its exact receipt\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t17 text Body connected\n\t\t\t18 text Body identity verified by read-only TCP health\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t25 text Last observation \u00b7 Workbench crash while uncertain\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t28 text Captured \u00b7 generation 0\n\t\t29 text OUTCOME UNCERTAIN\n\t\t30 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t36 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Check receipt recovered original known command read-only; four commands and paused runner", + "cached_public": null, + "client_commands": 4, + "name": "crash-recovered", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-uncertain-v1/crash-recovered.png", + "state": { + "active_job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "app_id": "676909dee6e1429abdcfa2e3698650cd", + "connection": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": null, + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "label": "Workbench crash while uncertain", + "last_intent": "Push the block rightward so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-2fa2310dcde0409ba65e9430c4e08a9a", + "operation": "dispatch", + "reason": "Exact historical stage recovered; explicit Resume observes fresh evidence", + "state": "recovered" + }, + "runner_status": "paused" + }, + "time": 1791835427.7403789, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51389/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51389/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t69 checkbox Workbench crash while uncertain Paused, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t70 text Workbench crash while uncertain\n\t\t71 text Paused\n\t\t14 button Resume, ID: pause\n\t\t72 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue.\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t73 text Body connected\n\t\t\t74 text Body identity verified by read-only TCP health\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button (disabled) Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t75 text Last observation \u00b7 Workbench crash while uncertain\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t76 text Captured \u00b7 generation 0\n\t\t77 text RECEIPT RECOVERED\n\t\t78 text The exact command receipt was recovered. The agent is paused until you resume.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t79 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Explicit Resume requested fresh image after historical receipt recovery", + "cached_public": null, + "client_commands": 5, + "name": "crash-resumed", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-uncertain-v1/crash-resumed.png", + "state": { + "active_job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "app_id": "676909dee6e1429abdcfa2e3698650cd", + "connection": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 1, + "image_url": "/api/frame?generation=1&sha256=cb4221e600bbb24aec549f49924fc9224876a1ad8d04159fe0cc3798658d8357" + }, + "jobs": [ + { + "call_count": 2, + "disturbance_test": false, + "error": null, + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "label": "Workbench crash while uncertain", + "last_intent": "Push the block rightward so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "A" + } + ], + "model_request_pending": true, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791835437.09873, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51389/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51389/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t69 checkbox Workbench crash while uncertain Paused, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t70 text Workbench crash while uncertain\n\t\t71 text Running\n\t\t14 button Pause, ID: pause\n\t\t16 container Description: Body connection, ID: network-card\n\t\t\t72 text Body connected\n\t\t\t73 text Current body boot verified for explicit Resume\n\t\t\t19 button Disconnect, ID: disconnect\n\t\t\t20 button (disabled) Check receipt, ID: reconcile\n\t\t\t21 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t22 text Test the simulated connection\n\t\t23 container Camera observation\n\t\t\t24 text OVERHEAD CAMERA\n\t\t\t74 text Last observation \u00b7 Workbench crash while uncertain\n\t\t\t26 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t27 text Orange part Blue pusher Green task region\n\t\t\t75 text Captured \u00b7 generation 0\n\t\t76 text AGENT\n\t\t77 text Push the block rightward so its center reaches the measurement region.\n\t\t31 container Job progress\n\t\t\t32 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t33 container Measurement\n\t\t\t34 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t35 text Measurement\n\t\t\t78 text Pending\n\t\t\t37 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Completed actual image-guided job and contact reset after uncertain host SIGKILL and exact recovery", + "cached_public": null, + "client_commands": 40, + "name": "crash-completed", + "offline": false, + "pending_command_ids": [], + "screenshot": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-uncertain-v1/crash-completed.png", + "state": { + "active_job_id": null, + "app_id": "676909dee6e1429abdcfa2e3698650cd", + "connection": { + "adapter_id": "41ef5e837de74370ab407737c6bd61a7", + "body_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=612118c97622874a02927de51dfa015cd96bfdc46dbc7c11146a229288798f12" + }, + "jobs": [ + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "label": "Workbench crash while uncertain", + "last_intent": "Finish the reset now that the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "2fa4eeaf21ac4effa12582df7a7c8061", + "block_home_verified": true, + "generation": 10, + "image_sha256": "612118c97622874a02927de51dfa015cd96bfdc46dbc7c11146a229288798f12", + "job_id": "e0ac9b38-db3e-4ba3-911d-f68e78762aec", + "kind": "finish_reset", + "native_time_s": 21.943999999998717, + "operation_id": "agent-f367be891109464b8c33dd8c992de912", + "pusher_home_verified": true, + "reset_contact_samples": 2986, + "reset_native_steps": 8205, + "state_sha256": "bcf29e5d41bc6334624f8f04b0bb0bdf4f3dda8e08e52bd9e10b31b37d71ac62" + }, + "status": "completed", + "target": "A" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791835654.313816, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51389/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51389/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 checkbox Workbench crash while uncertain Completed, Value: 1\n\t9 container\n\t\t10 heading Bench, Value: 1\n\t\t\t11 text Bench\n\t\t12 text Workbench crash while uncertain\n\t\t13 text Running\n\t\t14 button Pause, ID: pause\n\t\t15 container Description: Body connection, ID: network-card\n\t\t\t16 text Body connected\n\t\t\t17 text Body identity verified by read-only TCP health\n\t\t\t18 button Disconnect, ID: disconnect\n\t\t\t19 button (disabled) Check receipt, ID: reconcile\n\t\t\t20 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t21 text Test the simulated connection\n\t\t22 container Camera observation\n\t\t\t23 text OVERHEAD CAMERA\n\t\t\t24 text Last camera observation\n\t\t\t25 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t26 text Orange part Blue pusher Green task region\n\t\t\t27 text Captured \u00b7 generation 10\n\t\t28 text COMPLETED\n\t\t29 text Measurement recorded and reset verified. The bench is ready for another job.\n\t\t30 container Job progress\n\t\t\t31 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t32 container Measurement\n\t\t\t33 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t34 text Measurement\n\t\t\t35 text Historical result\n\t\t\t36 container measurement-result\n\t\t\t\t37 text 1,726\n\t\t\t\t38 text Orange pixels\n\t\t\t\t39 container\n\t\t\t\t\t40 text 4,794.4\n\t\t\t\t\t41 text mm\u00b2\n\t\t\t\t42 text Apparent footprint\n\t\t\t\t43 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment.\n\t\t\t44 button Repeat job, ID: repeat" + } + ] +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/known-receipt-conformance.json b/jevdual/results/workbench-hard-crashes-20261012/known-receipt-conformance.json new file mode 100644 index 0000000..1c58519 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/known-receipt-conformance.json @@ -0,0 +1,173 @@ +{ + "events": [ + { + "diagnostic_gate": "after_dispatch", + "identity": { + "adapter_id": "7f8b343a382c4e8da69d881553bfab9d", + "body_id": "43c0f4c1e7b64d909b7972a5dc9f6a80", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "kind": "ready", + "launcher": "crash_body.py", + "pid": 5467 + }, + { + "kind": "stopped_barrier", + "marker": { + "barrier": "after_dispatch", + "command_id": "command-24e75d697fd54bc7952de7f08bd42b5b", + "pid": 5467 + } + }, + { + "command_id": "command-24e75d697fd54bc7952de7f08bd42b5b", + "kind": "SIGKILL", + "pid": 5467, + "returncode": -9 + }, + { + "diagnostic_gate": null, + "identity": { + "adapter_id": "7f8b343a382c4e8da69d881553bfab9d", + "body_id": "43c0f4c1e7b64d909b7972a5dc9f6a80", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "kind": "ready", + "launcher": "body_network.py", + "pid": 5483 + }, + { + "exact_single_send": true, + "kind": "exact_lower_receipt_recovered", + "native_unchanged": true, + "receipt": { + "adapter_id": "7f8b343a382c4e8da69d881553bfab9d", + "body_id": "43c0f4c1e7b64d909b7972a5dc9f6a80", + "command_id": "command-24e75d697fd54bc7952de7f08bd42b5b", + "protocol": "footwork.body/1", + "reason": "read-only lower receipt recovered after restart", + "receipt_scope": "retained_command_result", + "recorded_epoch": 1, + "request_sha256": "d94a19acd27c066b8829a7b6c52cbd3552d744986d61ad79677be50934ad399d", + "result": { + "next_observation": { + "challenge_id": "challenge-e4c037eb16b7495eb7e50ea0f9fcb7b8", + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-crash-body-f9b6716db22e/body/bench/images/000001-665f7003914695a3.png", + "observation": { + "bench_id": "43c0f4c1e7b64d909b7972a5dc9f6a80", + "calibration": { + "bbox_convention": "exclusive_max_pixel_edges", + "height": 360, + "image_center_world_xy": [ + 0.0, + 0.0 + ], + "image_x_direction": "world_positive_x", + "image_y_direction": "world_negative_y", + "meters_per_pixel": 0.0016666666666666666, + "projection": "orthographic", + "valid": true, + "validity_reason": "bound orthographic calibration", + "vertical_span_m": 0.6, + "width": 640, + "world_frame": "sim_world" + }, + "generation": 1, + "image_path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-crash-body-f9b6716db22e/body/bench/images/000001-665f7003914695a3.png", + "image_sha256": "c3054638556af1832bce1a717ab197a1c1e0cb8621d6d5fc734d2d5867f845ad", + "job_id": "job", + "native_time_s": 2.7339999999999205, + "phase": "prepare", + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state_sha256": "3f9c81b61415826a5127bf21026fffb5d4155b58bf7a9dff2c34563105d3327b", + "version": 1 + }, + "observation_sha256": "eb813a3e2291ced4d734b0b413f4653fb159798df3905aaa868cdd13c28e5aa4", + "sequence": 4, + "session_id": "79161ccbff704b3fa47b12439ef03173", + "task": { + "bounds": { + "x_max": 0.18, + "x_min": -0.32, + "y_max": 0.15, + "y_min": -0.15 + }, + "home_zone": { + "x_max": -0.08, + "x_min": -0.16, + "y_max": 0.03, + "y_min": -0.03 + }, + "pusher_dimensions_m": { + "x": 0.04, + "y": 0.14 + }, + "zone": { + "x_max": 0.1, + "x_min": 0.04, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "observation_scope": "snapshot_at_dispatch_result", + "outcome": "executed", + "proposal_id": "proposal-934b16e5e404493abf64647771e5dd68", + "receipt": { + "bench_id": "43c0f4c1e7b64d909b7972a5dc9f6a80", + "command_speed_limit_m_s": 0.15, + "generation": 1, + "job_id": "job", + "kind": "move", + "max_axis_actuator_force_n": 0.38498849079591624, + "native_steps": 867, + "native_time_s": 2.7339999999999205, + "operation_id": "agent-41aee184254a4b8bb3acd838df4721eb", + "pusher_block_contact_samples": 0, + "state_sha256": "3f9c81b61415826a5127bf21026fffb5d4155b58bf7a9dff2c34563105d3327b", + "target_xy": [ + -0.19, + 0.0 + ] + } + }, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state": "completed" + } + } + ], + "passed": true, + "processes": [ + { + "pid": 5467, + "returncode": -9 + }, + { + "pid": 5483, + "returncode": -9 + } + ], + "processes_reaped": true, + "scenario_kind": "actual_process_SIGKILL_TCP_native", + "sources_unchanged": true, + "test_id": "test_crash_body.CrashBodyTests.test_indexed_lower_receipt_SIGKILL_recovers_exact_command_without_motion_replay" +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/native-gap-conformance.json b/jevdual/results/workbench-hard-crashes-20261012/native-gap-conformance.json new file mode 100644 index 0000000..233e898 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/native-gap-conformance.json @@ -0,0 +1,196 @@ +{ + "events": [ + { + "diagnostic_gate": "after_native", + "identity": { + "adapter_id": "e7e64d7025ab4cc380116a156fc8d2eb", + "body_id": "81f6633447fb481ea7f1d71db89c6b6d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "kind": "ready", + "launcher": "crash_body.py", + "pid": 5434 + }, + { + "kind": "stopped_barrier", + "marker": { + "barrier": "after_native", + "bench_id": "81f6633447fb481ea7f1d71db89c6b6d", + "command_id": "command-e5285f01f46f40bb835735c306172cac", + "diagnostic_source_sha256": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "manifest_generation": 0, + "native_generation": 1, + "native_state_sha256": "3f9c81b61415826a5127bf21026fffb5d4155b58bf7a9dff2c34563105d3327b", + "native_steps": 867, + "native_time_s": 2.7339999999999205, + "operation_id": "agent-9ee076336dc54db98e7b57b734efa415", + "pid": 5434, + "proposal_id": "proposal-0a2c2b53ce2944b98250e144bf6e1397", + "pusher_block_contact_samples": 0, + "request_sha256": "833667a9208a72786e8aecefaadbe53a384555623f3b28fbe4744be03b76ac23", + "version": 1 + } + }, + { + "command_id": "command-e5285f01f46f40bb835735c306172cac", + "kind": "SIGKILL", + "pid": 5434, + "returncode": -9 + }, + { + "diagnostic_gate": null, + "identity": { + "adapter_id": "e7e64d7025ab4cc380116a156fc8d2eb", + "body_id": "81f6633447fb481ea7f1d71db89c6b6d", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "kind": "ready", + "launcher": "body_network.py", + "pid": 5451 + }, + { + "advanced": { + "block_xyz": [ + -0.11999998846068155, + -1.428075558873589e-10, + 0.03499573045536993 + ], + "pusher_xyz": [ + -0.19000003075014832, + 0.0, + 0.06 + ], + "time_s": 2.7339999999999205 + }, + "before": { + "block_xyz": [ + -0.11999998822184511, + 1.630059551357049e-11, + 0.03499573045537004 + ], + "pusher_xyz": [ + -0.3, + 0.0, + 0.06 + ], + "time_s": 1.0000000000000007 + }, + "exact_single_send": true, + "kind": "blocked_after_native", + "marker": { + "barrier": "after_native", + "bench_id": "81f6633447fb481ea7f1d71db89c6b6d", + "command_id": "command-e5285f01f46f40bb835735c306172cac", + "diagnostic_source_sha256": "9163ad618c7a92efc1306bdeca2488ce5b10835edcb77eec062fa8490e0557ea", + "manifest_generation": 0, + "native_generation": 1, + "native_state_sha256": "3f9c81b61415826a5127bf21026fffb5d4155b58bf7a9dff2c34563105d3327b", + "native_steps": 867, + "native_time_s": 2.7339999999999205, + "operation_id": "agent-9ee076336dc54db98e7b57b734efa415", + "pid": 5434, + "proposal_id": "proposal-0a2c2b53ce2944b98250e144bf6e1397", + "pusher_block_contact_samples": 0, + "request_sha256": "833667a9208a72786e8aecefaadbe53a384555623f3b28fbe4744be03b76ac23", + "version": 1 + }, + "public_state": { + "identity": { + "adapter_id": "e7e64d7025ab4cc380116a156fc8d2eb", + "body_id": "81f6633447fb481ea7f1d71db89c6b6d", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-53d30a2e789d4169887ab245b1d03380", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "24b3a58899484b829247a929b71b955f" + }, + "pending_command_ids": [ + "command-e5285f01f46f40bb835735c306172cac" + ], + "server_state": { + "bench": { + "active_job": { + "job_id": "job", + "moves": 0, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.1, + "x_min": 0.04, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "81f6633447fb481ea7f1d71db89c6b6d", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "unknown", + "unresolved_effect": { + "generation": 0, + "kind": "move", + "operation_id": "agent-9ee076336dc54db98e7b57b734efa415" + }, + "version": 1 + }, + "identity": { + "adapter_id": "e7e64d7025ab4cc380116a156fc8d2eb", + "body_id": "81f6633447fb481ea7f1d71db89c6b6d", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": null, + "state": "unknown", + "unresolved_commands": [ + "command-e5285f01f46f40bb835735c306172cac" + ] + } + }, + "state_unchanged": true + } + ], + "passed": true, + "processes": [ + { + "pid": 5434, + "returncode": -9 + }, + { + "pid": 5451, + "returncode": -9 + } + ], + "processes_reaped": true, + "scenario_kind": "actual_process_SIGKILL_TCP_native", + "sources_unchanged": true, + "test_id": "test_crash_body.CrashBodyTests.test_after_native_SIGKILL_retains_real_actuation_but_stays_unknown_without_replay" +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/preserved-product.json b/jevdual/results/workbench-hard-crashes-20261012/preserved-product.json new file mode 100644 index 0000000..a0538ed --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/preserved-product.json @@ -0,0 +1,12 @@ +{ + "source_commit": "1b773b3b6ee70da85d5dc19619107176ec1c4514", + "previous_product": "http://127.0.0.1:51377/", + "retained_roots": [ + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim12-browser-health-v1/body", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim12-browser-health-v1/operator", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim12-handoff/persistent-relay" + ], + "unchanged_completed_jobs": 2, + "crash_target": false, + "plan": "Isolated fresh simulated roots/ports for all SIGKILL cases; existing paused product retained" +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/publication-gap-limitation.json b/jevdual/results/workbench-hard-crashes-20261012/publication-gap-limitation.json new file mode 100644 index 0000000..336b3a1 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/publication-gap-limitation.json @@ -0,0 +1,123 @@ +{ + "case": "test_workbench_crash_runtime.WorkbenchCrashRuntimeTests.test_publication_tail_sigkill_is_retained_as_explicit_recovery_limit", + "explicit_limit": { + "boundary": "event-fsync-before-manifest-publication", + "effect_commands": 0, + "error": "[Errno 17] File exists: '/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim13-host-crash-41308cad5f/host/events/000001.json'", + "sequence": 1 + }, + "host_state": { + "active_job_id": null, + "activity": [], + "app_id": "f35878562e5b4fca90b7650d88457722", + "applied_commands": [], + "body_identity": { + "adapter_id": "2e31732706cd415bb9b7bf1556693519", + "body_id": "1dac87ed0d63419291ced2d463bfa952", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_origin": "http://127.0.0.1:64095", + "connection": { + "adapter_id": "2e31732706cd415bb9b7bf1556693519", + "body_id": "1dac87ed0d63419291ced2d463bfa952", + "epoch": 1, + "fault": null, + "reason": "Current body boot verified for explicit Resume", + "state": "connected" + }, + "frame": null, + "jobs": [], + "link_origin": "http://127.0.0.1:64096", + "link_port": 64096, + "opened": true, + "reason": "Stopped; independent network body retained", + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "stopped", + "sequence": 1, + "sources": { + "body_client.py": "40388c238192d923888c9852b363314801dcf65c7266aadc8c377d3810aeb33d", + "codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "codex_policy.py": "40fe73e01d97ee1e5cfdb813650b11edadbea29534784ab10cc1ef4e7f4d5418", + "network_bench_operator.html": "75ca82328967ea65cf3c3f7f7d60c109a4201099ae3a24d3834e0ecf19b4d897", + "network_bench_operator.py": "9079729ea4e8b56ccdf2a6d187b0c5d79a23a33688748fe55d92d10922500b40", + "network_operator_runtime.py": "001511aa25fef7f1078bce444b9a0f7d0a09d26d4e5fba9e9819ee3ed98cb6b9", + "order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "workbench_link.py": "79571f62c5a9b5fb843a852bc34a2a509b0dc6dc1a6e091fa9f948ab98fae420" + }, + "stage": null, + "usage": { + "calls": 0, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0 + }, + "version": 1 + }, + "passed": true, + "policy": "explicitly_fake_diagnostic", + "processes": [ + { + "exit_code": -9, + "pid": 3836 + } + ], + "relay": [ + { + "method": "GET", + "path": "/identity", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "4ecbe1f02e97dc0d174795ba2c1d5e7774866343acc31c6a50ec70cba7a2d5c7", + "status": 200 + }, + { + "method": "GET", + "path": "/identity", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "4ecbe1f02e97dc0d174795ba2c1d5e7774866343acc31c6a50ec70cba7a2d5c7", + "status": 200 + }, + { + "method": "GET", + "path": "/state", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "a6a8c676f1a95a061ee255139aa66a17f209a9c6613ec74a9a2d62ea79a8d0b4", + "status": 200 + }, + { + "method": "GET", + "path": "/identity", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "4ecbe1f02e97dc0d174795ba2c1d5e7774866343acc31c6a50ec70cba7a2d5c7", + "status": 200 + }, + { + "method": "GET", + "path": "/state", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "a6a8c676f1a95a061ee255139aa66a17f209a9c6613ec74a9a2d62ea79a8d0b4", + "status": 200 + }, + { + "method": "GET", + "path": "/state", + "request": null, + "request_sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "response_sha256": "a6a8c676f1a95a061ee255139aa66a17f209a9c6613ec74a9a2d62ea79a8d0b4", + "status": 200 + } + ] +} diff --git a/jevdual/results/workbench-hard-crashes-20261012/report.md b/jevdual/results/workbench-hard-crashes-20261012/report.md new file mode 100644 index 0000000..1e4c7c6 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/report.md @@ -0,0 +1,28 @@ +# Hard-crash recovery at command boundaries + +Four actual browser cases passed independent post-stop scoring on frozen source `4e2dd74d27ec07f0b5a42039e0f30016402cf050`. Three recovered their original receipts and completed measurement/contact reset. The fourth saved native motion without its completion receipt and correctly stayed blocked. Across the qualified runs, 41 actual image-only Codex decisions and all 127 commands were independently bound to exact TCP evidence, with one wire send and fresh admission proof per command. [Summary](summary.json). + +| Hard crash | Browser behavior after restart | Independent result | +| --- | --- | --- | +| [Workbench in flight](host-inflight/hard-crash-score.json), after body completion but before reply delivery | Same original command returned uncertain; Check receipt recovered it while paused; explicit Resume obtained a fresh image | 13 decisions, 40 commands; job completed measurement and reset; body boot stayed one | +| [Body while already uncertain](body-uncertain/hard-crash-score.json) about a completed command | Original boot-one receipt recovered under boot two; runner stayed paused until Resume | 14 decisions, 43 commands; measurement/reset completed; stable body identity and no resend | +| [Workbench while already uncertain](host-uncertain/hard-crash-score.json) | Same pending stage survived SIGKILL; exact receipt recovery preceded Resume | 13 decisions, 40 commands; measurement/reset completed; no command or decision replay during restart | +| [Body native receipt gap](body-native-gap/hard-crash-score.json), after motion/state save but before the lower receipt | Boot two still reported an unknown outcome; two receipt lookups could not release it; continuation and New job were disabled | One decision, four commands; saved next state proves motion, while the original manifest stays busy and no lower completion is indexed | + +All four crashed processes exited by SIGKILL, with empty descendant sets at the selected gates and observed process reaping. Every other owned trial process exited zero before scoring. The three recoverable cases kept exactly four commands through crash, restart and receipt application. Fresh work began only after explicit Resume. The native-gap case never admitted a successor. An unfamiliar or apparently correct camera position supplied no completion authority. + +The new [controller](../../../tools/physical-workcell/qualify_workbench_crashes.py) retains exact expectations, PID/group ownership, before/offline/restored snapshots, immutable request/reply bytes and cleanup. It signals only its registered fresh simulation processes. Its external relay holds one completed dispatch reply for the in-flight host test. The [diagnostic body launcher](../../../tools/physical-workcell/crash_body.py) exposes the deeper gap immediately before the Bench terminal receipt commit; ordinary body code performs the restart. The [scorer](../../../tools/physical-workcell/score_workbench_crashes.py) checks the original command/request, boot and lease boundaries, exact image/model/proposal/lower-intent/result bindings, fresh ready-state proof, native placement, one SQLite measurement and contact reset. It uses a separate safely-blocked result for an unresolved native effect. + +Existing product body, client, core and runtime files were unchanged. Twenty-two distinct new checks passed: three body-launcher cases, two host process cases, nine controller/recorder checks and eight scorer controls. The stricter model schema also verified all 27 retained SIM12 decisions read-only; those were not new model requests. Ruff, syntax, notebook, redaction and whitespace checks passed. [Conformance](conformance.json). + +The [attempt ledger](attempt-ledger.json) includes failures and evidence limitations. Native-gap-v1 behaved correctly in the browser, but its scorer assumed a measurement database existed before any measurement. The correction accepts database absence only in the native-gap case, creates nothing, and still refuses missing completed-case databases or invalid existing databases. Native-gap-v2 then failed recording because a browser helper retained the first capture directory; the recorder refused its missing file. Three v1 offline capture files were overwritten by that v2 capture. Original ledgers, other captures and private run traces remain, and this loss is recorded explicitly. Native-gap-v3 repeated the actual move with explicit capture paths and passed. Total actual calls including those two unqualified attempts were 43; qualified metrics above include 41. + +A separate [event-publication test](publication-gap-limitation.json) exposed a recovery limit: SIGKILL after an event file is written but before its manifest sequence is saved can cause FileExistsError on Resume. A retained stage before client command identity is saved also lacks a release route. These instruction-level windows are not covered by the four successful acknowledged-boundary trials. Killing a host during active planning can leave its independent planner session; the selected command gates had no active planner or descendant. Physical actuator stops, power-loss durability and authenticated cross-machine control remain unqualified. The native receipt gap has no safe automatic release path and its unresolved root remains preserved. + +Scoring preceded handoff. Three scored roots remain stopped; the host-uncertain root restarted afterward to leave the [live workbench](http://127.0.0.1:51389/) connected and paused, with its completed result and no active job, model request or uncertain command. [Handoff metadata](handoff.json) records the new owned body/relay/workbench PIDs, retained roots and body boot two. The handoff relay has injection disabled and no trial deadline. Administrative boot events may be appended to that live journal; the saved scores and snapshots describe the stopped trials. The previous paused health-test product and its roots were preserved separately, outside the crash targets. + +Private model prompts, traces, raw TCP images and all attempt directories remain under ignored `tools/physical-workcell/output/sim13-*` storage. Reproduce on new roots with the [runbook](../../../tools/physical-workcell/NETWORK_WORKBENCH.md) and [preregistered SIM13](../../docs/experiments/SIM13-workbench-hard-crashes.md), scoring after stop and before handoff. + +![Historical receipt recovered after a hard body reboot; agent remains paused](body-uncertain/crash-recovered.png) + +![Missing native receipt remains uncertain and blocked](body-native-gap/crash-still-blocked.png) diff --git a/jevdual/results/workbench-hard-crashes-20261012/summary.json b/jevdual/results/workbench-hard-crashes-20261012/summary.json new file mode 100644 index 0000000..4e0cb09 --- /dev/null +++ b/jevdual/results/workbench-hard-crashes-20261012/summary.json @@ -0,0 +1,62 @@ +{ + "passed": true, + "cases": [ + { + "case": "body-native-gap", + "passed": true, + "completed_cycles": 0, + "safely_blocked": true, + "model_calls": 1, + "commands": 4, + "boot_before": 1, + "boot_after": 2, + "cutoff": 19, + "root": "tools/physical-workcell/output/sim13-body-native-gap-v3", + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050" + }, + { + "case": "host-inflight", + "passed": true, + "completed_cycles": 1, + "safely_blocked": false, + "model_calls": 13, + "commands": 40, + "boot_before": 1, + "boot_after": 1, + "cutoff": 137, + "root": "tools/physical-workcell/output/sim13-host-inflight-v1", + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050" + }, + { + "case": "body-uncertain", + "passed": true, + "completed_cycles": 1, + "safely_blocked": false, + "model_calls": 14, + "commands": 43, + "boot_before": 1, + "boot_after": 2, + "cutoff": 151, + "root": "tools/physical-workcell/output/sim13-body-uncertain-v1", + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050" + }, + { + "case": "host-uncertain", + "passed": true, + "completed_cycles": 1, + "safely_blocked": false, + "model_calls": 13, + "commands": 40, + "boot_before": 1, + "boot_after": 1, + "cutoff": 139, + "root": "tools/physical-workcell/output/sim13-host-uncertain-v1", + "source_commit": "4e2dd74d27ec07f0b5a42039e0f30016402cf050" + } + ], + "qualified_model_calls": 41, + "qualified_commands": 127, + "completed_jobs": 3, + "safe_blocks": 1, + "no_replay": true +} diff --git a/jevdual/results/workbench-health-20261012/attempt-ledger.json b/jevdual/results/workbench-health-20261012/attempt-ledger.json new file mode 100644 index 0000000..2e98e64 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/attempt-ledger.json @@ -0,0 +1,11 @@ +{ + "source_commit": "37ebfdb3054b977149d7b60b46254ddd852b749a", + "before_fix": [ + "tools/physical-workcell/output/sim12-health-before-fix.log", + "tools/physical-workcell/output/sim12-link-before-fix.log" + ], + "intermediate_failure": "tools/physical-workcell/output/sim12-health-targeted-v2.log", + "setup_only_unretained": "Wrong relative interpreter path exited127 before tests; reported in notebook SIM12-U2", + "actual_trial": "tools/physical-workcell/output/sim12-browser-health-v1", + "historical_warm_probes": "32 sequential reads succeeded; physical scheduling cause of original SIM11 timeout remains unresolved" +} diff --git a/jevdual/results/workbench-health-20261012/conformance.json b/jevdual/results/workbench-health-20261012/conformance.json new file mode 100644 index 0000000..05eba7c --- /dev/null +++ b/jevdual/results/workbench-health-20261012/conformance.json @@ -0,0 +1,15 @@ +{ + "runtime": { + "passed": 26, + "log": "tools/physical-workcell/output/sim12-runtime-final-v1.log" + }, + "core_network_HTTP_including_link": { + "passed": 84, + "log": "tools/physical-workcell/output/sim12-core-network-regression.log" + }, + "harness_scorer": { + "passed": 7, + "log": "tools/physical-workcell/output/sim12-harness-tests-v1/unittest.log" + }, + "total_distinct_suite_checks": 117 +} diff --git a/jevdual/results/workbench-health-20261012/diagnosis.json b/jevdual/results/workbench-health-20261012/diagnosis.json new file mode 100644 index 0000000..ed68637 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/diagnosis.json @@ -0,0 +1,116 @@ +{ + "isolated_transport_scan": { + "max_s": 0.029363750014454126, + "median_s": 0.020804957952350378, + "samples": [ + { + "bytes_read": 576223, + "counts": { + "canonical_request_digest": 664, + "intent.json": 83, + "result.json": 166 + }, + "elapsed_s": 0.02449941704981029, + "iteration": 0, + "records": 83, + "unresolved_count": 0 + }, + { + "bytes_read": 576223, + "counts": { + "canonical_request_digest": 664, + "intent.json": 83, + "result.json": 166 + }, + "elapsed_s": 0.020804957952350378, + "iteration": 1, + "records": 83, + "unresolved_count": 0 + }, + { + "bytes_read": 576223, + "counts": { + "canonical_request_digest": 664, + "intent.json": 83, + "result.json": 166 + }, + "elapsed_s": 0.029363750014454126, + "iteration": 2, + "records": 83, + "unresolved_count": 0 + }, + { + "bytes_read": 576223, + "counts": { + "canonical_request_digest": 664, + "intent.json": 83, + "result.json": 166 + }, + "elapsed_s": 0.019083166029304266, + "iteration": 3, + "records": 83, + "unresolved_count": 0 + }, + { + "bytes_read": 576223, + "counts": { + "canonical_request_digest": 664, + "intent.json": 83, + "result.json": 166 + }, + "elapsed_s": 0.018134792102500796, + "iteration": 4, + "records": 83, + "unresolved_count": 0 + } + ], + "scope": "copied immutable command journal only; no service/native constructor or effects" + }, + "no_model_calls": true, + "no_native_actions": true, + "routes": { + "direct_identity": { + "max_connect_s": 0.003117375075817108, + "max_headers_s": 0.07855645893141627, + "max_s": 0.0818679581861943, + "median_s": 0.025921083986759186, + "min_s": 0.022658249828964472, + "samples": 8, + "successes": 8 + }, + "direct_state": { + "max_connect_s": 0.00038674985989928246, + "max_headers_s": 0.13627387513406575, + "max_s": 0.13670033309608698, + "median_s": 0.09210818749852479, + "min_s": 0.08545674988999963, + "samples": 8, + "successes": 8 + }, + "linked_identity": { + "max_connect_s": 0.000262250192463398, + "max_headers_s": 0.03264912497252226, + "max_s": 0.03301991708576679, + "median_s": 0.02526693802792579, + "min_s": 0.024027250008657575, + "samples": 8, + "successes": 8 + }, + "linked_state": { + "max_connect_s": 0.00039504189044237137, + "max_headers_s": 0.12115875002928078, + "max_s": 0.12164500006474555, + "median_s": 0.09428881213534623, + "min_s": 0.0896729591768235, + "samples": 8, + "successes": 8 + } + }, + "samples": 32, + "sequential_requests": true, + "source_sha256": { + "body_client.py": "40388c238192d923888c9852b363314801dcf65c7266aadc8c377d3810aeb33d", + "body_network.py": "95fd21597bcdd9e982a698859cd1219ae4522062e332c50fea233629dbfb343c", + "workbench_link.py": "578d94684463bcb3d7f2d45d9ba68abcd27d8a27f7f39538323a269e251c330e" + } +} diff --git a/jevdual/results/workbench-health-20261012/final-public.json b/jevdual/results/workbench-health-20261012/final-public.json new file mode 100644 index 0000000..a15ed79 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/final-public.json @@ -0,0 +1,320 @@ +{ + "active_job_id": null, + "activity": [ + { + "intent": "Health inspection 02", + "kind": "rpc_applied", + "outcome": "propose: command-e6cd1a382110409ab381d5fd4c4cc3b0", + "seq": 261, + "time": 1791832252.5547252 + }, + { + "evidence": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "c51f62b6b7e6b77045faa7e62b207b6083aae7cea93bc688b1d5d13ebc2183a9" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 262, + "time": 1791832252.67232 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 263, + "time": 1791832252.76916 + }, + { + "intent": "Health inspection 02", + "kind": "rpc_applied", + "outcome": "dispatch: command-34752e8b30564841a21d087d561a5930", + "seq": 264, + "time": 1791832253.140799 + }, + { + "evidence": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "c91ba6318b8583251c817e27b82994527fd14970642478cf703ba8623ab401ee" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 265, + "time": 1791832253.259854 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 266, + "time": 1791832253.339296 + }, + { + "intent": "Health inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-c966c8b893db42eb84c5b0616acca391", + "seq": 267, + "time": 1791832253.647178 + }, + { + "intent": "Health inspection 02", + "kind": "inference", + "outcome": "12", + "seq": 268, + "time": 1791832253.650083 + }, + { + "evidence": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "507e8fc580d3a1de3645c79017d29cbdb19fbe08c7bb29b53151c86cb670beb8" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 269, + "time": 1791832261.705438 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 270, + "time": 1791832261.803793 + }, + { + "intent": "Health inspection 02", + "kind": "rpc_applied", + "outcome": "propose: command-c8ad5019377e44159aef0ee22364b18f", + "seq": 271, + "time": 1791832261.969871 + }, + { + "evidence": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "6f79059dab5d68858756144c67eaf9be40238e57b91180f0f3da17633c09fdad" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 272, + "time": 1791832262.110875 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 273, + "time": 1791832262.20136 + }, + { + "intent": "Health inspection 02", + "kind": "rpc_applied", + "outcome": "dispatch: command-10a03ed616ea4d96a26e96ae0973c32b", + "seq": 274, + "time": 1791832262.66578 + }, + { + "evidence": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "b7771a9f856f21997220b7646337a78d909ab9201902d30832ec913dc2ce5243" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "observe", + "seq": 275, + "time": 1791832262.80537 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 276, + "time": 1791832262.8756638 + }, + { + "intent": "Health inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-6b6e57d717d249c7811ef4708c6b8667", + "seq": 277, + "time": 1791832263.206536 + }, + { + "intent": "Health inspection 02", + "kind": "inference", + "outcome": "13", + "seq": 278, + "time": 1791832263.209843 + }, + { + "evidence": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "ad60b3ef58d11abfe414f2b391c8adbf9ce89722fe1594db2aeb69a6db2098ef" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "propose", + "seq": 279, + "time": 1791832269.831627 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 280, + "time": 1791832269.9314198 + }, + { + "intent": "Health inspection 02", + "kind": "rpc_applied", + "outcome": "propose: command-594d54e2793d428bbb42f80713e94ed3", + "seq": 281, + "time": 1791832270.19759 + }, + { + "evidence": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787", + "state_sha256": "1cd6c2dd647f4e3bfccb6b46456551baf8e02d9b03e8eeb6ad8d00f54372e53b" + }, + "intent": "", + "kind": "admission_proof", + "outcome": "dispatch", + "seq": 282, + "time": 1791832270.339628 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 283, + "time": 1791832270.4208908 + }, + { + "intent": "Health inspection 02", + "kind": "rpc_applied", + "outcome": "dispatch: command-bd5377590da74532a8333ac5ca2a72b3", + "seq": 284, + "time": 1791832270.9544058 + } + ], + "app_id": "433d80f43f4148ef87d341b6d117396d", + "connection": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "fault": null, + "reason": "Health read delayed; fresh body proof is required before any command", + "state": "checking" + }, + "current_frame": { + "available": true, + "generation": 21, + "image_url": "/api/frame?generation=21&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "last_intent": "Finish reset with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "block_home_verified": true, + "generation": 11, + "image_sha256": "e47008c08f81d1ed137d87f021a4273e354a0c3d79e0c9b1da21e6dbeaf28429", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "kind": "finish_reset", + "native_time_s": 22.28199999999853, + "operation_id": "agent-53ec814ec65e409f894ccaf9c1dd2c2a", + "pusher_home_verified": true, + "reset_contact_samples": 3166, + "reset_native_steps": 7940, + "state_sha256": "0de60aca2a69c72c36eb8396ce0c3e420c60000ca6190ec36315207e14c804a4" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "last_intent": "Verify the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "block_home_verified": true, + "generation": 21, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "kind": "finish_reset", + "native_time_s": 43.03000000000662, + "operation_id": "agent-6ec3f611bd9042a5b9a1acc3867b74e7", + "pusher_home_verified": true, + "reset_contact_samples": 3304, + "reset_native_steps": 8106, + "state_sha256": "d3bdf81922c73fc0177d3453905a0376c365e73434d7708da14061adba34ddf7" + }, + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": false, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 27, + "input_tokens": 352734, + "output_tokens": 4129, + "reported_calls": 27, + "token_usage_complete": true + } +} diff --git a/jevdual/results/workbench-health-20261012/handoff.json b/jevdual/results/workbench-health-20261012/handoff.json new file mode 100644 index 0000000..f697e71 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/handoff.json @@ -0,0 +1,116 @@ +{ + "processes": [ + { + "role": "body", + "pid": 84367 + }, + { + "role": "health-relay", + "pid": 84666, + "prior_diagnostic_pid": 84373, + "prior_diagnostic_exited": true + }, + { + "role": "workbench", + "pid": 84375 + } + ], + "body_identity": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "browser_origin": "http://127.0.0.1:51377", + "relay_origin": "http://127.0.0.1:49270", + "relay_root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim12-handoff/persistent-relay", + "body_root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim12-browser-health-v1/body", + "operator_root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim12-browser-health-v1/operator", + "delay_injection": false, + "public": { + "app_id": "433d80f43f4148ef87d341b6d117396d", + "runner_status": "paused", + "connection": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 2, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "model_request_pending": false, + "active_job_id": null, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "last_intent": "Finish reset with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "block_home_verified": true, + "generation": 11, + "image_sha256": "e47008c08f81d1ed137d87f021a4273e354a0c3d79e0c9b1da21e6dbeaf28429", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "kind": "finish_reset", + "native_time_s": 22.28199999999853, + "operation_id": "agent-53ec814ec65e409f894ccaf9c1dd2c2a", + "pusher_home_verified": true, + "reset_contact_samples": 3166, + "reset_native_steps": 7940, + "state_sha256": "0de60aca2a69c72c36eb8396ce0c3e420c60000ca6190ec36315207e14c804a4" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "last_intent": "Verify the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "block_home_verified": true, + "generation": 21, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "kind": "finish_reset", + "native_time_s": 43.03000000000662, + "operation_id": "agent-6ec3f611bd9042a5b9a1acc3867b74e7", + "pusher_home_verified": true, + "reset_contact_samples": 3304, + "reset_native_steps": 8106, + "state_sha256": "d3bdf81922c73fc0177d3453905a0376c365e73434d7708da14061adba34ddf7" + }, + "status": "completed", + "target": "B" + } + ] + }, + "relay_bounded_workers": 8, + "relay_has_trial_deadline": false +} diff --git a/jevdual/results/workbench-health-20261012/health-qualification-score.json b/jevdual/results/workbench-health-20261012/health-qualification-score.json new file mode 100644 index 0000000..7d99026 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/health-qualification-score.json @@ -0,0 +1,1164 @@ +{ + "base_qualification": { + "browser_transitions_passed": true, + "completed_cycles": 2, + "lost_reply_reconciled_without_resend": true, + "model_boundaries_passed": true, + "passed": true, + "source_unchanged": true + }, + "cancelled_model_calls": [], + "fresh_admission_proofs": [ + { + "command_id": "command-accc773656494216b8991d02cab9b603", + "command_sequence": 16, + "lease_sequence": 15, + "operation": "start_job", + "passed": true, + "proof_event": 2, + "state_read_sequence": 13 + }, + { + "command_id": "command-85d8b423ec7c4a53bb1987fb1a62bdf6", + "command_sequence": 20, + "lease_sequence": 19, + "operation": "observe", + "passed": true, + "proof_event": 5, + "state_read_sequence": 17 + }, + { + "command_id": "command-b93638f4643f4d35b5cde6fde06ed807", + "command_sequence": 32, + "lease_sequence": 31, + "operation": "propose", + "passed": true, + "proof_event": 10, + "state_read_sequence": 29 + }, + { + "command_id": "command-a44e60b9d8c24bdaa49e479c4b0ef780", + "command_sequence": 36, + "lease_sequence": 35, + "operation": "dispatch", + "passed": true, + "proof_event": 13, + "state_read_sequence": 33 + }, + { + "command_id": "command-79dcaf5d02e048449c681748167c446a", + "command_sequence": 40, + "lease_sequence": 39, + "operation": "observe", + "passed": true, + "proof_event": 16, + "state_read_sequence": 37 + }, + { + "command_id": "command-069d2460341443e69bbcd0b5f39b91f2", + "command_sequence": 49, + "lease_sequence": 48, + "operation": "propose", + "passed": true, + "proof_event": 20, + "state_read_sequence": 46 + }, + { + "command_id": "command-20fde1f33f97495cade8fce105d16ae4", + "command_sequence": 53, + "lease_sequence": 52, + "operation": "dispatch", + "passed": true, + "proof_event": 23, + "state_read_sequence": 50 + }, + { + "command_id": "command-107896fd188d4b72be6d2a6debd2ba0c", + "command_sequence": 57, + "lease_sequence": 56, + "operation": "observe", + "passed": true, + "proof_event": 26, + "state_read_sequence": 54 + }, + { + "command_id": "command-1bc0e6eb4e064fe1b7b66dfbf750968a", + "command_sequence": 68, + "lease_sequence": 67, + "operation": "propose", + "passed": true, + "proof_event": 30, + "state_read_sequence": 65 + }, + { + "command_id": "command-70f9b8c19b59452693061139bda6c71b", + "command_sequence": 72, + "lease_sequence": 71, + "operation": "dispatch", + "passed": true, + "proof_event": 33, + "state_read_sequence": 69 + }, + { + "command_id": "command-16683e1227d84968879714ddd0556488", + "command_sequence": 76, + "lease_sequence": 75, + "operation": "observe", + "passed": true, + "proof_event": 36, + "state_read_sequence": 73 + }, + { + "command_id": "command-1fb29328cf0a4c7e800022c949b77f7a", + "command_sequence": 85, + "lease_sequence": 84, + "operation": "propose", + "passed": true, + "proof_event": 40, + "state_read_sequence": 82 + }, + { + "command_id": "command-f1e1760598fa4cd89339d2d2091f8d1a", + "command_sequence": 89, + "lease_sequence": 88, + "operation": "dispatch", + "passed": true, + "proof_event": 43, + "state_read_sequence": 86 + }, + { + "command_id": "command-1a4dc2ad405846ecbdfaed4b2d825f3e", + "command_sequence": 94, + "lease_sequence": 93, + "operation": "observe", + "passed": true, + "proof_event": 46, + "state_read_sequence": 91 + }, + { + "command_id": "command-55007cfbc1d7410d92d582397af2555b", + "command_sequence": 103, + "lease_sequence": 102, + "operation": "propose", + "passed": true, + "proof_event": 50, + "state_read_sequence": 100 + }, + { + "command_id": "command-ee7b8dee4444416c98f550c3e5a7c01f", + "command_sequence": 107, + "lease_sequence": 106, + "operation": "dispatch", + "passed": true, + "proof_event": 53, + "state_read_sequence": 104 + }, + { + "command_id": "command-77279a97fd4a4ff2aee720c0b7a46c40", + "command_sequence": 111, + "lease_sequence": 110, + "operation": "observe", + "passed": true, + "proof_event": 56, + "state_read_sequence": 108 + }, + { + "command_id": "command-3211cda07dda4c5c8dd2cc700fdaad98", + "command_sequence": 121, + "lease_sequence": 120, + "operation": "propose", + "passed": true, + "proof_event": 61, + "state_read_sequence": 118 + }, + { + "command_id": "command-e52c9ef5005a410fb80600b4ad7f947a", + "command_sequence": 125, + "lease_sequence": 124, + "operation": "dispatch", + "passed": true, + "proof_event": 64, + "state_read_sequence": 122 + }, + { + "command_id": "command-97245de4fa73480c87503051fc90a8a9", + "command_sequence": 179, + "lease_sequence": 178, + "operation": "observe", + "passed": true, + "proof_event": 72, + "state_read_sequence": 176 + }, + { + "command_id": "command-5457db2a5bcf4cd3ba1b306e5182ede1", + "command_sequence": 200, + "lease_sequence": 199, + "operation": "propose", + "passed": true, + "proof_event": 76, + "state_read_sequence": 197 + }, + { + "command_id": "command-d4045c653de542aea3cd3f100603a0dd", + "command_sequence": 204, + "lease_sequence": 203, + "operation": "dispatch", + "passed": true, + "proof_event": 79, + "state_read_sequence": 201 + }, + { + "command_id": "command-127c700baa7e4224a9f4d4d1654380fc", + "command_sequence": 208, + "lease_sequence": 207, + "operation": "observe", + "passed": true, + "proof_event": 82, + "state_read_sequence": 205 + }, + { + "command_id": "command-12d35e26325248a5881960ffcbd97a6f", + "command_sequence": 223, + "lease_sequence": 222, + "operation": "propose", + "passed": true, + "proof_event": 86, + "state_read_sequence": 220 + }, + { + "command_id": "command-fb95362e732c42c793f505dd7673ea38", + "command_sequence": 227, + "lease_sequence": 226, + "operation": "dispatch", + "passed": true, + "proof_event": 89, + "state_read_sequence": 224 + }, + { + "command_id": "command-5365431f8f094a738c04898a662544f5", + "command_sequence": 231, + "lease_sequence": 230, + "operation": "observe", + "passed": true, + "proof_event": 92, + "state_read_sequence": 228 + }, + { + "command_id": "command-1ca372f4627f44ca820de2e16d58a939", + "command_sequence": 246, + "lease_sequence": 245, + "operation": "propose", + "passed": true, + "proof_event": 96, + "state_read_sequence": 243 + }, + { + "command_id": "command-8074e0e06a9d46f68b944f8a3949a7d5", + "command_sequence": 250, + "lease_sequence": 249, + "operation": "dispatch", + "passed": true, + "proof_event": 99, + "state_read_sequence": 247 + }, + { + "command_id": "command-58c9af8646c94c0b9e453fb32e4622f1", + "command_sequence": 254, + "lease_sequence": 253, + "operation": "observe", + "passed": true, + "proof_event": 102, + "state_read_sequence": 251 + }, + { + "command_id": "command-706d43a193d349b0abb2a1a2e213670d", + "command_sequence": 269, + "lease_sequence": 268, + "operation": "propose", + "passed": true, + "proof_event": 106, + "state_read_sequence": 266 + }, + { + "command_id": "command-f01b770705654cf0b746795ef56a5e51", + "command_sequence": 273, + "lease_sequence": 272, + "operation": "dispatch", + "passed": true, + "proof_event": 109, + "state_read_sequence": 270 + }, + { + "command_id": "command-01c5b18c0f4848ab83a3a506c6164bd7", + "command_sequence": 277, + "lease_sequence": 276, + "operation": "observe", + "passed": true, + "proof_event": 112, + "state_read_sequence": 274 + }, + { + "command_id": "command-455161d907e44cc2a0c00c4713404a47", + "command_sequence": 292, + "lease_sequence": 291, + "operation": "propose", + "passed": true, + "proof_event": 116, + "state_read_sequence": 289 + }, + { + "command_id": "command-c085b10cc40c44539a927aa06c7a4339", + "command_sequence": 296, + "lease_sequence": 295, + "operation": "dispatch", + "passed": true, + "proof_event": 119, + "state_read_sequence": 293 + }, + { + "command_id": "command-7c91d1e39cc047049a3c4453e8b35923", + "command_sequence": 300, + "lease_sequence": 299, + "operation": "observe", + "passed": true, + "proof_event": 122, + "state_read_sequence": 297 + }, + { + "command_id": "command-02d0ee9bb015446e974769918792b02f", + "command_sequence": 313, + "lease_sequence": 312, + "operation": "propose", + "passed": true, + "proof_event": 126, + "state_read_sequence": 310 + }, + { + "command_id": "command-15941d3c6bb74b9a8916ec43a2fe0e1f", + "command_sequence": 317, + "lease_sequence": 316, + "operation": "dispatch", + "passed": true, + "proof_event": 129, + "state_read_sequence": 314 + }, + { + "command_id": "command-f615d864a28e4af88536d43c26a06e7c", + "command_sequence": 321, + "lease_sequence": 320, + "operation": "observe", + "passed": true, + "proof_event": 132, + "state_read_sequence": 318 + }, + { + "command_id": "command-d11cdb7e2538490784e2e8a2425572b7", + "command_sequence": 334, + "lease_sequence": 333, + "operation": "propose", + "passed": true, + "proof_event": 136, + "state_read_sequence": 331 + }, + { + "command_id": "command-cdf687139f2e43cab73f3afa8053018d", + "command_sequence": 338, + "lease_sequence": 337, + "operation": "dispatch", + "passed": true, + "proof_event": 139, + "state_read_sequence": 335 + }, + { + "command_id": "command-6db96f5e11fc4baebb1607c174a5898f", + "command_sequence": 342, + "lease_sequence": 341, + "operation": "observe", + "passed": true, + "proof_event": 142, + "state_read_sequence": 339 + }, + { + "command_id": "command-000b1a6737a14842a6b79c23e0a5ca92", + "command_sequence": 357, + "lease_sequence": 356, + "operation": "propose", + "passed": true, + "proof_event": 146, + "state_read_sequence": 354 + }, + { + "command_id": "command-72de9f33d999438198fc8b176ee89f6d", + "command_sequence": 361, + "lease_sequence": 360, + "operation": "dispatch", + "passed": true, + "proof_event": 149, + "state_read_sequence": 358 + }, + { + "command_id": "command-155208c1dd824b43a411bf0d9f879dc9", + "command_sequence": 366, + "lease_sequence": 365, + "operation": "start_job", + "passed": true, + "proof_event": 152, + "state_read_sequence": 363 + }, + { + "command_id": "command-debb71a7f11f45b1b2093c2da309bb26", + "command_sequence": 370, + "lease_sequence": 369, + "operation": "observe", + "passed": true, + "proof_event": 155, + "state_read_sequence": 367 + }, + { + "command_id": "command-8fe46178c8564d2b8af7b48dd51ceb69", + "command_sequence": 387, + "lease_sequence": 386, + "operation": "propose", + "passed": true, + "proof_event": 159, + "state_read_sequence": 384 + }, + { + "command_id": "command-1fdd5229845447258a05de892ad29824", + "command_sequence": 391, + "lease_sequence": 390, + "operation": "dispatch", + "passed": true, + "proof_event": 162, + "state_read_sequence": 388 + }, + { + "command_id": "command-7e14ebef15814262bf16ee0a417469ea", + "command_sequence": 395, + "lease_sequence": 394, + "operation": "observe", + "passed": true, + "proof_event": 165, + "state_read_sequence": 392 + }, + { + "command_id": "command-a6931c3bf281480fb0dd090384b20178", + "command_sequence": 408, + "lease_sequence": 407, + "operation": "propose", + "passed": true, + "proof_event": 169, + "state_read_sequence": 405 + }, + { + "command_id": "command-5a3fb52b0a34409da8c43ce284d498f8", + "command_sequence": 412, + "lease_sequence": 411, + "operation": "dispatch", + "passed": true, + "proof_event": 172, + "state_read_sequence": 409 + }, + { + "command_id": "command-6371aeae6cdb432c851d1a095b64f85e", + "command_sequence": 416, + "lease_sequence": 415, + "operation": "observe", + "passed": true, + "proof_event": 175, + "state_read_sequence": 413 + }, + { + "command_id": "command-020d09130bdf4e10b2e30aed41ee93fd", + "command_sequence": 433, + "lease_sequence": 432, + "operation": "propose", + "passed": true, + "proof_event": 179, + "state_read_sequence": 430 + }, + { + "command_id": "command-185d0f856fbc4990be03fb5a4d680444", + "command_sequence": 437, + "lease_sequence": 436, + "operation": "dispatch", + "passed": true, + "proof_event": 182, + "state_read_sequence": 434 + }, + { + "command_id": "command-271d260ff225464ca18069efe6b89a3f", + "command_sequence": 442, + "lease_sequence": 441, + "operation": "observe", + "passed": true, + "proof_event": 185, + "state_read_sequence": 439 + }, + { + "command_id": "command-8bc103965b3b4b548f0ab2a4f6529315", + "command_sequence": 455, + "lease_sequence": 454, + "operation": "propose", + "passed": true, + "proof_event": 189, + "state_read_sequence": 452 + }, + { + "command_id": "command-52b299434a2a4354ae1a0c27f7afda73", + "command_sequence": 459, + "lease_sequence": 458, + "operation": "dispatch", + "passed": true, + "proof_event": 192, + "state_read_sequence": 456 + }, + { + "command_id": "command-b50b8172d84a4c2ca69c1b13799c0cd3", + "command_sequence": 463, + "lease_sequence": 462, + "operation": "observe", + "passed": true, + "proof_event": 195, + "state_read_sequence": 460 + }, + { + "command_id": "command-55878c3e64e7438786d949269a3ca632", + "command_sequence": 478, + "lease_sequence": 477, + "operation": "propose", + "passed": true, + "proof_event": 199, + "state_read_sequence": 475 + }, + { + "command_id": "command-3c4907bffd1043b7841f4f7a81e14c2e", + "command_sequence": 482, + "lease_sequence": 481, + "operation": "dispatch", + "passed": true, + "proof_event": 202, + "state_read_sequence": 479 + }, + { + "command_id": "command-a3d1bf0c87ed4d748bcfddf5acbcdbc8", + "command_sequence": 486, + "lease_sequence": 485, + "operation": "observe", + "passed": true, + "proof_event": 205, + "state_read_sequence": 483 + }, + { + "command_id": "command-c2820c925b7b43e48231001f02731ba6", + "command_sequence": 501, + "lease_sequence": 500, + "operation": "propose", + "passed": true, + "proof_event": 209, + "state_read_sequence": 498 + }, + { + "command_id": "command-8a9753d1c9604e3094a1478b421465f0", + "command_sequence": 505, + "lease_sequence": 504, + "operation": "dispatch", + "passed": true, + "proof_event": 212, + "state_read_sequence": 502 + }, + { + "command_id": "command-c21a6cfb3b154f799bb67d2695787ef8", + "command_sequence": 509, + "lease_sequence": 508, + "operation": "observe", + "passed": true, + "proof_event": 215, + "state_read_sequence": 506 + }, + { + "command_id": "command-6d7f70c0e5b14d6494d7eb02930b9d66", + "command_sequence": 524, + "lease_sequence": 523, + "operation": "propose", + "passed": true, + "proof_event": 219, + "state_read_sequence": 521 + }, + { + "command_id": "command-7d2c4e20bc454be996d99dc7edeea280", + "command_sequence": 528, + "lease_sequence": 527, + "operation": "dispatch", + "passed": true, + "proof_event": 222, + "state_read_sequence": 525 + }, + { + "command_id": "command-3352c7fd2fb14ffcaf69c1b27c6c53d9", + "command_sequence": 532, + "lease_sequence": 531, + "operation": "observe", + "passed": true, + "proof_event": 225, + "state_read_sequence": 529 + }, + { + "command_id": "command-896ce409c627486b9104d8d7415cefd3", + "command_sequence": 551, + "lease_sequence": 550, + "operation": "propose", + "passed": true, + "proof_event": 229, + "state_read_sequence": 548 + }, + { + "command_id": "command-1875feda0eaf4c058b771536779ccd62", + "command_sequence": 555, + "lease_sequence": 554, + "operation": "dispatch", + "passed": true, + "proof_event": 232, + "state_read_sequence": 552 + }, + { + "command_id": "command-844f1072836b419fa30a4c4f4a8697f1", + "command_sequence": 559, + "lease_sequence": 558, + "operation": "observe", + "passed": true, + "proof_event": 235, + "state_read_sequence": 556 + }, + { + "command_id": "command-7d6435405eaf4824b97108643856b361", + "command_sequence": 572, + "lease_sequence": 571, + "operation": "propose", + "passed": true, + "proof_event": 239, + "state_read_sequence": 569 + }, + { + "command_id": "command-7408e15ea38244a592d2800662c2b3c8", + "command_sequence": 576, + "lease_sequence": 575, + "operation": "dispatch", + "passed": true, + "proof_event": 242, + "state_read_sequence": 573 + }, + { + "command_id": "command-3cdfb3055b044aeeb059afc8731dac9c", + "command_sequence": 580, + "lease_sequence": 579, + "operation": "observe", + "passed": true, + "proof_event": 245, + "state_read_sequence": 577 + }, + { + "command_id": "command-59ebc979948340c1bceb69a8e01ac2aa", + "command_sequence": 597, + "lease_sequence": 596, + "operation": "propose", + "passed": true, + "proof_event": 249, + "state_read_sequence": 594 + }, + { + "command_id": "command-7761812fccc94a208c8f3e2dc26c28ec", + "command_sequence": 601, + "lease_sequence": 600, + "operation": "dispatch", + "passed": true, + "proof_event": 252, + "state_read_sequence": 598 + }, + { + "command_id": "command-2e37938e1f6a4a63b928572e1cf365af", + "command_sequence": 605, + "lease_sequence": 604, + "operation": "observe", + "passed": true, + "proof_event": 255, + "state_read_sequence": 602 + }, + { + "command_id": "command-e6cd1a382110409ab381d5fd4c4cc3b0", + "command_sequence": 620, + "lease_sequence": 619, + "operation": "propose", + "passed": true, + "proof_event": 259, + "state_read_sequence": 617 + }, + { + "command_id": "command-34752e8b30564841a21d087d561a5930", + "command_sequence": 624, + "lease_sequence": 623, + "operation": "dispatch", + "passed": true, + "proof_event": 262, + "state_read_sequence": 621 + }, + { + "command_id": "command-c966c8b893db42eb84c5b0616acca391", + "command_sequence": 628, + "lease_sequence": 627, + "operation": "observe", + "passed": true, + "proof_event": 265, + "state_read_sequence": 625 + }, + { + "command_id": "command-c8ad5019377e44159aef0ee22364b18f", + "command_sequence": 638, + "lease_sequence": 637, + "operation": "propose", + "passed": true, + "proof_event": 269, + "state_read_sequence": 635 + }, + { + "command_id": "command-10a03ed616ea4d96a26e96ae0973c32b", + "command_sequence": 642, + "lease_sequence": 641, + "operation": "dispatch", + "passed": true, + "proof_event": 272, + "state_read_sequence": 639 + }, + { + "command_id": "command-6b6e57d717d249c7811ef4708c6b8667", + "command_sequence": 646, + "lease_sequence": 645, + "operation": "observe", + "passed": true, + "proof_event": 275, + "state_read_sequence": 643 + }, + { + "command_id": "command-594d54e2793d428bbb42f80713e94ed3", + "command_sequence": 655, + "lease_sequence": 654, + "operation": "propose", + "passed": true, + "proof_event": 279, + "state_read_sequence": 652 + }, + { + "command_id": "command-bd5377590da74532a8333ac5ca2a72b3", + "command_sequence": 659, + "lease_sequence": 658, + "operation": "dispatch", + "passed": true, + "proof_event": 282, + "state_read_sequence": 656 + } + ], + "fresh_proof_every_rpc": true, + "genuine_upstream_long_read_count": 0, + "genuine_upstream_long_reads": [], + "healthy_injected_long_read_count": 173, + "healthy_injected_long_reads": [ + { + "injected_elapsed_s": 1.2040192501153797, + "path": "/identity", + "sequence": 2, + "upstream_elapsed_s": 0.002776124980300665 + }, + { + "injected_elapsed_s": 1.2024811250157654, + "path": "/identity", + "sequence": 3, + "upstream_elapsed_s": 0.005044083809480071 + }, + { + "injected_elapsed_s": 1.2039304592180997, + "path": "/identity", + "sequence": 4, + "upstream_elapsed_s": 0.0035592911299318075 + }, + { + "injected_elapsed_s": 1.2020084999967366, + "path": "/identity", + "sequence": 5, + "upstream_elapsed_s": 0.0029993748757988214 + }, + { + "injected_elapsed_s": 1.2050337500404567, + "path": "/identity", + "sequence": 6, + "upstream_elapsed_s": 0.0033572500105947256 + }, + { + "injected_elapsed_s": 1.203755582915619, + "path": "/identity", + "sequence": 7, + "upstream_elapsed_s": 0.09075575019232929 + }, + { + "injected_elapsed_s": 1.2050538749899715, + "path": "/identity", + "sequence": 8, + "upstream_elapsed_s": 0.0031042909249663353 + }, + { + "injected_elapsed_s": 1.203882124973461, + "path": "/identity", + "sequence": 9, + "upstream_elapsed_s": 0.0031348341144621372 + }, + { + "injected_elapsed_s": 1.204291749978438, + "path": "/identity", + "sequence": 10, + "upstream_elapsed_s": 0.005035291891545057 + }, + { + "injected_elapsed_s": 1.2027659590821713, + "path": "/identity", + "sequence": 11, + "upstream_elapsed_s": 0.002828457858413458 + }, + { + "injected_elapsed_s": 1.2050365000031888, + "path": "/identity", + "sequence": 12, + "upstream_elapsed_s": 0.003178749931976199 + }, + { + "injected_elapsed_s": 1.201763124903664, + "path": "/identity", + "sequence": 22, + "upstream_elapsed_s": 0.00289629097096622 + }, + { + "injected_elapsed_s": 1.2055749581195414, + "path": "/identity", + "sequence": 23, + "upstream_elapsed_s": 0.002508667064830661 + }, + { + "injected_elapsed_s": 1.205075625097379, + "path": "/identity", + "sequence": 24, + "upstream_elapsed_s": 0.003260375000536442 + }, + { + "injected_elapsed_s": 1.2044482911005616, + "path": "/identity", + "sequence": 25, + "upstream_elapsed_s": 0.003892249893397093 + }, + { + "injected_elapsed_s": 1.204990333179012, + "path": "/identity", + "sequence": 26, + "upstream_elapsed_s": 0.003343291115015745 + }, + { + "injected_elapsed_s": 1.2049401660915464, + "path": "/identity", + "sequence": 27, + "upstream_elapsed_s": 0.0034893339034169912 + }, + { + "injected_elapsed_s": 1.204629332991317, + "path": "/identity", + "sequence": 28, + "upstream_elapsed_s": 0.003361874958500266 + }, + { + "injected_elapsed_s": 1.2040073750540614, + "path": "/identity", + "sequence": 42, + "upstream_elapsed_s": 0.00299987499602139 + }, + { + "injected_elapsed_s": 1.2035016249865294, + "path": "/identity", + "sequence": 43, + "upstream_elapsed_s": 0.0031940829940140247 + }, + { + "injected_elapsed_s": 1.2012091248761863, + "path": "/identity", + "sequence": 44, + "upstream_elapsed_s": 0.004062541993334889 + }, + { + "injected_elapsed_s": 1.2042698750738055, + "path": "/identity", + "sequence": 45, + "upstream_elapsed_s": 0.002662374870851636 + }, + { + "injected_elapsed_s": 1.2008610831107944, + "path": "/identity", + "sequence": 59, + "upstream_elapsed_s": 0.0025203339755535126 + }, + { + "injected_elapsed_s": 1.2012784169055521, + "path": "/identity", + "sequence": 60, + "upstream_elapsed_s": 0.009544290835037827 + }, + { + "injected_elapsed_s": 1.2050834170076996, + "path": "/identity", + "sequence": 61, + "upstream_elapsed_s": 0.0035029591526836157 + }, + { + "injected_elapsed_s": 1.2050489170942456, + "path": "/identity", + "sequence": 62, + "upstream_elapsed_s": 0.0033430408220738173 + }, + { + "injected_elapsed_s": 1.200188459130004, + "path": "/identity", + "sequence": 63, + "upstream_elapsed_s": 0.004118707962334156 + }, + { + "injected_elapsed_s": 1.2050984590314329, + "path": "/identity", + "sequence": 64, + "upstream_elapsed_s": 0.007497084094211459 + }, + { + "injected_elapsed_s": 1.2025703340768814, + "path": "/identity", + "sequence": 78, + "upstream_elapsed_s": 0.0029468329157680273 + }, + { + "injected_elapsed_s": 1.2046614589635283, + "path": "/identity", + "sequence": 79, + "upstream_elapsed_s": 0.009623541962355375 + }, + { + "injected_elapsed_s": 1.2050566668622196, + "path": "/identity", + "sequence": 80, + "upstream_elapsed_s": 0.003390332916751504 + }, + { + "injected_elapsed_s": 1.2048170422203839, + "path": "/identity", + "sequence": 81, + "upstream_elapsed_s": 0.00339320907369256 + }, + { + "injected_elapsed_s": 1.2029053340665996, + "path": "/identity", + "sequence": 96, + "upstream_elapsed_s": 0.012598375091329217 + }, + { + "injected_elapsed_s": 1.2034727919381112, + "path": "/identity", + "sequence": 97, + "upstream_elapsed_s": 0.01664000004529953 + }, + { + "injected_elapsed_s": 1.2048349999822676, + "path": "/identity", + "sequence": 98, + "upstream_elapsed_s": 0.019909291993826628 + }, + { + "injected_elapsed_s": 1.2022679159417748, + "path": "/identity", + "sequence": 99, + "upstream_elapsed_s": 0.016452833777293563 + }, + { + "injected_elapsed_s": 1.2038175829220563, + "path": "/identity", + "sequence": 113, + "upstream_elapsed_s": 0.013531167060136795 + }, + { + "injected_elapsed_s": 1.2040615831501782, + "path": "/identity", + "sequence": 114, + "upstream_elapsed_s": 0.013567666988819838 + }, + { + "injected_elapsed_s": 1.2006667500827461, + "path": "/identity", + "sequence": 115, + "upstream_elapsed_s": 0.021057708887383342 + }, + { + "injected_elapsed_s": 1.2005423330701888, + "path": "/identity", + "sequence": 116, + "upstream_elapsed_s": 0.014461665879935026 + }, + { + "injected_elapsed_s": 1.204718666849658, + "path": "/identity", + "sequence": 117, + "upstream_elapsed_s": 0.017344833118841052 + }, + { + "injected_elapsed_s": 1.2039870421867818, + "path": "/identity", + "sequence": 126, + "upstream_elapsed_s": 0.019155291141942143 + }, + { + "injected_elapsed_s": 1.2014507499989122, + "path": "/identity", + "sequence": 127, + "upstream_elapsed_s": 0.012994375079870224 + }, + { + "injected_elapsed_s": 1.2014389999676496, + "path": "/identity", + "sequence": 128, + "upstream_elapsed_s": 0.02700358279980719 + }, + { + "injected_elapsed_s": 1.2036458330694586, + "path": "/identity", + "sequence": 129, + "upstream_elapsed_s": 0.01330141699872911 + }, + { + "injected_elapsed_s": 1.2067252080887556, + "path": "/identity", + "sequence": 130, + "upstream_elapsed_s": 0.024299542186781764 + }, + { + "injected_elapsed_s": 1.2093212499748915, + "path": "/identity", + "sequence": 131, + "upstream_elapsed_s": 0.033242583042010665 + }, + { + "injected_elapsed_s": 1.2037583331111819, + "path": "/identity", + "sequence": 132, + "upstream_elapsed_s": 0.0233499170280993 + }, + { + "injected_elapsed_s": 1.2021812500897795, + "path": "/identity", + "sequence": 133, + "upstream_elapsed_s": 0.023109209025278687 + }, + { + "injected_elapsed_s": 1.2020818751771003, + "path": "/state", + "sequence": 211, + "upstream_elapsed_s": 0.04533958388492465 + }, + { + "injected_elapsed_s": 1.2005837499164045, + "path": "/state", + "sequence": 213, + "upstream_elapsed_s": 0.04676191695034504 + }, + { + "injected_elapsed_s": 1.2046695840544999, + "path": "/state", + "sequence": 215, + "upstream_elapsed_s": 0.04107379191555083 + }, + { + "injected_elapsed_s": 1.2013546251691878, + "path": "/state", + "sequence": 217, + "upstream_elapsed_s": 0.04071483411826193 + }, + { + "injected_elapsed_s": 1.2013900419697165, + "path": "/state", + "sequence": 219, + "upstream_elapsed_s": 0.041966750053688884 + }, + { + "injected_elapsed_s": 1.2015124999452382, + "path": "/state", + "sequence": 234, + "upstream_elapsed_s": 0.04945133300498128 + }, + { + "injected_elapsed_s": 1.2010567500256002, + "path": "/state", + "sequence": 236, + "upstream_elapsed_s": 0.04513141606003046 + }, + { + "injected_elapsed_s": 1.2007370002102107, + "path": "/state", + "sequence": 238, + "upstream_elapsed_s": 0.046239792136475444 + }, + { + "injected_elapsed_s": 1.2003611251711845, + "path": "/state", + "sequence": 240, + "upstream_elapsed_s": 0.05429583298973739 + }, + { + "injected_elapsed_s": 1.20504245813936, + "path": "/state", + "sequence": 242, + "upstream_elapsed_s": 0.04574579210020602 + }, + { + "injected_elapsed_s": 1.202794292010367, + "path": "/state", + "sequence": 257, + "upstream_elapsed_s": 0.04627279192209244 + }, + { + "injected_elapsed_s": 1.2046860409900546, + "path": "/state", + "sequence": 259, + "upstream_elapsed_s": 0.04723550006747246 + }, + { + "injected_elapsed_s": 1.2001820839941502, + "path": "/state", + "sequence": 261, + "upstream_elapsed_s": 0.047370166052132845 + }, + { + "injected_elapsed_s": 1.2050447911024094, + "path": "/state", + "sequence": 263, + "upstream_elapsed_s": 0.04227699991315603 + }, + { + "injected_elapsed_s": 1.2012766250409186, + "path": "/state", + "sequence": 265, + "upstream_elapsed_s": 0.056063625030219555 + } + ], + "owned_readers_exited": true, + "passed": true, + "relay_cleanup": { + "active_workers": 0, + "completed_workers": 685, + "peak_workers": 2, + "request_count": 685, + "server_thread_exited": true, + "worker_bound": 8 + }, + "relay_evidence_valid": true, + "scope": "same-Mac loopback simulation; 1.2s diagnostic auxiliary read delay", + "soft_checking_visible_while_running": true, + "unexpected_halts": [] +} diff --git a/jevdual/results/workbench-health-20261012/identity.json b/jevdual/results/workbench-health-20261012/identity.json new file mode 100644 index 0000000..c505c87 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/identity.json @@ -0,0 +1,8 @@ +{ + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" +} diff --git a/jevdual/results/workbench-health-20261012/preserved-baseline.json b/jevdual/results/workbench-health-20261012/preserved-baseline.json new file mode 100644 index 0000000..30ce9fb --- /dev/null +++ b/jevdual/results/workbench-health-20261012/preserved-baseline.json @@ -0,0 +1,12 @@ +{ + "previous_root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "completed_jobs": 2, + "source_commit": "3de70f1de65a74e9285f651418852fe88672d2a4", + "preserved_sources": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim12-preserved-baseline/source", + "stopped_owned_pids": [ + 8139, + 8145 + ] +} diff --git a/jevdual/results/workbench-health-20261012/process-ledger.json b/jevdual/results/workbench-health-20261012/process-ledger.json new file mode 100644 index 0000000..b887c30 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/process-ledger.json @@ -0,0 +1,40 @@ +{ + "app_id": "433d80f43f4148ef87d341b6d117396d", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "body_origin": "http://127.0.0.1:51373", + "browser_origin": "http://127.0.0.1:51377", + "health_delay_s": 1.2, + "limit_s": 2700, + "owned_processes_exited": true, + "planner": "actual_codex_cli_images_over_TCP", + "processes": [ + { + "exit_observed": true, + "pid": 63766, + "returncode": 0, + "role": "body" + }, + { + "exit_observed": true, + "pid": 63768, + "returncode": 0, + "role": "health-relay" + }, + { + "exit_observed": true, + "pid": 63773, + "returncode": 0, + "role": "initialize" + }, + { + "exit_observed": true, + "pid": 63779, + "returncode": 0, + "role": "workbench" + } + ], + "ready": true, + "relay_origin": "http://127.0.0.1:49270", + "simulation": true, + "source_commit": "37ebfdb3054b977149d7b60b46254ddd852b749a" +} diff --git a/jevdual/results/workbench-health-20261012/qualification-score.json b/jevdual/results/workbench-health-20261012/qualification-score.json new file mode 100644 index 0000000..cddb0b7 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/qualification-score.json @@ -0,0 +1,724 @@ +{ + "body_id": "eea757a5363c43ca81de4a2f78715534", + "browser_transitions_passed": true, + "calls": [ + { + "action": "move", + "call": 1, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10880, + "input_tokens": 13076, + "output_tokens": 213 + }, + "withheld": false + }, + { + "action": "move", + "call": 2, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12681, + "output_tokens": 168 + }, + "withheld": false + }, + { + "action": "measure", + "call": 3, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 12752, + "output_tokens": 142 + }, + "withheld": false + }, + { + "action": "begin_reset", + "call": 4, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10880, + "input_tokens": 13288, + "output_tokens": 42 + }, + "withheld": false + }, + { + "action": "move", + "call": 5, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12887, + "output_tokens": 158 + }, + "withheld": false + }, + { + "action": "move", + "call": 6, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12958, + "output_tokens": 183 + }, + "withheld": false + }, + { + "action": "move", + "call": 7, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10880, + "input_tokens": 13496, + "output_tokens": 190 + }, + "withheld": false + }, + { + "action": "move", + "call": 8, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13038, + "output_tokens": 185 + }, + "withheld": false + }, + { + "action": "move", + "call": 9, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13040, + "output_tokens": 156 + }, + "withheld": false + }, + { + "action": "move", + "call": 10, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13041, + "output_tokens": 182 + }, + "withheld": false + }, + { + "action": "move", + "call": 11, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13503, + "output_tokens": 134 + }, + "withheld": false + }, + { + "action": "move", + "call": 12, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13516, + "output_tokens": 131 + }, + "withheld": false + }, + { + "action": "finish_reset", + "call": 13, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13052, + "output_tokens": 112 + }, + "withheld": false + }, + { + "action": "move", + "call": 1, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10880, + "input_tokens": 13057, + "output_tokens": 194 + }, + "withheld": false + }, + { + "action": "move", + "call": 2, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 12678, + "output_tokens": 142 + }, + "withheld": false + }, + { + "action": "move", + "call": 3, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10880, + "input_tokens": 13215, + "output_tokens": 187 + }, + "withheld": false + }, + { + "action": "measure", + "call": 4, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 12821, + "output_tokens": 134 + }, + "withheld": false + }, + { + "action": "begin_reset", + "call": 5, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10880, + "input_tokens": 13358, + "output_tokens": 42 + }, + "withheld": false + }, + { + "action": "move", + "call": 6, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 12957, + "output_tokens": 189 + }, + "withheld": false + }, + { + "action": "move", + "call": 7, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13032, + "output_tokens": 137 + }, + "withheld": false + }, + { + "action": "move", + "call": 8, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13033, + "output_tokens": 229 + }, + "withheld": false + }, + { + "action": "move", + "call": 9, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13038, + "output_tokens": 175 + }, + "withheld": false + }, + { + "action": "move", + "call": 10, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13039, + "output_tokens": 182 + }, + "withheld": false + }, + { + "action": "move", + "call": 11, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13039, + "output_tokens": 161 + }, + "withheld": false + }, + { + "action": "move", + "call": 12, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13047, + "output_tokens": 135 + }, + "withheld": false + }, + { + "action": "move", + "call": 13, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13044, + "output_tokens": 121 + }, + "withheld": false + }, + { + "action": "finish_reset", + "call": 14, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13048, + "output_tokens": 105 + }, + "withheld": false + } + ], + "completed_cycles": 2, + "lost_reply_reconciled_without_resend": true, + "model_boundaries_passed": true, + "owned_processes_exited": true, + "passed": true, + "scope": "browser UI and actual image policy over loopback TCP simulation", + "scores": [ + { + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "measurement_receipt": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "generation": 3, + "image_sha256": "abd56127579f6a8c8dd685d44e7d633a4b7f230837d5892d1b0d10b71bbddd4b", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 6.401999999999517, + "operation_id": "agent-48fcf185c3fc465ea239b3ade25ddf5b", + "source_kind": "mujoco_simulation", + "state_sha256": "f6e8a7529bd2326775b9043e7a716b126906ab7003fafbc6f229c159b904c7d0", + "version": 1, + "worker_pid": 66283 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06580390404818154, + -1.5285398651025924e-10, + 0.034995730455369904 + ], + "external_force_cleared": true, + "generation": 3, + "native_time_s": 6.401999999999517, + "pusher_home": false, + "pusher_xyz": [ + -0.029999970057503877, + 1.0537612379601558e-25, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "f6e8a7529bd2326775b9043e7a716b126906ab7003fafbc6f229c159b904c7d0" + }, + "model_calls": 14, + "motion_count": 11, + "passed": true, + "reset_receipt": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "block_home_verified": true, + "generation": 11, + "image_sha256": "e47008c08f81d1ed137d87f021a4273e354a0c3d79e0c9b1da21e6dbeaf28429", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "kind": "finish_reset", + "native_time_s": 22.28199999999853, + "operation_id": "agent-53ec814ec65e409f894ccaf9c1dd2c2a", + "pusher_home_verified": true, + "reset_contact_samples": 3166, + "reset_native_steps": 7940, + "state_sha256": "0de60aca2a69c72c36eb8396ce0c3e420c60000ca6190ec36315207e14c804a4" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11082008854694171, + -2.0220096198944655e-09, + 0.034995730455369765 + ], + "external_force_cleared": true, + "generation": 11, + "native_time_s": 22.28199999999853, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "0de60aca2a69c72c36eb8396ce0c3e420c60000ca6190ec36315207e14c804a4" + } + }, + { + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "measurement_receipt": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "generation": 13, + "image_sha256": "6f3ad87788edf85031472937026d8d6bfe0a4e8304ca20491585684be65d6725", + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 26.817999999996015, + "operation_id": "agent-eb4c669d330d4019afbef8648c3bf058", + "source_kind": "mujoco_simulation", + "state_sha256": "d1a7c1fbfc10e537f714dd8dfb5bc518914f1a36ec5477a71b4ad4f0f78969c3", + "version": 1, + "worker_pid": 75476 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.07584523883164036, + -2.021993327103355e-09, + 0.03499573045536984 + ], + "external_force_cleared": true, + "generation": 13, + "native_time_s": 26.817999999996015, + "pusher_home": false, + "pusher_xyz": [ + -0.029999969669727955, + 5.643066017863546e-26, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "d1a7c1fbfc10e537f714dd8dfb5bc518914f1a36ec5477a71b4ad4f0f78969c3" + }, + "model_calls": 13, + "motion_count": 10, + "passed": true, + "reset_receipt": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "block_home_verified": true, + "generation": 21, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "kind": "finish_reset", + "native_time_s": 43.03000000000662, + "operation_id": "agent-6ec3f611bd9042a5b9a1acc3867b74e7", + "pusher_home_verified": true, + "reset_contact_samples": 3304, + "reset_native_steps": 8106, + "state_sha256": "d3bdf81922c73fc0177d3453905a0376c365e73434d7708da14061adba34ddf7" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11578232825908402, + -4.197266959787217e-09, + 0.03499573045536993 + ], + "external_force_cleared": true, + "generation": 21, + "native_time_s": 43.03000000000662, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "d3bdf81922c73fc0177d3453905a0376c365e73434d7708da14061adba34ddf7" + } + } + ], + "source_commit": "37ebfdb3054b977149d7b60b46254ddd852b749a", + "source_unchanged": true, + "sqlite": { + "count": 2, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "generation": 13, + "image_sha256": "6f3ad87788edf85031472937026d8d6bfe0a4e8304ca20491585684be65d6725", + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "operation_id": "agent-eb4c669d330d4019afbef8648c3bf058", + "receipt": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "generation": 13, + "image_sha256": "6f3ad87788edf85031472937026d8d6bfe0a4e8304ca20491585684be65d6725", + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 26.817999999996015, + "operation_id": "agent-eb4c669d330d4019afbef8648c3bf058", + "source_kind": "mujoco_simulation", + "state_sha256": "d1a7c1fbfc10e537f714dd8dfb5bc518914f1a36ec5477a71b4ad4f0f78969c3", + "version": 1, + "worker_pid": 75476 + }, + "state_sha256": "d1a7c1fbfc10e537f714dd8dfb5bc518914f1a36ec5477a71b4ad4f0f78969c3" + }, + { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "generation": 3, + "image_sha256": "abd56127579f6a8c8dd685d44e7d633a4b7f230837d5892d1b0d10b71bbddd4b", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "operation_id": "agent-48fcf185c3fc465ea239b3ade25ddf5b", + "receipt": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "generation": 3, + "image_sha256": "abd56127579f6a8c8dd685d44e7d633a4b7f230837d5892d1b0d10b71bbddd4b", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 6.401999999999517, + "operation_id": "agent-48fcf185c3fc465ea239b3ade25ddf5b", + "source_kind": "mujoco_simulation", + "state_sha256": "f6e8a7529bd2326775b9043e7a716b126906ab7003fafbc6f229c159b904c7d0", + "version": 1, + "worker_pid": 66283 + }, + "state_sha256": "f6e8a7529bd2326775b9043e7a716b126906ab7003fafbc6f229c159b904c7d0" + } + ] + }, + "transport_command_count": 83, + "transport_journal_bound": true, + "ui_steps": [ + "completed", + "disconnected", + "healthy_checking", + "offline_receipt", + "reconnected", + "recovered", + "reloaded", + "resumed", + "successive_checking", + "uncertain" + ] +} diff --git a/jevdual/results/workbench-health-20261012/relay-cleanup.json b/jevdual/results/workbench-health-20261012/relay-cleanup.json new file mode 100644 index 0000000..0374b50 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/relay-cleanup.json @@ -0,0 +1,8 @@ +{ + "active_workers": 0, + "completed_workers": 685, + "peak_workers": 2, + "request_count": 685, + "server_thread_exited": true, + "worker_bound": 8 +} diff --git a/jevdual/results/workbench-health-20261012/report.md b/jevdual/results/workbench-health-20261012/report.md new file mode 100644 index 0000000..488b38a --- /dev/null +++ b/jevdual/results/workbench-health-20261012/report.md @@ -0,0 +1,30 @@ +# Delayed health checks without false pauses + +Two actual image-guided jobs completed under deliberately delayed health replies with zero unexpected halts or cancelled decisions. The [independent health score](health-qualification-score.json) passed on frozen source `37ebfdb3054b977149d7b60b46254ddd852b749a`. Every one of 83 commands had fresh pinned ready-state proof before its lease and durable admission. The [native/model score](qualification-score.json) separately verified placement, one SQLite measurement per job, contact resets, exact TCP images and proposal/receipt bindings. + +The original runtime treated one auxiliary health deadline as body loss and cancelled healthy planning. A real-socket delayed response reproduced that false pause. Repeated slow probes also reproduced six concurrent upstream readers, three per path. The repaired link permits one reader per identity/state path, returns a bounded pending response, discards late results and joins its readers before releasing ownership. The runtime shows **Checking body** for an inconclusive auxiliary read, keeps planning available and requires an independent full status check before every command. Failed authoritative proof still pauses progress; identity, source, boot, lease, freshness, unknown-effect and explicit Pause guards remain in force. Body, client and core policy files were unchanged. + +The [diagnosis](diagnosis.json) distinguishes that reproduced mechanism from the earlier incident's unexplained physical scheduling delay. Thirty-two warm sequential pinned reads all succeeded; direct state median was 92.1 ms and linked state median 94.3 ms, with no maximum above 136.7 ms. An isolated scan of 83 copied command records took a median 20.8 ms. Those measurements show real overhead but do not establish why the historical reply crossed its deadline. + +| Product check | Retained evidence | +| --- | --- | +| Healthy planning under delayed health | Browser showed Checking body while the active AI continued planning; 173 healthy replies received 1.2-second injected delays | +| Successive jobs | Health inspection 01 used 14 decisions; Health inspection 02 used 13; both completed measurement and contact reset | +| Lost command reply | The command became uncertain and blocked successors; disconnect, reconnect and receipt recovery kept the count at nineteen | +| Recovery | Check receipt read the exact retained outcome without resending; the agent remained paused until explicit Resume obtained fresh evidence | +| Reload | Both completed jobs and all 83 command records remained; no command was added | +| Cleanup | All four owned trial processes exited zero; the relay joined workers after 685 requests, with peak concurrency two; the link retained zero live health readers | + +The trial used both delayed paths, then disabled injection for the explicit receipt recovery sequence, then delayed state reads and finally identity reads during the second job. The supplemental [exposure audit](review-audit.json) correlates delayed replies with actual model intervals, checks both paths during both jobs and strengthens proof matching. No naturally slow upstream reply of at least 0.8 seconds occurred in this trial. Artificial delay establishes tolerance to the tested condition, not the cause of the original incident. + +[Conformance](conformance.json) passed 117 distinct checks: 26 runtime socket/native cases, 84 core/network/link/HTTP checks and seven harness/scorer cases. Ruff, JavaScript syntax, notebook and whitespace checks passed. The [attempt ledger](attempt-ledger.json) retains the original failing runtime and backlog checks, the intermediate boot-classification failure, and the unretained setup-only interpreter-path error. No measured source changed during the actual model run. + +Scoring occurred after the trial stopped and before user handoff. The saved final public state ends at event 284. Restarting the retained roots for handoff advanced the body to boot two and added administrative restart/outage halt events while the host was already paused. These later events remain in the live journal and are distinguished in the supplemental audit; the saved scores describe the stopped trial. Raw prompts, model output and full relay traces remain in ignored storage at `tools/physical-workcell/output/sim12-browser-health-v1/`. + +The [live workbench](http://127.0.0.1:51377/) is connected and paused, with both results retained and no active job, model request or uncertain command. [Handoff metadata](handoff.json) records the owned body, persistent relay and workbench PIDs, roots and identities. Delay injection is disabled. The handoff relay has bounded workers and waits for explicit shutdown; it does not inherit the qualification harness's 45-minute trial deadline. + +Scope remains same-Mac loopback simulation. Physical hardware, sudden process death during persistence and authenticated cross-machine access remain unqualified. Reproduce with a new trial root using the [runbook](../../../tools/physical-workcell/NETWORK_WORKBENCH.md) and [preregistered experiment](../../docs/experiments/SIM12-workbench-health.md); score after stopping and before restarting that root for handoff. + +![AI planning continues while health is unconfirmed](ui/healthy_checking.png) + +![Exact receipt recovery leaves the agent paused](ui/recovered.png) diff --git a/jevdual/results/workbench-health-20261012/review-audit-unscoped.json b/jevdual/results/workbench-health-20261012/review-audit-unscoped.json new file mode 100644 index 0000000..2fb8763 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/review-audit-unscoped.json @@ -0,0 +1,3340 @@ +{ + "audit_kind": "supplemental_post_run_read_only_review", + "audit_source_sha256": "a38d99756a83f0fd9ea2ed136eec2c8e04b80ffd50be4af569ae835ccbdb588b", + "both_jobs_both_paths_overlap_actual_model": true, + "cancelled_calls": [], + "control_events": [ + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 66, + "time": 1791832016.9072719 + }, + { + "intent": "", + "kind": "disconnect", + "outcome": "Transport only; no body reset or replay", + "seq": 67, + "time": 1791832033.026589 + }, + { + "intent": "", + "kind": "reconnect", + "outcome": "No command sent or replayed", + "seq": 68, + "time": 1791832051.478241 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 71, + "time": 1791832069.452853 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "seq": 285, + "time": 1791832363.512448 + } + ], + "frozen_source_commit": "37ebfdb3054b977149d7b60b46254ddd852b749a", + "frozen_source_unchanged": true, + "healthy_checking_same_application_and_body": true, + "healthy_delayed_reads": [ + { + "delay_finished_wall": 1791831951.136652, + "delay_started_wall": 1791831949.9326327, + "genuine_upstream_elapsed_s": 0.002776124980300665, + "injected_elapsed_s": 1.2040192501153797, + "model_overlaps": [], + "path": "/identity", + "sequence": 2, + "wall_monotonic_offset_range_s": 0.00032067298889160156 + }, + { + "delay_finished_wall": 1791831952.751093, + "delay_started_wall": 1791831951.5486119, + "genuine_upstream_elapsed_s": 0.005044083809480071, + "injected_elapsed_s": 1.2024811250157654, + "model_overlaps": [], + "path": "/identity", + "sequence": 3, + "wall_monotonic_offset_range_s": 0.0005669593811035156 + }, + { + "delay_finished_wall": 1791831954.359777, + "delay_started_wall": 1791831953.1558466, + "genuine_upstream_elapsed_s": 0.0035592911299318075, + "injected_elapsed_s": 1.2039304592180997, + "model_overlaps": [], + "path": "/identity", + "sequence": 4, + "wall_monotonic_offset_range_s": 0.0003485679626464844 + }, + { + "delay_finished_wall": 1791831955.967743, + "delay_started_wall": 1791831954.7657344, + "genuine_upstream_elapsed_s": 0.0029993748757988214, + "injected_elapsed_s": 1.2020084999967366, + "model_overlaps": [], + "path": "/identity", + "sequence": 5, + "wall_monotonic_offset_range_s": 0.00022411346435546875 + }, + { + "delay_finished_wall": 1791831957.5783129, + "delay_started_wall": 1791831956.373279, + "genuine_upstream_elapsed_s": 0.0033572500105947256, + "injected_elapsed_s": 1.2050337500404567, + "model_overlaps": [], + "path": "/identity", + "sequence": 6, + "wall_monotonic_offset_range_s": 0.00037097930908203125 + }, + { + "delay_finished_wall": 1791831959.320833, + "delay_started_wall": 1791831958.1170774, + "genuine_upstream_elapsed_s": 0.09075575019232929, + "injected_elapsed_s": 1.203755582915619, + "model_overlaps": [], + "path": "/identity", + "sequence": 7, + "wall_monotonic_offset_range_s": 0.0005834102630615234 + }, + { + "delay_finished_wall": 1791831960.801874, + "delay_started_wall": 1791831959.59682, + "genuine_upstream_elapsed_s": 0.0031042909249663353, + "injected_elapsed_s": 1.2050538749899715, + "model_overlaps": [], + "path": "/identity", + "sequence": 8, + "wall_monotonic_offset_range_s": 0.0002377033233642578 + }, + { + "delay_finished_wall": 1791831962.408829, + "delay_started_wall": 1791831961.2049468, + "genuine_upstream_elapsed_s": 0.0031348341144621372, + "injected_elapsed_s": 1.203882124973461, + "model_overlaps": [], + "path": "/identity", + "sequence": 9, + "wall_monotonic_offset_range_s": 0.0003495216369628906 + }, + { + "delay_finished_wall": 1791831964.0189052, + "delay_started_wall": 1791831962.8146133, + "genuine_upstream_elapsed_s": 0.005035291891545057, + "injected_elapsed_s": 1.204291749978438, + "model_overlaps": [], + "path": "/identity", + "sequence": 10, + "wall_monotonic_offset_range_s": 0.0006890296936035156 + }, + { + "delay_finished_wall": 1791831965.624738, + "delay_started_wall": 1791831964.421972, + "genuine_upstream_elapsed_s": 0.002828457858413458, + "injected_elapsed_s": 1.2027659590821713, + "model_overlaps": [], + "path": "/identity", + "sequence": 11, + "wall_monotonic_offset_range_s": 0.00026869773864746094 + }, + { + "delay_finished_wall": 1791831967.239062, + "delay_started_wall": 1791831966.0340257, + "genuine_upstream_elapsed_s": 0.003178749931976199, + "injected_elapsed_s": 1.2050365000031888, + "model_overlaps": [], + "path": "/identity", + "sequence": 12, + "wall_monotonic_offset_range_s": 0.0002815723419189453 + }, + { + "delay_finished_wall": 1791831968.8454201, + "delay_started_wall": 1791831967.643657, + "genuine_upstream_elapsed_s": 0.00289629097096622, + "injected_elapsed_s": 1.201763124903664, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2017631530761719 + } + ], + "path": "/identity", + "sequence": 22, + "wall_monotonic_offset_range_s": 0.00020837783813476562 + }, + { + "delay_finished_wall": 1791831970.458034, + "delay_started_wall": 1791831969.252459, + "genuine_upstream_elapsed_s": 0.002508667064830661, + "injected_elapsed_s": 1.2055749581195414, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2055749893188477 + } + ], + "path": "/identity", + "sequence": 23, + "wall_monotonic_offset_range_s": 0.0002646446228027344 + }, + { + "delay_finished_wall": 1791831972.06533, + "delay_started_wall": 1791831970.8602543, + "genuine_upstream_elapsed_s": 0.003260375000536442, + "injected_elapsed_s": 1.205075625097379, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.205075740814209 + } + ], + "path": "/identity", + "sequence": 24, + "wall_monotonic_offset_range_s": 0.0003116130828857422 + }, + { + "delay_finished_wall": 1791831973.677821, + "delay_started_wall": 1791831972.4733727, + "genuine_upstream_elapsed_s": 0.003892249893397093, + "injected_elapsed_s": 1.2044482911005616, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2044482231140137 + } + ], + "path": "/identity", + "sequence": 25, + "wall_monotonic_offset_range_s": 0.0011014938354492188 + }, + { + "delay_finished_wall": 1791831975.285738, + "delay_started_wall": 1791831974.0807476, + "genuine_upstream_elapsed_s": 0.003343291115015745, + "injected_elapsed_s": 1.204990333179012, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2049903869628906 + } + ], + "path": "/identity", + "sequence": 26, + "wall_monotonic_offset_range_s": 0.00025391578674316406 + }, + { + "delay_finished_wall": 1791831976.8951979, + "delay_started_wall": 1791831975.6902578, + "genuine_upstream_elapsed_s": 0.0034893339034169912, + "injected_elapsed_s": 1.2049401660915464, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2049400806427002 + } + ], + "path": "/identity", + "sequence": 27, + "wall_monotonic_offset_range_s": 0.0002818107604980469 + }, + { + "delay_finished_wall": 1791831978.499134, + "delay_started_wall": 1791831977.2945046, + "genuine_upstream_elapsed_s": 0.003361874958500266, + "injected_elapsed_s": 1.204629332991317, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.7861673831939697 + } + ], + "path": "/identity", + "sequence": 28, + "wall_monotonic_offset_range_s": 0.0003018379211425781 + }, + { + "delay_finished_wall": 1791831980.10637, + "delay_started_wall": 1791831978.9023626, + "genuine_upstream_elapsed_s": 0.00299987499602139, + "injected_elapsed_s": 1.2040073750540614, + "model_overlaps": [ + { + "call": 2, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2040073871612549 + } + ], + "path": "/identity", + "sequence": 42, + "wall_monotonic_offset_range_s": 0.0002627372741699219 + }, + { + "delay_finished_wall": 1791831981.715692, + "delay_started_wall": 1791831980.5121903, + "genuine_upstream_elapsed_s": 0.0031940829940140247, + "injected_elapsed_s": 1.2035016249865294, + "model_overlaps": [ + { + "call": 2, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2035017013549805 + } + ], + "path": "/identity", + "sequence": 43, + "wall_monotonic_offset_range_s": 0.0002980232238769531 + }, + { + "delay_finished_wall": 1791831983.322252, + "delay_started_wall": 1791831982.121043, + "genuine_upstream_elapsed_s": 0.004062541993334889, + "injected_elapsed_s": 1.2012091248761863, + "model_overlaps": [ + { + "call": 2, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2012090682983398 + } + ], + "path": "/identity", + "sequence": 44, + "wall_monotonic_offset_range_s": 0.00027871131896972656 + }, + { + "delay_finished_wall": 1791831984.93428, + "delay_started_wall": 1791831983.73001, + "genuine_upstream_elapsed_s": 0.002662374870851636, + "injected_elapsed_s": 1.2042698750738055, + "model_overlaps": [ + { + "call": 2, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.0789592266082764 + } + ], + "path": "/identity", + "sequence": 45, + "wall_monotonic_offset_range_s": 0.00023412704467773438 + }, + { + "delay_finished_wall": 1791831986.539777, + "delay_started_wall": 1791831985.338916, + "genuine_upstream_elapsed_s": 0.0025203339755535126, + "injected_elapsed_s": 1.2008610831107944, + "model_overlaps": [ + { + "call": 3, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2008609771728516 + } + ], + "path": "/identity", + "sequence": 59, + "wall_monotonic_offset_range_s": 0.00014352798461914062 + }, + { + "delay_finished_wall": 1791831988.159762, + "delay_started_wall": 1791831986.9584835, + "genuine_upstream_elapsed_s": 0.009544290835037827, + "injected_elapsed_s": 1.2012784169055521, + "model_overlaps": [ + { + "call": 3, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2012784481048584 + } + ], + "path": "/identity", + "sequence": 60, + "wall_monotonic_offset_range_s": 0.00033092498779296875 + }, + { + "delay_finished_wall": 1791831989.7679, + "delay_started_wall": 1791831988.5628166, + "genuine_upstream_elapsed_s": 0.0035029591526836157, + "injected_elapsed_s": 1.2050834170076996, + "model_overlaps": [ + { + "call": 3, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2050833702087402 + } + ], + "path": "/identity", + "sequence": 61, + "wall_monotonic_offset_range_s": 0.00027179718017578125 + }, + { + "delay_finished_wall": 1791831991.377156, + "delay_started_wall": 1791831990.1721072, + "genuine_upstream_elapsed_s": 0.0033430408220738173, + "injected_elapsed_s": 1.2050489170942456, + "model_overlaps": [ + { + "call": 3, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2050487995147705 + } + ], + "path": "/identity", + "sequence": 62, + "wall_monotonic_offset_range_s": 0.0002346038818359375 + }, + { + "delay_finished_wall": 1791831992.996601, + "delay_started_wall": 1791831991.7964127, + "genuine_upstream_elapsed_s": 0.004118707962334156, + "injected_elapsed_s": 1.200188459130004, + "model_overlaps": [ + { + "call": 3, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.200188398361206 + } + ], + "path": "/identity", + "sequence": 63, + "wall_monotonic_offset_range_s": 0.005568981170654297 + }, + { + "delay_finished_wall": 1791831994.607224, + "delay_started_wall": 1791831993.4021256, + "genuine_upstream_elapsed_s": 0.007497084094211459, + "injected_elapsed_s": 1.2050984590314329, + "model_overlaps": [ + { + "call": 3, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2050983905792236 + } + ], + "path": "/identity", + "sequence": 64, + "wall_monotonic_offset_range_s": 0.0003578662872314453 + }, + { + "delay_finished_wall": 1791831997.214113, + "delay_started_wall": 1791831996.0115426, + "genuine_upstream_elapsed_s": 0.0029468329157680273, + "injected_elapsed_s": 1.2025703340768814, + "model_overlaps": [ + { + "call": 4, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2025704383850098 + } + ], + "path": "/identity", + "sequence": 78, + "wall_monotonic_offset_range_s": 0.00033402442932128906 + }, + { + "delay_finished_wall": 1791831998.838238, + "delay_started_wall": 1791831997.6335766, + "genuine_upstream_elapsed_s": 0.009623541962355375, + "injected_elapsed_s": 1.2046614589635283, + "model_overlaps": [ + { + "call": 4, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2046613693237305 + } + ], + "path": "/identity", + "sequence": 79, + "wall_monotonic_offset_range_s": 0.0003039836883544922 + }, + { + "delay_finished_wall": 1791832000.441949, + "delay_started_wall": 1791831999.2368922, + "genuine_upstream_elapsed_s": 0.003390332916751504, + "injected_elapsed_s": 1.2050566668622196, + "model_overlaps": [ + { + "call": 4, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2050566673278809 + } + ], + "path": "/identity", + "sequence": 80, + "wall_monotonic_offset_range_s": 0.00024771690368652344 + }, + { + "delay_finished_wall": 1791832002.051035, + "delay_started_wall": 1791832000.8462179, + "genuine_upstream_elapsed_s": 0.00339320907369256, + "injected_elapsed_s": 1.2048170422203839, + "model_overlaps": [ + { + "call": 4, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.8470175266265869 + } + ], + "path": "/identity", + "sequence": 81, + "wall_monotonic_offset_range_s": 0.0002200603485107422 + }, + { + "delay_finished_wall": 1791832003.6653, + "delay_started_wall": 1791832002.4623945, + "genuine_upstream_elapsed_s": 0.012598375091329217, + "injected_elapsed_s": 1.2029053340665996, + "model_overlaps": [ + { + "call": 5, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.1388180255889893 + } + ], + "path": "/identity", + "sequence": 96, + "wall_monotonic_offset_range_s": 0.00030541419982910156 + }, + { + "delay_finished_wall": 1791832005.285439, + "delay_started_wall": 1791832004.0819662, + "genuine_upstream_elapsed_s": 0.01664000004529953, + "injected_elapsed_s": 1.2034727919381112, + "model_overlaps": [ + { + "call": 5, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2034728527069092 + } + ], + "path": "/identity", + "sequence": 97, + "wall_monotonic_offset_range_s": 0.0003800392150878906 + }, + { + "delay_finished_wall": 1791832006.90365, + "delay_started_wall": 1791832005.698815, + "genuine_upstream_elapsed_s": 0.019909291993826628, + "injected_elapsed_s": 1.2048349999822676, + "model_overlaps": [ + { + "call": 5, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2048349380493164 + } + ], + "path": "/identity", + "sequence": 98, + "wall_monotonic_offset_range_s": 0.0004360675811767578 + }, + { + "delay_finished_wall": 1791832008.506123, + "delay_started_wall": 1791832007.3038552, + "genuine_upstream_elapsed_s": 0.016452833777293563, + "injected_elapsed_s": 1.2022679159417748, + "model_overlaps": [ + { + "call": 6, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.40236496925354004 + } + ], + "path": "/identity", + "sequence": 99, + "wall_monotonic_offset_range_s": 0.0002734661102294922 + }, + { + "delay_finished_wall": 1791832010.115941, + "delay_started_wall": 1791832008.9121234, + "genuine_upstream_elapsed_s": 0.013531167060136795, + "injected_elapsed_s": 1.2038175829220563, + "model_overlaps": [ + { + "call": 6, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.20381760597229 + } + ], + "path": "/identity", + "sequence": 113, + "wall_monotonic_offset_range_s": 0.0002827644348144531 + }, + { + "delay_finished_wall": 1791832011.727929, + "delay_started_wall": 1791832010.5238676, + "genuine_upstream_elapsed_s": 0.013567666988819838, + "injected_elapsed_s": 1.2040615831501782, + "model_overlaps": [ + { + "call": 6, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.204061508178711 + } + ], + "path": "/identity", + "sequence": 114, + "wall_monotonic_offset_range_s": 0.0003399848937988281 + }, + { + "delay_finished_wall": 1791832013.34195, + "delay_started_wall": 1791832012.1412833, + "genuine_upstream_elapsed_s": 0.021057708887383342, + "injected_elapsed_s": 1.2006667500827461, + "model_overlaps": [ + { + "call": 6, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2006666660308838 + } + ], + "path": "/identity", + "sequence": 115, + "wall_monotonic_offset_range_s": 0.000274658203125 + }, + { + "delay_finished_wall": 1791832014.943507, + "delay_started_wall": 1791832013.7429645, + "genuine_upstream_elapsed_s": 0.014461665879935026, + "injected_elapsed_s": 1.2005423330701888, + "model_overlaps": [ + { + "call": 6, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2005424499511719 + } + ], + "path": "/identity", + "sequence": 116, + "wall_monotonic_offset_range_s": 0.00047659873962402344 + }, + { + "delay_finished_wall": 1791832016.56296, + "delay_started_wall": 1791832015.3582413, + "genuine_upstream_elapsed_s": 0.017344833118841052, + "injected_elapsed_s": 1.204718666849658, + "model_overlaps": [ + { + "call": 6, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.8864147663116455 + } + ], + "path": "/identity", + "sequence": 117, + "wall_monotonic_offset_range_s": 0.0005025863647460938 + }, + { + "delay_finished_wall": 1791832018.174688, + "delay_started_wall": 1791832016.970701, + "genuine_upstream_elapsed_s": 0.019155291141942143, + "injected_elapsed_s": 1.2039870421867818, + "model_overlaps": [], + "path": "/identity", + "sequence": 126, + "wall_monotonic_offset_range_s": 0.00030732154846191406 + }, + { + "delay_finished_wall": 1791832019.776822, + "delay_started_wall": 1791832018.5753713, + "genuine_upstream_elapsed_s": 0.012994375079870224, + "injected_elapsed_s": 1.2014507499989122, + "model_overlaps": [], + "path": "/identity", + "sequence": 127, + "wall_monotonic_offset_range_s": 0.00035262107849121094 + }, + { + "delay_finished_wall": 1791832021.3976462, + "delay_started_wall": 1791832020.1962073, + "genuine_upstream_elapsed_s": 0.02700358279980719, + "injected_elapsed_s": 1.2014389999676496, + "model_overlaps": [], + "path": "/identity", + "sequence": 128, + "wall_monotonic_offset_range_s": 0.0003247261047363281 + }, + { + "delay_finished_wall": 1791832022.9915159, + "delay_started_wall": 1791832021.78787, + "genuine_upstream_elapsed_s": 0.01330141699872911, + "injected_elapsed_s": 1.2036458330694586, + "model_overlaps": [], + "path": "/identity", + "sequence": 129, + "wall_monotonic_offset_range_s": 0.0003898143768310547 + }, + { + "delay_finished_wall": 1791832024.675057, + "delay_started_wall": 1791832023.4683318, + "genuine_upstream_elapsed_s": 0.024299542186781764, + "injected_elapsed_s": 1.2067252080887556, + "model_overlaps": [], + "path": "/identity", + "sequence": 130, + "wall_monotonic_offset_range_s": 0.00021409988403320312 + }, + { + "delay_finished_wall": 1791832026.3910022, + "delay_started_wall": 1791832025.181681, + "genuine_upstream_elapsed_s": 0.033242583042010665, + "injected_elapsed_s": 1.2093212499748915, + "model_overlaps": [], + "path": "/identity", + "sequence": 131, + "wall_monotonic_offset_range_s": 0.0005581378936767578 + }, + { + "delay_finished_wall": 1791832027.847797, + "delay_started_wall": 1791832026.6440387, + "genuine_upstream_elapsed_s": 0.0233499170280993, + "injected_elapsed_s": 1.2037583331111819, + "model_overlaps": [], + "path": "/identity", + "sequence": 132, + "wall_monotonic_offset_range_s": 0.0006263256072998047 + }, + { + "delay_finished_wall": 1791832029.485719, + "delay_started_wall": 1791832028.2835376, + "genuine_upstream_elapsed_s": 0.023109209025278687, + "injected_elapsed_s": 1.2021812500897795, + "model_overlaps": [], + "path": "/identity", + "sequence": 133, + "wall_monotonic_offset_range_s": 0.0005357265472412109 + }, + { + "delay_finished_wall": 1791832081.0974221, + "delay_started_wall": 1791832079.8953402, + "genuine_upstream_elapsed_s": 0.04533958388492465, + "injected_elapsed_s": 1.2020818751771003, + "model_overlaps": [ + { + "call": 8, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2020819187164307 + } + ], + "path": "/state", + "sequence": 211, + "wall_monotonic_offset_range_s": 0.00023651123046875 + }, + { + "delay_finished_wall": 1791832082.7192068, + "delay_started_wall": 1791832081.518623, + "genuine_upstream_elapsed_s": 0.04676191695034504, + "injected_elapsed_s": 1.2005837499164045, + "model_overlaps": [ + { + "call": 8, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2005836963653564 + } + ], + "path": "/state", + "sequence": 213, + "wall_monotonic_offset_range_s": 0.0003616809844970703 + }, + { + "delay_finished_wall": 1791832084.339633, + "delay_started_wall": 1791832083.1349635, + "genuine_upstream_elapsed_s": 0.04107379191555083, + "injected_elapsed_s": 1.2046695840544999, + "model_overlaps": [ + { + "call": 8, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.20466947555542 + } + ], + "path": "/state", + "sequence": 215, + "wall_monotonic_offset_range_s": 0.0002899169921875 + }, + { + "delay_finished_wall": 1791832085.960802, + "delay_started_wall": 1791832084.7594473, + "genuine_upstream_elapsed_s": 0.04071483411826193, + "injected_elapsed_s": 1.2013546251691878, + "model_overlaps": [ + { + "call": 8, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.201354742050171 + } + ], + "path": "/state", + "sequence": 217, + "wall_monotonic_offset_range_s": 0.00032806396484375 + }, + { + "delay_finished_wall": 1791832087.585452, + "delay_started_wall": 1791832086.384062, + "genuine_upstream_elapsed_s": 0.041966750053688884, + "injected_elapsed_s": 1.2013900419697165, + "model_overlaps": [ + { + "call": 8, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.9910397529602051 + } + ], + "path": "/state", + "sequence": 219, + "wall_monotonic_offset_range_s": 0.00026154518127441406 + }, + { + "delay_finished_wall": 1791832090.22193, + "delay_started_wall": 1791832089.0204175, + "genuine_upstream_elapsed_s": 0.04945133300498128, + "injected_elapsed_s": 1.2015124999452382, + "model_overlaps": [ + { + "call": 9, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2015125751495361 + } + ], + "path": "/state", + "sequence": 234, + "wall_monotonic_offset_range_s": 0.0002675056457519531 + }, + { + "delay_finished_wall": 1791832091.845014, + "delay_started_wall": 1791832090.6439574, + "genuine_upstream_elapsed_s": 0.04513141606003046, + "injected_elapsed_s": 1.2010567500256002, + "model_overlaps": [ + { + "call": 9, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.201056718826294 + } + ], + "path": "/state", + "sequence": 236, + "wall_monotonic_offset_range_s": 0.0003001689910888672 + }, + { + "delay_finished_wall": 1791832093.470641, + "delay_started_wall": 1791832092.269904, + "genuine_upstream_elapsed_s": 0.046239792136475444, + "injected_elapsed_s": 1.2007370002102107, + "model_overlaps": [ + { + "call": 9, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2007369995117188 + } + ], + "path": "/state", + "sequence": 238, + "wall_monotonic_offset_range_s": 0.0003094673156738281 + }, + { + "delay_finished_wall": 1791832095.102564, + "delay_started_wall": 1791832093.902203, + "genuine_upstream_elapsed_s": 0.05429583298973739, + "injected_elapsed_s": 1.2003611251711845, + "model_overlaps": [ + { + "call": 9, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2003610134124756 + } + ], + "path": "/state", + "sequence": 240, + "wall_monotonic_offset_range_s": 0.0003120899200439453 + }, + { + "delay_finished_wall": 1791832096.736833, + "delay_started_wall": 1791832095.5317907, + "genuine_upstream_elapsed_s": 0.04574579210020602, + "injected_elapsed_s": 1.20504245813936, + "model_overlaps": [ + { + "call": 10, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.24535512924194336 + } + ], + "path": "/state", + "sequence": 242, + "wall_monotonic_offset_range_s": 0.004605531692504883 + }, + { + "delay_finished_wall": 1791832098.352134, + "delay_started_wall": 1791832097.1493397, + "genuine_upstream_elapsed_s": 0.04627279192209244, + "injected_elapsed_s": 1.202794292010367, + "model_overlaps": [ + { + "call": 10, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2027943134307861 + } + ], + "path": "/state", + "sequence": 257, + "wall_monotonic_offset_range_s": 0.0003185272216796875 + }, + { + "delay_finished_wall": 1791832099.991491, + "delay_started_wall": 1791832098.7868052, + "genuine_upstream_elapsed_s": 0.04723550006747246, + "injected_elapsed_s": 1.2046860409900546, + "model_overlaps": [ + { + "call": 10, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.204685926437378 + } + ], + "path": "/state", + "sequence": 259, + "wall_monotonic_offset_range_s": 0.000301361083984375 + }, + { + "delay_finished_wall": 1791832101.614825, + "delay_started_wall": 1791832100.4146428, + "genuine_upstream_elapsed_s": 0.047370166052132845, + "injected_elapsed_s": 1.2001820839941502, + "model_overlaps": [ + { + "call": 10, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2001821994781494 + } + ], + "path": "/state", + "sequence": 261, + "wall_monotonic_offset_range_s": 0.0003604888916015625 + }, + { + "delay_finished_wall": 1791832103.239155, + "delay_started_wall": 1791832102.0341103, + "genuine_upstream_elapsed_s": 0.04227699991315603, + "injected_elapsed_s": 1.2050447911024094, + "model_overlaps": [ + { + "call": 10, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2050447463989258 + } + ], + "path": "/state", + "sequence": 263, + "wall_monotonic_offset_range_s": 0.0004055500030517578 + }, + { + "delay_finished_wall": 1791832104.877825, + "delay_started_wall": 1791832103.6765485, + "genuine_upstream_elapsed_s": 0.056063625030219555, + "injected_elapsed_s": 1.2012766250409186, + "model_overlaps": [ + { + "call": 10, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.19235777854919434 + }, + { + "call": 11, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.09160304069519043 + } + ], + "path": "/state", + "sequence": 265, + "wall_monotonic_offset_range_s": 0.0004611015319824219 + }, + { + "delay_finished_wall": 1791832106.4973052, + "delay_started_wall": 1791832105.292261, + "genuine_upstream_elapsed_s": 0.046128083020448685, + "injected_elapsed_s": 1.2050441671162844, + "model_overlaps": [ + { + "call": 11, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2050442695617676 + } + ], + "path": "/state", + "sequence": 280, + "wall_monotonic_offset_range_s": 0.0004367828369140625 + }, + { + "delay_finished_wall": 1791832108.148909, + "delay_started_wall": 1791832106.9444427, + "genuine_upstream_elapsed_s": 0.0588835421949625, + "injected_elapsed_s": 1.2044663750566542, + "model_overlaps": [ + { + "call": 11, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2044663429260254 + } + ], + "path": "/state", + "sequence": 282, + "wall_monotonic_offset_range_s": 0.0002453327178955078 + }, + { + "delay_finished_wall": 1791832109.773719, + "delay_started_wall": 1791832108.572417, + "genuine_upstream_elapsed_s": 0.050575749948620796, + "injected_elapsed_s": 1.2013020410668105, + "model_overlaps": [ + { + "call": 11, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2013020515441895 + } + ], + "path": "/state", + "sequence": 284, + "wall_monotonic_offset_range_s": 0.00033092498779296875 + }, + { + "delay_finished_wall": 1791832111.400123, + "delay_started_wall": 1791832110.1959536, + "genuine_upstream_elapsed_s": 0.046160792000591755, + "injected_elapsed_s": 1.204169207951054, + "model_overlaps": [ + { + "call": 11, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2041692733764648 + } + ], + "path": "/state", + "sequence": 286, + "wall_monotonic_offset_range_s": 0.0002598762512207031 + }, + { + "delay_finished_wall": 1791832113.030656, + "delay_started_wall": 1791832111.8256311, + "genuine_upstream_elapsed_s": 0.044818750116974115, + "injected_elapsed_s": 1.205024957889691, + "model_overlaps": [ + { + "call": 11, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.0970909595489502 + } + ], + "path": "/state", + "sequence": 288, + "wall_monotonic_offset_range_s": 0.0003409385681152344 + }, + { + "delay_finished_wall": 1791832115.661109, + "delay_started_wall": 1791832114.4575708, + "genuine_upstream_elapsed_s": 0.04853254184126854, + "injected_elapsed_s": 1.203538167057559, + "model_overlaps": [ + { + "call": 12, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.203538179397583 + } + ], + "path": "/state", + "sequence": 303, + "wall_monotonic_offset_range_s": 0.0002593994140625 + }, + { + "delay_finished_wall": 1791832117.282847, + "delay_started_wall": 1791832116.0813665, + "genuine_upstream_elapsed_s": 0.04559245798736811, + "injected_elapsed_s": 1.2014803329948336, + "model_overlaps": [ + { + "call": 12, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2014803886413574 + } + ], + "path": "/state", + "sequence": 305, + "wall_monotonic_offset_range_s": 0.00033092498779296875 + }, + { + "delay_finished_wall": 1791832118.914329, + "delay_started_wall": 1791832117.7099512, + "genuine_upstream_elapsed_s": 0.048586250049993396, + "injected_elapsed_s": 1.2043779590167105, + "model_overlaps": [ + { + "call": 12, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2043778896331787 + } + ], + "path": "/state", + "sequence": 307, + "wall_monotonic_offset_range_s": 0.0003142356872558594 + }, + { + "delay_finished_wall": 1791832120.540607, + "delay_started_wall": 1791832119.338083, + "genuine_upstream_elapsed_s": 0.04915620805695653, + "injected_elapsed_s": 1.2025240000803024, + "model_overlaps": [ + { + "call": 12, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.202523946762085 + } + ], + "path": "/state", + "sequence": 309, + "wall_monotonic_offset_range_s": 0.0002961158752441406 + }, + { + "delay_finished_wall": 1791832123.173666, + "delay_started_wall": 1791832121.9724607, + "genuine_upstream_elapsed_s": 0.05501812510192394, + "injected_elapsed_s": 1.2012051669880748, + "model_overlaps": [ + { + "call": 13, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2012052536010742 + } + ], + "path": "/state", + "sequence": 324, + "wall_monotonic_offset_range_s": 0.0004284381866455078 + }, + { + "delay_finished_wall": 1791832124.797261, + "delay_started_wall": 1791832123.5969458, + "genuine_upstream_elapsed_s": 0.05386095796711743, + "injected_elapsed_s": 1.20031529199332, + "model_overlaps": [ + { + "call": 13, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.200315237045288 + } + ], + "path": "/state", + "sequence": 326, + "wall_monotonic_offset_range_s": 0.0003066062927246094 + }, + { + "delay_finished_wall": 1791832126.434151, + "delay_started_wall": 1791832125.228527, + "genuine_upstream_elapsed_s": 0.05417429096996784, + "injected_elapsed_s": 1.2056237920187414, + "model_overlaps": [ + { + "call": 13, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2056238651275635 + } + ], + "path": "/state", + "sequence": 328, + "wall_monotonic_offset_range_s": 0.0003712177276611328 + }, + { + "delay_finished_wall": 1791832128.0681322, + "delay_started_wall": 1791832126.8663008, + "genuine_upstream_elapsed_s": 0.059533082880079746, + "injected_elapsed_s": 1.2018312909640372, + "model_overlaps": [ + { + "call": 13, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.201831340789795 + } + ], + "path": "/state", + "sequence": 330, + "wall_monotonic_offset_range_s": 0.0003476142883300781 + }, + { + "delay_finished_wall": 1791832130.698807, + "delay_started_wall": 1791832129.4979184, + "genuine_upstream_elapsed_s": 0.05425262497738004, + "injected_elapsed_s": 1.2008886248804629, + "model_overlaps": [ + { + "call": 14, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2008886337280273 + } + ], + "path": "/state", + "sequence": 345, + "wall_monotonic_offset_range_s": 0.000308990478515625 + }, + { + "delay_finished_wall": 1791832132.343055, + "delay_started_wall": 1791832131.1425955, + "genuine_upstream_elapsed_s": 0.07181812496855855, + "injected_elapsed_s": 1.2004595410544425, + "model_overlaps": [ + { + "call": 14, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2004594802856445 + } + ], + "path": "/state", + "sequence": 347, + "wall_monotonic_offset_range_s": 0.0002918243408203125 + }, + { + "delay_finished_wall": 1791832133.955904, + "delay_started_wall": 1791832132.7540894, + "genuine_upstream_elapsed_s": 0.052502250066027045, + "injected_elapsed_s": 1.2018145839683712, + "model_overlaps": [ + { + "call": 14, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2018146514892578 + } + ], + "path": "/state", + "sequence": 349, + "wall_monotonic_offset_range_s": 0.0005249977111816406 + }, + { + "delay_finished_wall": 1791832135.582197, + "delay_started_wall": 1791832134.3774855, + "genuine_upstream_elapsed_s": 0.04988845787011087, + "injected_elapsed_s": 1.2047113340813667, + "model_overlaps": [ + { + "call": 14, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2047114372253418 + } + ], + "path": "/state", + "sequence": 351, + "wall_monotonic_offset_range_s": 0.0002865791320800781 + }, + { + "delay_finished_wall": 1791832137.22383, + "delay_started_wall": 1791832136.022428, + "genuine_upstream_elapsed_s": 0.06868283404037356, + "injected_elapsed_s": 1.2014020418282598, + "model_overlaps": [ + { + "call": 14, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.35050058364868164 + } + ], + "path": "/state", + "sequence": 353, + "wall_monotonic_offset_range_s": 0.0003383159637451172 + }, + { + "delay_finished_wall": 1791832138.840893, + "delay_started_wall": 1791832137.6358533, + "genuine_upstream_elapsed_s": 0.05692441714927554, + "injected_elapsed_s": 1.2050397919956595, + "model_overlaps": [ + { + "call": 1, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2050397396087646 + } + ], + "path": "/state", + "sequence": 373, + "wall_monotonic_offset_range_s": 0.00036334991455078125 + }, + { + "delay_finished_wall": 1791832140.523594, + "delay_started_wall": 1791832139.3210244, + "genuine_upstream_elapsed_s": 0.08095504203811288, + "injected_elapsed_s": 1.2025694160256535, + "model_overlaps": [ + { + "call": 1, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2025694847106934 + } + ], + "path": "/state", + "sequence": 375, + "wall_monotonic_offset_range_s": 0.0003018379211425781 + }, + { + "delay_finished_wall": 1791832142.144813, + "delay_started_wall": 1791832140.9398186, + "genuine_upstream_elapsed_s": 0.06230750004760921, + "injected_elapsed_s": 1.2049943341407925, + "model_overlaps": [ + { + "call": 1, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2049944400787354 + } + ], + "path": "/state", + "sequence": 377, + "wall_monotonic_offset_range_s": 0.00025177001953125 + }, + { + "delay_finished_wall": 1791832143.774648, + "delay_started_wall": 1791832142.5730557, + "genuine_upstream_elapsed_s": 0.05781725002452731, + "injected_elapsed_s": 1.2015922078862786, + "model_overlaps": [ + { + "call": 1, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.201592206954956 + } + ], + "path": "/state", + "sequence": 379, + "wall_monotonic_offset_range_s": 0.0005812644958496094 + }, + { + "delay_finished_wall": 1791832145.40432, + "delay_started_wall": 1791832144.1992726, + "genuine_upstream_elapsed_s": 0.05713516683317721, + "injected_elapsed_s": 1.2050473750568926, + "model_overlaps": [ + { + "call": 1, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.205047369003296 + } + ], + "path": "/state", + "sequence": 381, + "wall_monotonic_offset_range_s": 0.0003993511199951172 + }, + { + "delay_finished_wall": 1791832147.056463, + "delay_started_wall": 1791832145.8514771, + "genuine_upstream_elapsed_s": 0.07064691698178649, + "injected_elapsed_s": 1.2049859159160405, + "model_overlaps": [ + { + "call": 1, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 0.6222236156463623 + } + ], + "path": "/state", + "sequence": 383, + "wall_monotonic_offset_range_s": 0.0005612373352050781 + }, + { + "delay_finished_wall": 1791832149.664667, + "delay_started_wall": 1791832148.461329, + "genuine_upstream_elapsed_s": 0.05194795900024474, + "injected_elapsed_s": 1.2033379168715328, + "model_overlaps": [ + { + "call": 2, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2033379077911377 + } + ], + "path": "/state", + "sequence": 398, + "wall_monotonic_offset_range_s": 0.0003561973571777344 + }, + { + "delay_finished_wall": 1791832151.294802, + "delay_started_wall": 1791832150.0908835, + "genuine_upstream_elapsed_s": 0.052277165930718184, + "injected_elapsed_s": 1.2039184591267258, + "model_overlaps": [ + { + "call": 2, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.20391845703125 + } + ], + "path": "/state", + "sequence": 400, + "wall_monotonic_offset_range_s": 0.00032806396484375 + }, + { + "delay_finished_wall": 1791832152.944254, + "delay_started_wall": 1791832151.7392159, + "genuine_upstream_elapsed_s": 0.07039116695523262, + "injected_elapsed_s": 1.2050380420405418, + "model_overlaps": [ + { + "call": 2, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.205038070678711 + } + ], + "path": "/state", + "sequence": 402, + "wall_monotonic_offset_range_s": 0.0005617141723632812 + }, + { + "delay_finished_wall": 1791832154.562625, + "delay_started_wall": 1791832153.3620024, + "genuine_upstream_elapsed_s": 0.060910666128620505, + "injected_elapsed_s": 1.2006225001532584, + "model_overlaps": [ + { + "call": 2, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.20062255859375 + } + ], + "path": "/state", + "sequence": 404, + "wall_monotonic_offset_range_s": 0.0006086826324462891 + }, + { + "delay_finished_wall": 1791832157.187784, + "delay_started_wall": 1791832155.9839876, + "genuine_upstream_elapsed_s": 0.054760915925726295, + "injected_elapsed_s": 1.2037962921895087, + "model_overlaps": [ + { + "call": 3, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.20379638671875 + } + ], + "path": "/state", + "sequence": 419, + "wall_monotonic_offset_range_s": 0.002040386199951172 + }, + { + "delay_finished_wall": 1791832158.8312519, + "delay_started_wall": 1791832157.6299183, + "genuine_upstream_elapsed_s": 0.06451662513427436, + "injected_elapsed_s": 1.2013334999792278, + "model_overlaps": [ + { + "call": 3, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2013335227966309 + } + ], + "path": "/state", + "sequence": 421, + "wall_monotonic_offset_range_s": 0.0003459453582763672 + }, + { + "delay_finished_wall": 1791832160.4510279, + "delay_started_wall": 1791832159.2491255, + "genuine_upstream_elapsed_s": 0.05649487487971783, + "injected_elapsed_s": 1.2019024998880923, + "model_overlaps": [ + { + "call": 3, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2019023895263672 + } + ], + "path": "/state", + "sequence": 423, + "wall_monotonic_offset_range_s": 0.0005939006805419922 + }, + { + "delay_finished_wall": 1791832162.102963, + "delay_started_wall": 1791832160.9011786, + "genuine_upstream_elapsed_s": 0.06587324989959598, + "injected_elapsed_s": 1.2017843751236796, + "model_overlaps": [ + { + "call": 3, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.201784372329712 + } + ], + "path": "/state", + "sequence": 425, + "wall_monotonic_offset_range_s": 0.0006198883056640625 + }, + { + "delay_finished_wall": 1791832163.716691, + "delay_started_wall": 1791832162.5151398, + "genuine_upstream_elapsed_s": 0.05449808295816183, + "injected_elapsed_s": 1.2015511670615524, + "model_overlaps": [ + { + "call": 3, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2015511989593506 + } + ], + "path": "/state", + "sequence": 427, + "wall_monotonic_offset_range_s": 0.0002949237823486328 + }, + { + "delay_finished_wall": 1791832165.361527, + "delay_started_wall": 1791832164.1579523, + "genuine_upstream_elapsed_s": 0.07102287514135242, + "injected_elapsed_s": 1.203574666986242, + "model_overlaps": [ + { + "call": 3, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 0.15041804313659668 + } + ], + "path": "/state", + "sequence": 429, + "wall_monotonic_offset_range_s": 0.0006797313690185547 + }, + { + "delay_finished_wall": 1791832168.0370932, + "delay_started_wall": 1791832166.8349886, + "genuine_upstream_elapsed_s": 0.09905841713771224, + "injected_elapsed_s": 1.2021046250592917, + "model_overlaps": [ + { + "call": 4, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2021045684814453 + } + ], + "path": "/state", + "sequence": 445, + "wall_monotonic_offset_range_s": 0.0002818107604980469 + }, + { + "delay_finished_wall": 1791832169.681438, + "delay_started_wall": 1791832168.4777927, + "genuine_upstream_elapsed_s": 0.09563441714271903, + "injected_elapsed_s": 1.203645166940987, + "model_overlaps": [ + { + "call": 4, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2036452293395996 + } + ], + "path": "/state", + "sequence": 447, + "wall_monotonic_offset_range_s": 0.0003516674041748047 + }, + { + "delay_finished_wall": 1791832171.319584, + "delay_started_wall": 1791832170.1186836, + "genuine_upstream_elapsed_s": 0.09399583307094872, + "injected_elapsed_s": 1.2009002920240164, + "model_overlaps": [ + { + "call": 4, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2009003162384033 + } + ], + "path": "/state", + "sequence": 449, + "wall_monotonic_offset_range_s": 0.0005812644958496094 + }, + { + "delay_finished_wall": 1791832172.953556, + "delay_started_wall": 1791832171.753168, + "genuine_upstream_elapsed_s": 0.08927262504585087, + "injected_elapsed_s": 1.200387875083834, + "model_overlaps": [ + { + "call": 4, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 0.32279539108276367 + } + ], + "path": "/state", + "sequence": 451, + "wall_monotonic_offset_range_s": 0.0003695487976074219 + }, + { + "delay_finished_wall": 1791832175.5946329, + "delay_started_wall": 1791832174.3903122, + "genuine_upstream_elapsed_s": 0.08938425011001527, + "injected_elapsed_s": 1.2043205830268562, + "model_overlaps": [ + { + "call": 5, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2043206691741943 + } + ], + "path": "/state", + "sequence": 466, + "wall_monotonic_offset_range_s": 0.00030350685119628906 + }, + { + "delay_finished_wall": 1791832177.2314198, + "delay_started_wall": 1791832176.030892, + "genuine_upstream_elapsed_s": 0.08454362489283085, + "injected_elapsed_s": 1.2005280000157654, + "model_overlaps": [ + { + "call": 5, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2005279064178467 + } + ], + "path": "/state", + "sequence": 468, + "wall_monotonic_offset_range_s": 0.0006012916564941406 + }, + { + "delay_finished_wall": 1791832178.8902102, + "delay_started_wall": 1791832177.6890914, + "genuine_upstream_elapsed_s": 0.10279183299280703, + "injected_elapsed_s": 1.2011187500320375, + "model_overlaps": [ + { + "call": 5, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2011187076568604 + } + ], + "path": "/state", + "sequence": 470, + "wall_monotonic_offset_range_s": 0.0003154277801513672 + }, + { + "delay_finished_wall": 1791832180.523807, + "delay_started_wall": 1791832179.3190742, + "genuine_upstream_elapsed_s": 0.09166183299385011, + "injected_elapsed_s": 1.2047327919863164, + "model_overlaps": [ + { + "call": 5, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.204732894897461 + } + ], + "path": "/state", + "sequence": 472, + "wall_monotonic_offset_range_s": 0.0003097057342529297 + }, + { + "delay_finished_wall": 1791832182.173342, + "delay_started_wall": 1791832180.9683156, + "genuine_upstream_elapsed_s": 0.09823537501506507, + "injected_elapsed_s": 1.205026458017528, + "model_overlaps": [], + "path": "/state", + "sequence": 474, + "wall_monotonic_offset_range_s": 0.0003254413604736328 + }, + { + "delay_finished_wall": 1791832184.845115, + "delay_started_wall": 1791832183.6415613, + "genuine_upstream_elapsed_s": 0.1093828750308603, + "injected_elapsed_s": 1.2035536249168217, + "model_overlaps": [ + { + "call": 6, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2035536766052246 + } + ], + "path": "/state", + "sequence": 489, + "wall_monotonic_offset_range_s": 0.0005691051483154297 + }, + { + "delay_finished_wall": 1791832186.476904, + "delay_started_wall": 1791832185.2725573, + "genuine_upstream_elapsed_s": 0.09748349990695715, + "injected_elapsed_s": 1.204346542013809, + "model_overlaps": [ + { + "call": 6, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2043466567993164 + } + ], + "path": "/state", + "sequence": 491, + "wall_monotonic_offset_range_s": 0.00036978721618652344 + }, + { + "delay_finished_wall": 1791832188.116647, + "delay_started_wall": 1791832186.9127026, + "genuine_upstream_elapsed_s": 0.09679954196326435, + "injected_elapsed_s": 1.2039444581605494, + "model_overlaps": [ + { + "call": 6, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.203944444656372 + } + ], + "path": "/state", + "sequence": 493, + "wall_monotonic_offset_range_s": 0.0013127326965332031 + }, + { + "delay_finished_wall": 1791832189.75743, + "delay_started_wall": 1791832188.5523894, + "genuine_upstream_elapsed_s": 0.0957978330552578, + "injected_elapsed_s": 1.205040582921356, + "model_overlaps": [ + { + "call": 6, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.205040693283081 + } + ], + "path": "/state", + "sequence": 495, + "wall_monotonic_offset_range_s": 0.0003192424774169922 + }, + { + "delay_finished_wall": 1791832191.387458, + "delay_started_wall": 1791832190.1836927, + "genuine_upstream_elapsed_s": 0.08754870807752013, + "injected_elapsed_s": 1.2037652910221368, + "model_overlaps": [ + { + "call": 6, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2037653923034668 + } + ], + "path": "/state", + "sequence": 497, + "wall_monotonic_offset_range_s": 0.00024628639221191406 + }, + { + "delay_finished_wall": 1791832195.1446521, + "delay_started_wall": 1791832193.9439216, + "genuine_upstream_elapsed_s": 0.19697441696189344, + "injected_elapsed_s": 1.2007305829320103, + "model_overlaps": [ + { + "call": 7, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.200730562210083 + } + ], + "path": "/state", + "sequence": 512, + "wall_monotonic_offset_range_s": 0.00048828125 + }, + { + "delay_finished_wall": 1791832196.679007, + "delay_started_wall": 1791832195.4773612, + "genuine_upstream_elapsed_s": 0.09738016710616648, + "injected_elapsed_s": 1.2016459580045193, + "model_overlaps": [ + { + "call": 7, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.201645851135254 + } + ], + "path": "/state", + "sequence": 514, + "wall_monotonic_offset_range_s": 0.000316619873046875 + }, + { + "delay_finished_wall": 1791832198.432412, + "delay_started_wall": 1791832197.22811, + "genuine_upstream_elapsed_s": 0.1660980840679258, + "injected_elapsed_s": 1.2043019169941545, + "model_overlaps": [ + { + "call": 7, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2043018341064453 + } + ], + "path": "/state", + "sequence": 516, + "wall_monotonic_offset_range_s": 0.0003120899200439453 + }, + { + "delay_finished_wall": 1791832200.023287, + "delay_started_wall": 1791832198.821372, + "genuine_upstream_elapsed_s": 0.10865400009788573, + "injected_elapsed_s": 1.2019149158149958, + "model_overlaps": [ + { + "call": 7, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2019150257110596 + } + ], + "path": "/state", + "sequence": 518, + "wall_monotonic_offset_range_s": 0.0007584095001220703 + }, + { + "delay_finished_wall": 1791832201.67074, + "delay_started_wall": 1791832200.4670024, + "genuine_upstream_elapsed_s": 0.11654558405280113, + "injected_elapsed_s": 1.2037374170031399, + "model_overlaps": [ + { + "call": 7, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.0191373825073242 + } + ], + "path": "/state", + "sequence": 520, + "wall_monotonic_offset_range_s": 0.00032782554626464844 + }, + { + "delay_finished_wall": 1791832204.3241541, + "delay_started_wall": 1791832203.1212535, + "genuine_upstream_elapsed_s": 0.11968966713175178, + "injected_elapsed_s": 1.2029006250668317, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2029006481170654 + } + ], + "path": "/state", + "sequence": 535, + "wall_monotonic_offset_range_s": 0.0002541542053222656 + }, + { + "delay_finished_wall": 1791832205.943559, + "delay_started_wall": 1791832204.7405307, + "genuine_upstream_elapsed_s": 0.09816970909014344, + "injected_elapsed_s": 1.203028209041804, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2030282020568848 + } + ], + "path": "/state", + "sequence": 537, + "wall_monotonic_offset_range_s": 0.0002627372741699219 + }, + { + "delay_finished_wall": 1791832207.5819252, + "delay_started_wall": 1791832206.377185, + "genuine_upstream_elapsed_s": 0.09490183321759105, + "injected_elapsed_s": 1.2047400001902133, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.204740047454834 + } + ], + "path": "/state", + "sequence": 539, + "wall_monotonic_offset_range_s": 0.0003991127014160156 + }, + { + "delay_finished_wall": 1791832209.210984, + "delay_started_wall": 1791832208.0081983, + "genuine_upstream_elapsed_s": 0.08556483290158212, + "injected_elapsed_s": 1.2027858330402523, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2027857303619385 + } + ], + "path": "/state", + "sequence": 541, + "wall_monotonic_offset_range_s": 0.0003407001495361328 + }, + { + "delay_finished_wall": 1791832210.8634849, + "delay_started_wall": 1791832209.658373, + "genuine_upstream_elapsed_s": 0.09433629107661545, + "injected_elapsed_s": 1.2051117080263793, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2051117420196533 + } + ], + "path": "/state", + "sequence": 543, + "wall_monotonic_offset_range_s": 0.0002968311309814453 + }, + { + "delay_finished_wall": 1791832212.57867, + "delay_started_wall": 1791832211.3756423, + "genuine_upstream_elapsed_s": 0.1304359589703381, + "injected_elapsed_s": 1.2030276670120656, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2030277252197266 + } + ], + "path": "/state", + "sequence": 545, + "wall_monotonic_offset_range_s": 0.0003571510314941406 + }, + { + "delay_finished_wall": 1791832214.220621, + "delay_started_wall": 1791832213.0155869, + "genuine_upstream_elapsed_s": 0.1333520410116762, + "injected_elapsed_s": 1.205034333979711, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 0.3882322311401367 + } + ], + "path": "/state", + "sequence": 547, + "wall_monotonic_offset_range_s": 0.00029397010803222656 + }, + { + "delay_finished_wall": 1791832216.812608, + "delay_started_wall": 1791832215.61119, + "genuine_upstream_elapsed_s": 0.08992187515832484, + "injected_elapsed_s": 1.2014179578982294, + "model_overlaps": [ + { + "call": 9, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2014179229736328 + } + ], + "path": "/state", + "sequence": 562, + "wall_monotonic_offset_range_s": 0.0003185272216796875 + }, + { + "delay_finished_wall": 1791832218.466902, + "delay_started_wall": 1791832217.2640164, + "genuine_upstream_elapsed_s": 0.10495808301493526, + "injected_elapsed_s": 1.2028855830430984, + "model_overlaps": [ + { + "call": 9, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.202885627746582 + } + ], + "path": "/state", + "sequence": 564, + "wall_monotonic_offset_range_s": 0.0003886222839355469 + }, + { + "delay_finished_wall": 1791832220.111368, + "delay_started_wall": 1791832218.9085712, + "genuine_upstream_elapsed_s": 0.10092691704630852, + "injected_elapsed_s": 1.2027965830639005, + "model_overlaps": [ + { + "call": 9, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2027966976165771 + } + ], + "path": "/state", + "sequence": 566, + "wall_monotonic_offset_range_s": 0.0003330707550048828 + }, + { + "delay_finished_wall": 1791832221.7597818, + "delay_started_wall": 1791832220.5593228, + "genuine_upstream_elapsed_s": 0.10268970811739564, + "injected_elapsed_s": 1.2004589578136802, + "model_overlaps": [ + { + "call": 9, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2004590034484863 + } + ], + "path": "/state", + "sequence": 568, + "wall_monotonic_offset_range_s": 0.0002837181091308594 + }, + { + "delay_finished_wall": 1791832225.4402492, + "delay_started_wall": 1791832224.2368987, + "genuine_upstream_elapsed_s": 0.12126195780001581, + "injected_elapsed_s": 1.2033504580613226, + "model_overlaps": [ + { + "call": 10, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.20335054397583 + } + ], + "path": "/state", + "sequence": 583, + "wall_monotonic_offset_range_s": 0.0022809505462646484 + }, + { + "delay_finished_wall": 1791832227.076514, + "delay_started_wall": 1791832225.872466, + "genuine_upstream_elapsed_s": 0.12050370895303786, + "injected_elapsed_s": 1.2040479579009116, + "model_overlaps": [ + { + "call": 10, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2040479183197021 + } + ], + "path": "/state", + "sequence": 585, + "wall_monotonic_offset_range_s": 0.0003409385681152344 + }, + { + "delay_finished_wall": 1791832228.698619, + "delay_started_wall": 1791832227.4964974, + "genuine_upstream_elapsed_s": 0.09993179212324321, + "injected_elapsed_s": 1.2021215830463916, + "model_overlaps": [ + { + "call": 10, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2021214962005615 + } + ], + "path": "/state", + "sequence": 587, + "wall_monotonic_offset_range_s": 0.0005545616149902344 + }, + { + "delay_finished_wall": 1791832230.342627, + "delay_started_wall": 1791832229.1391137, + "genuine_upstream_elapsed_s": 0.1068087921012193, + "injected_elapsed_s": 1.2035132918972522, + "model_overlaps": [ + { + "call": 10, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2035133838653564 + } + ], + "path": "/state", + "sequence": 589, + "wall_monotonic_offset_range_s": 0.00033020973205566406 + }, + { + "delay_finished_wall": 1791832232.0394418, + "delay_started_wall": 1791832230.8349648, + "genuine_upstream_elapsed_s": 0.09745725011453032, + "injected_elapsed_s": 1.2044771250803024, + "model_overlaps": [ + { + "call": 10, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.204477071762085 + } + ], + "path": "/state", + "sequence": 591, + "wall_monotonic_offset_range_s": 0.00030493736267089844 + }, + { + "delay_finished_wall": 1791832233.691145, + "delay_started_wall": 1791832232.4860492, + "genuine_upstream_elapsed_s": 0.11080433311872184, + "injected_elapsed_s": 1.2050957919564098, + "model_overlaps": [ + { + "call": 10, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.1795988082885742 + } + ], + "path": "/state", + "sequence": 593, + "wall_monotonic_offset_range_s": 0.0002911090850830078 + }, + { + "delay_finished_wall": 1791832245.443464, + "delay_started_wall": 1791832244.2379303, + "genuine_upstream_elapsed_s": 0.12208491703495383, + "injected_elapsed_s": 1.2055337911006063, + "model_overlaps": [ + { + "call": 11, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.205533742904663 + } + ], + "path": "/state", + "sequence": 608, + "wall_monotonic_offset_range_s": 0.0003032684326171875 + }, + { + "delay_finished_wall": 1791832247.1843069, + "delay_started_wall": 1791832245.9779978, + "genuine_upstream_elapsed_s": 0.19989608298055828, + "injected_elapsed_s": 1.20630900003016, + "model_overlaps": [ + { + "call": 11, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2063090801239014 + } + ], + "path": "/state", + "sequence": 610, + "wall_monotonic_offset_range_s": 0.0006725788116455078 + }, + { + "delay_finished_wall": 1791832248.728961, + "delay_started_wall": 1791832247.5280106, + "genuine_upstream_elapsed_s": 0.10658545885235071, + "injected_elapsed_s": 1.2009502919390798, + "model_overlaps": [ + { + "call": 11, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2009503841400146 + } + ], + "path": "/state", + "sequence": 612, + "wall_monotonic_offset_range_s": 0.00034689903259277344 + }, + { + "delay_finished_wall": 1791832250.64911, + "delay_started_wall": 1791832249.445706, + "genuine_upstream_elapsed_s": 0.274660708848387, + "injected_elapsed_s": 1.2034040831495076, + "model_overlaps": [ + { + "call": 11, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.203404188156128 + } + ], + "path": "/state", + "sequence": 614, + "wall_monotonic_offset_range_s": 0.0013229846954345703 + }, + { + "delay_finished_wall": 1791832252.131116, + "delay_started_wall": 1791832250.9289129, + "genuine_upstream_elapsed_s": 0.11848937487229705, + "injected_elapsed_s": 1.202203124994412, + "model_overlaps": [ + { + "call": 11, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2022030353546143 + } + ], + "path": "/state", + "sequence": 616, + "wall_monotonic_offset_range_s": 0.00039649009704589844 + }, + { + "delay_finished_wall": 1791832255.648841, + "delay_started_wall": 1791832254.4483225, + "genuine_upstream_elapsed_s": 0.024830207927152514, + "injected_elapsed_s": 1.200518290977925, + "model_overlaps": [ + { + "call": 12, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2005183696746826 + } + ], + "path": "/identity", + "sequence": 630, + "wall_monotonic_offset_range_s": 0.00040221214294433594 + }, + { + "delay_finished_wall": 1791832257.266772, + "delay_started_wall": 1791832256.064008, + "genuine_upstream_elapsed_s": 0.029722166946157813, + "injected_elapsed_s": 1.2027640838641673, + "model_overlaps": [ + { + "call": 12, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2027640342712402 + } + ], + "path": "/identity", + "sequence": 631, + "wall_monotonic_offset_range_s": 0.00035691261291503906 + }, + { + "delay_finished_wall": 1791832258.873054, + "delay_started_wall": 1791832257.672685, + "genuine_upstream_elapsed_s": 0.03023020806722343, + "injected_elapsed_s": 1.2003691669087857, + "model_overlaps": [ + { + "call": 12, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.200369119644165 + } + ], + "path": "/identity", + "sequence": 632, + "wall_monotonic_offset_range_s": 0.0008234977722167969 + }, + { + "delay_finished_wall": 1791832260.484274, + "delay_started_wall": 1791832259.279202, + "genuine_upstream_elapsed_s": 0.029689125018194318, + "injected_elapsed_s": 1.2050719999242574, + "model_overlaps": [ + { + "call": 12, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2050719261169434 + } + ], + "path": "/identity", + "sequence": 633, + "wall_monotonic_offset_range_s": 0.0003199577331542969 + }, + { + "delay_finished_wall": 1791832262.09807, + "delay_started_wall": 1791832260.895133, + "genuine_upstream_elapsed_s": 0.03271045791916549, + "injected_elapsed_s": 1.2029367920476943, + "model_overlaps": [ + { + "call": 12, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 0.6850109100341797 + } + ], + "path": "/identity", + "sequence": 634, + "wall_monotonic_offset_range_s": 0.00032401084899902344 + }, + { + "delay_finished_wall": 1791832264.7211719, + "delay_started_wall": 1791832263.5168095, + "genuine_upstream_elapsed_s": 0.0447540411259979, + "injected_elapsed_s": 1.2043624999932945, + "model_overlaps": [ + { + "call": 13, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.204362392425537 + } + ], + "path": "/identity", + "sequence": 648, + "wall_monotonic_offset_range_s": 0.0003616809844970703 + }, + { + "delay_finished_wall": 1791832266.3139899, + "delay_started_wall": 1791832265.1089513, + "genuine_upstream_elapsed_s": 0.028605000115931034, + "injected_elapsed_s": 1.2050385000184178, + "model_overlaps": [ + { + "call": 13, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2050385475158691 + } + ], + "path": "/identity", + "sequence": 649, + "wall_monotonic_offset_range_s": 0.0003781318664550781 + }, + { + "delay_finished_wall": 1791832267.919033, + "delay_started_wall": 1791832266.7139947, + "genuine_upstream_elapsed_s": 0.02525570895522833, + "injected_elapsed_s": 1.2050382499583066, + "model_overlaps": [ + { + "call": 13, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.20503830909729 + } + ], + "path": "/identity", + "sequence": 650, + "wall_monotonic_offset_range_s": 0.00035643577575683594 + }, + { + "delay_finished_wall": 1791832269.526366, + "delay_started_wall": 1791832268.3221338, + "genuine_upstream_elapsed_s": 0.023437874857336283, + "injected_elapsed_s": 1.2042323329951614, + "model_overlaps": [ + { + "call": 13, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2042322158813477 + } + ], + "path": "/identity", + "sequence": 651, + "wall_monotonic_offset_range_s": 0.0003230571746826172 + }, + { + "delay_finished_wall": 1791832273.146421, + "delay_started_wall": 1791832271.9417524, + "genuine_upstream_elapsed_s": 0.026460542110726237, + "injected_elapsed_s": 1.2046686250250787, + "model_overlaps": [], + "path": "/identity", + "sequence": 661, + "wall_monotonic_offset_range_s": 0.0002894401550292969 + }, + { + "delay_finished_wall": 1791832274.7546492, + "delay_started_wall": 1791832273.5496144, + "genuine_upstream_elapsed_s": 0.02605204191058874, + "injected_elapsed_s": 1.2050346250180155, + "model_overlaps": [], + "path": "/identity", + "sequence": 662, + "wall_monotonic_offset_range_s": 0.00043654441833496094 + }, + { + "delay_finished_wall": 1791832276.3647099, + "delay_started_wall": 1791832275.159664, + "genuine_upstream_elapsed_s": 0.024871707893908024, + "injected_elapsed_s": 1.2050458330195397, + "model_overlaps": [], + "path": "/identity", + "sequence": 663, + "wall_monotonic_offset_range_s": 0.0003113746643066406 + }, + { + "delay_finished_wall": 1791832277.976248, + "delay_started_wall": 1791832276.7734878, + "genuine_upstream_elapsed_s": 0.027691290946677327, + "injected_elapsed_s": 1.2027602919843048, + "model_overlaps": [], + "path": "/identity", + "sequence": 664, + "wall_monotonic_offset_range_s": 0.00038623809814453125 + }, + { + "delay_finished_wall": 1791832279.578651, + "delay_started_wall": 1791832278.3785973, + "genuine_upstream_elapsed_s": 0.024854457937180996, + "injected_elapsed_s": 1.2000537081621587, + "model_overlaps": [], + "path": "/identity", + "sequence": 665, + "wall_monotonic_offset_range_s": 0.00032401084899902344 + }, + { + "delay_finished_wall": 1791832281.194527, + "delay_started_wall": 1791832279.9924984, + "genuine_upstream_elapsed_s": 0.027741792146116495, + "injected_elapsed_s": 1.2020284999161959, + "model_overlaps": [], + "path": "/identity", + "sequence": 666, + "wall_monotonic_offset_range_s": 0.0002951622009277344 + }, + { + "delay_finished_wall": 1791832282.808212, + "delay_started_wall": 1791832281.6038282, + "genuine_upstream_elapsed_s": 0.033845792058855295, + "injected_elapsed_s": 1.204383792122826, + "model_overlaps": [], + "path": "/identity", + "sequence": 667, + "wall_monotonic_offset_range_s": 0.00036454200744628906 + }, + { + "delay_finished_wall": 1791832284.4161339, + "delay_started_wall": 1791832283.211826, + "genuine_upstream_elapsed_s": 0.03321212506853044, + "injected_elapsed_s": 1.204307832987979, + "model_overlaps": [], + "path": "/identity", + "sequence": 668, + "wall_monotonic_offset_range_s": 0.00027680397033691406 + }, + { + "delay_finished_wall": 1791832286.023186, + "delay_started_wall": 1791832284.8185112, + "genuine_upstream_elapsed_s": 0.031950625125318766, + "injected_elapsed_s": 1.2046747910790145, + "model_overlaps": [], + "path": "/identity", + "sequence": 669, + "wall_monotonic_offset_range_s": 0.0003528594970703125 + }, + { + "delay_finished_wall": 1791832287.63012, + "delay_started_wall": 1791832286.425594, + "genuine_upstream_elapsed_s": 0.025775166926905513, + "injected_elapsed_s": 1.204525833018124, + "model_overlaps": [], + "path": "/identity", + "sequence": 670, + "wall_monotonic_offset_range_s": 0.0003333091735839844 + }, + { + "delay_finished_wall": 1791832289.234438, + "delay_started_wall": 1791832288.0331938, + "genuine_upstream_elapsed_s": 0.023571416968479753, + "injected_elapsed_s": 1.2012440001126379, + "model_overlaps": [], + "path": "/identity", + "sequence": 671, + "wall_monotonic_offset_range_s": 0.00030875205993652344 + }, + { + "delay_finished_wall": 1791832290.857811, + "delay_started_wall": 1791832289.6537929, + "genuine_upstream_elapsed_s": 0.03697133297100663, + "injected_elapsed_s": 1.2040182081982493, + "model_overlaps": [], + "path": "/identity", + "sequence": 672, + "wall_monotonic_offset_range_s": 0.0004439353942871094 + }, + { + "delay_finished_wall": 1791832292.550731, + "delay_started_wall": 1791832291.347129, + "genuine_upstream_elapsed_s": 0.10883808392100036, + "injected_elapsed_s": 1.203601917019114, + "model_overlaps": [], + "path": "/identity", + "sequence": 673, + "wall_monotonic_offset_range_s": 0.0004405975341796875 + }, + { + "delay_finished_wall": 1791832294.067949, + "delay_started_wall": 1791832292.8650882, + "genuine_upstream_elapsed_s": 0.024111542152240872, + "injected_elapsed_s": 1.2028609160333872, + "model_overlaps": [], + "path": "/identity", + "sequence": 674, + "wall_monotonic_offset_range_s": 0.00026679039001464844 + }, + { + "delay_finished_wall": 1791832295.688381, + "delay_started_wall": 1791832294.487985, + "genuine_upstream_elapsed_s": 0.035032999934628606, + "injected_elapsed_s": 1.2003959577996284, + "model_overlaps": [], + "path": "/identity", + "sequence": 675, + "wall_monotonic_offset_range_s": 0.00037360191345214844 + }, + { + "delay_finished_wall": 1791832297.2962801, + "delay_started_wall": 1791832296.0939546, + "genuine_upstream_elapsed_s": 0.03289383416995406, + "injected_elapsed_s": 1.2023255832027644, + "model_overlaps": [], + "path": "/identity", + "sequence": 676, + "wall_monotonic_offset_range_s": 0.0004773139953613281 + }, + { + "delay_finished_wall": 1791832298.916022, + "delay_started_wall": 1791832297.7152755, + "genuine_upstream_elapsed_s": 0.04468645807355642, + "injected_elapsed_s": 1.2007464580237865, + "model_overlaps": [], + "path": "/identity", + "sequence": 677, + "wall_monotonic_offset_range_s": 0.000461578369140625 + }, + { + "delay_finished_wall": 1791832300.50497, + "delay_started_wall": 1791832299.302549, + "genuine_upstream_elapsed_s": 0.024397874949499965, + "injected_elapsed_s": 1.2024210828822106, + "model_overlaps": [], + "path": "/identity", + "sequence": 678, + "wall_monotonic_offset_range_s": 0.0003190040588378906 + }, + { + "delay_finished_wall": 1791832302.12138, + "delay_started_wall": 1791832300.916171, + "genuine_upstream_elapsed_s": 0.0270841249730438, + "injected_elapsed_s": 1.205209042178467, + "model_overlaps": [], + "path": "/identity", + "sequence": 679, + "wall_monotonic_offset_range_s": 0.00034427642822265625 + }, + { + "delay_finished_wall": 1791832303.727721, + "delay_started_wall": 1791832302.5242665, + "genuine_upstream_elapsed_s": 0.025939082959666848, + "injected_elapsed_s": 1.2034545419737697, + "model_overlaps": [], + "path": "/identity", + "sequence": 680, + "wall_monotonic_offset_range_s": 0.00041365623474121094 + }, + { + "delay_finished_wall": 1791832305.343824, + "delay_started_wall": 1791832304.138787, + "genuine_upstream_elapsed_s": 0.03202695888467133, + "injected_elapsed_s": 1.2050369170028716, + "model_overlaps": [], + "path": "/identity", + "sequence": 681, + "wall_monotonic_offset_range_s": 0.00038743019104003906 + }, + { + "delay_finished_wall": 1791832306.948216, + "delay_started_wall": 1791832305.7446063, + "genuine_upstream_elapsed_s": 0.026920791948214173, + "injected_elapsed_s": 1.2036097499076277, + "model_overlaps": [], + "path": "/identity", + "sequence": 682, + "wall_monotonic_offset_range_s": 0.00027108192443847656 + }, + { + "delay_finished_wall": 1791832308.563554, + "delay_started_wall": 1791832307.3598785, + "genuine_upstream_elapsed_s": 0.031036084052175283, + "injected_elapsed_s": 1.2036756249144673, + "model_overlaps": [], + "path": "/identity", + "sequence": 683, + "wall_monotonic_offset_range_s": 0.0003514289855957031 + }, + { + "delay_finished_wall": 1791832310.165736, + "delay_started_wall": 1791832308.9651709, + "genuine_upstream_elapsed_s": 0.024415332823991776, + "injected_elapsed_s": 1.200565000064671, + "model_overlaps": [], + "path": "/identity", + "sequence": 684, + "wall_monotonic_offset_range_s": 0.0003516674041748047 + }, + { + "delay_finished_wall": 1791832311.7797499, + "delay_started_wall": 1791832310.576029, + "genuine_upstream_elapsed_s": 0.02451429096981883, + "injected_elapsed_s": 1.203720834106207, + "model_overlaps": [], + "path": "/identity", + "sequence": 685, + "wall_monotonic_offset_range_s": 0.00028967857360839844 + } + ], + "jobs": [ + { + "both_paths_overlap_model": true, + "final_status": "completed", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "path_coverage": { + "/identity": { + "healthy_injected_reads_overlapping_model": 30, + "model_calls": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "relay_sequences": [ + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 42, + 43, + 44, + 45, + 59, + 60, + 61, + 62, + 63, + 64, + 78, + 79, + 80, + 81, + 96, + 97, + 98, + 99, + 113, + 114, + 115, + 116, + 117 + ] + }, + "/state": { + "healthy_injected_reads_overlapping_model": 33, + "model_calls": [ + 8, + 9, + 10, + 11, + 12, + 13, + 14 + ], + "relay_sequences": [ + 211, + 213, + 215, + 217, + 219, + 234, + 236, + 238, + 240, + 242, + 257, + 259, + 261, + 263, + 265, + 280, + 282, + 284, + 286, + 288, + 303, + 305, + 307, + 309, + 324, + 326, + 328, + 330, + 345, + 347, + 349, + 351, + 353 + ] + } + } + }, + { + "both_paths_overlap_model": true, + "final_status": "completed", + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "path_coverage": { + "/identity": { + "healthy_injected_reads_overlapping_model": 9, + "model_calls": [ + 12, + 13 + ], + "relay_sequences": [ + 630, + 631, + 632, + 633, + 634, + 648, + 649, + 650, + 651 + ] + }, + "/state": { + "healthy_injected_reads_overlapping_model": 56, + "model_calls": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11 + ], + "relay_sequences": [ + 373, + 375, + 377, + 379, + 381, + 383, + 398, + 400, + 402, + 404, + 419, + 421, + 423, + 425, + 427, + 429, + 445, + 447, + 449, + 451, + 466, + 468, + 470, + 472, + 489, + 491, + 493, + 495, + 497, + 512, + 514, + 516, + 518, + 520, + 535, + 537, + 539, + 541, + 543, + 545, + 547, + 562, + 564, + 566, + 568, + 583, + 585, + 587, + 589, + 591, + 593, + 608, + 610, + 612, + 614, + 616 + ] + } + } + } + ], + "limits": [ + "This supplementary audit was authored after source freeze; it does not replace the frozen qualifier.", + "Model intervals use provider started_at plus monotonic elapsed_s; delay windows use recorded send time minus measured wait.", + "Wall/monotonic offset spread is retained to expose timing precision limits.", + "Overlap establishes exposure during model planning; it does not prove the cause of the historical SIM11 timeout." + ], + "model_calls": [ + { + "call": 1, + "elapsed_s": 8.888634625123814, + "finished_wall": 1791832146.4737008, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832137.585066 + }, + { + "call": 2, + "elapsed_s": 7.371869457885623, + "finished_wall": 1791832154.8865297, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832147.5146601 + }, + { + "call": 3, + "elapsed_s": 8.43007241608575, + "finished_wall": 1791832164.3083704, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832155.878298 + }, + { + "call": 4, + "elapsed_s": 6.128192666918039, + "finished_wall": 1791832172.0759635, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832165.9477708 + }, + { + "call": 5, + "elapsed_s": 7.361145915929228, + "finished_wall": 1791832180.94438, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832173.583234 + }, + { + "call": 6, + "elapsed_s": 8.747338708955795, + "finished_wall": 1791832191.5603619, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832182.813023 + }, + { + "call": 7, + "elapsed_s": 8.426699750125408, + "finished_wall": 1791832201.4861398, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832193.0594401 + }, + { + "call": 8, + "elapsed_s": 10.435514916898683, + "finished_wall": 1791832213.403819, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832202.9683042 + }, + { + "call": 9, + "elapsed_s": 6.884068874875084, + "finished_wall": 1791832221.844614, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832214.960545 + }, + { + "call": 10, + "elapsed_s": 10.11140904109925, + "finished_wall": 1791832233.665648, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832223.554239 + }, + { + "call": 11, + "elapsed_s": 8.000156666850671, + "finished_wall": 1791832252.1834106, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832244.183254 + }, + { + "call": 12, + "elapsed_s": 7.8165919170714915, + "finished_wall": 1791832261.580144, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832253.763552 + }, + { + "call": 13, + "elapsed_s": 6.307222834089771, + "finished_wall": 1791832269.6300058, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832263.322783 + }, + { + "call": 1, + "elapsed_s": 10.586642124922946, + "finished_wall": 1791831978.080672, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791831967.49403 + }, + { + "call": 2, + "elapsed_s": 6.294448249973357, + "finished_wall": 1791831984.8089693, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791831978.5145211 + }, + { + "call": 3, + "elapsed_s": 9.542235415894538, + "finished_wall": 1791831994.7687895, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791831985.2265542 + }, + { + "call": 4, + "elapsed_s": 6.4462542498949915, + "finished_wall": 1791832001.6932354, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791831995.2469811 + }, + { + "call": 5, + "elapsed_s": 4.764401749940589, + "finished_wall": 1791832007.2908835, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832002.5264819 + }, + { + "call": 6, + "elapsed_s": 8.140898042125627, + "finished_wall": 1791832016.244656, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832008.103758 + }, + { + "call": 7, + "elapsed_s": 8.440171458059922, + "finished_wall": 1791832078.4239416, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832069.9837701 + }, + { + "call": 8, + "elapsed_s": 8.132765874965116, + "finished_wall": 1791832087.3751018, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832079.242336 + }, + { + "call": 9, + "elapsed_s": 7.226740124868229, + "finished_wall": 1791832095.51877, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832088.2920299 + }, + { + "call": 10, + "elapsed_s": 7.37742824992165, + "finished_wall": 1791832103.8689063, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832096.491478 + }, + { + "call": 11, + "elapsed_s": 8.136500083142892, + "finished_wall": 1791832112.922722, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832104.786222 + }, + { + "call": 12, + "elapsed_s": 6.762274875072762, + "finished_wall": 1791832120.609567, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832113.847292 + }, + { + "call": 13, + "elapsed_s": 6.597302458947524, + "finished_wall": 1791832128.1300006, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832121.5326982 + }, + { + "call": 14, + "elapsed_s": 7.210898624965921, + "finished_wall": 1791832136.3729286, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832129.16203 + } + ], + "passed": true, + "proof_count": 83, + "proof_events_unique": true, + "strengthened_admission_bindings": [ + { + "command_id": "command-accc773656494216b8991d02cab9b603", + "passed": true + }, + { + "command_id": "command-85d8b423ec7c4a53bb1987fb1a62bdf6", + "passed": true + }, + { + "command_id": "command-b93638f4643f4d35b5cde6fde06ed807", + "passed": true + }, + { + "command_id": "command-a44e60b9d8c24bdaa49e479c4b0ef780", + "passed": true + }, + { + "command_id": "command-79dcaf5d02e048449c681748167c446a", + "passed": true + }, + { + "command_id": "command-069d2460341443e69bbcd0b5f39b91f2", + "passed": true + }, + { + "command_id": "command-20fde1f33f97495cade8fce105d16ae4", + "passed": true + }, + { + "command_id": "command-107896fd188d4b72be6d2a6debd2ba0c", + "passed": true + }, + { + "command_id": "command-1bc0e6eb4e064fe1b7b66dfbf750968a", + "passed": true + }, + { + "command_id": "command-70f9b8c19b59452693061139bda6c71b", + "passed": true + }, + { + "command_id": "command-16683e1227d84968879714ddd0556488", + "passed": true + }, + { + "command_id": "command-1fb29328cf0a4c7e800022c949b77f7a", + "passed": true + }, + { + "command_id": "command-f1e1760598fa4cd89339d2d2091f8d1a", + "passed": true + }, + { + "command_id": "command-1a4dc2ad405846ecbdfaed4b2d825f3e", + "passed": true + }, + { + "command_id": "command-55007cfbc1d7410d92d582397af2555b", + "passed": true + }, + { + "command_id": "command-ee7b8dee4444416c98f550c3e5a7c01f", + "passed": true + }, + { + "command_id": "command-77279a97fd4a4ff2aee720c0b7a46c40", + "passed": true + }, + { + "command_id": "command-3211cda07dda4c5c8dd2cc700fdaad98", + "passed": true + }, + { + "command_id": "command-e52c9ef5005a410fb80600b4ad7f947a", + "passed": true + }, + { + "command_id": "command-97245de4fa73480c87503051fc90a8a9", + "passed": true + }, + { + "command_id": "command-5457db2a5bcf4cd3ba1b306e5182ede1", + "passed": true + }, + { + "command_id": "command-d4045c653de542aea3cd3f100603a0dd", + "passed": true + }, + { + "command_id": "command-127c700baa7e4224a9f4d4d1654380fc", + "passed": true + }, + { + "command_id": "command-12d35e26325248a5881960ffcbd97a6f", + "passed": true + }, + { + "command_id": "command-fb95362e732c42c793f505dd7673ea38", + "passed": true + }, + { + "command_id": "command-5365431f8f094a738c04898a662544f5", + "passed": true + }, + { + "command_id": "command-1ca372f4627f44ca820de2e16d58a939", + "passed": true + }, + { + "command_id": "command-8074e0e06a9d46f68b944f8a3949a7d5", + "passed": true + }, + { + "command_id": "command-58c9af8646c94c0b9e453fb32e4622f1", + "passed": true + }, + { + "command_id": "command-706d43a193d349b0abb2a1a2e213670d", + "passed": true + }, + { + "command_id": "command-f01b770705654cf0b746795ef56a5e51", + "passed": true + }, + { + "command_id": "command-01c5b18c0f4848ab83a3a506c6164bd7", + "passed": true + }, + { + "command_id": "command-455161d907e44cc2a0c00c4713404a47", + "passed": true + }, + { + "command_id": "command-c085b10cc40c44539a927aa06c7a4339", + "passed": true + }, + { + "command_id": "command-7c91d1e39cc047049a3c4453e8b35923", + "passed": true + }, + { + "command_id": "command-02d0ee9bb015446e974769918792b02f", + "passed": true + }, + { + "command_id": "command-15941d3c6bb74b9a8916ec43a2fe0e1f", + "passed": true + }, + { + "command_id": "command-f615d864a28e4af88536d43c26a06e7c", + "passed": true + }, + { + "command_id": "command-d11cdb7e2538490784e2e8a2425572b7", + "passed": true + }, + { + "command_id": "command-cdf687139f2e43cab73f3afa8053018d", + "passed": true + }, + { + "command_id": "command-6db96f5e11fc4baebb1607c174a5898f", + "passed": true + }, + { + "command_id": "command-000b1a6737a14842a6b79c23e0a5ca92", + "passed": true + }, + { + "command_id": "command-72de9f33d999438198fc8b176ee89f6d", + "passed": true + }, + { + "command_id": "command-155208c1dd824b43a411bf0d9f879dc9", + "passed": true + }, + { + "command_id": "command-debb71a7f11f45b1b2093c2da309bb26", + "passed": true + }, + { + "command_id": "command-8fe46178c8564d2b8af7b48dd51ceb69", + "passed": true + }, + { + "command_id": "command-1fdd5229845447258a05de892ad29824", + "passed": true + }, + { + "command_id": "command-7e14ebef15814262bf16ee0a417469ea", + "passed": true + }, + { + "command_id": "command-a6931c3bf281480fb0dd090384b20178", + "passed": true + }, + { + "command_id": "command-5a3fb52b0a34409da8c43ce284d498f8", + "passed": true + }, + { + "command_id": "command-6371aeae6cdb432c851d1a095b64f85e", + "passed": true + }, + { + "command_id": "command-020d09130bdf4e10b2e30aed41ee93fd", + "passed": true + }, + { + "command_id": "command-185d0f856fbc4990be03fb5a4d680444", + "passed": true + }, + { + "command_id": "command-271d260ff225464ca18069efe6b89a3f", + "passed": true + }, + { + "command_id": "command-8bc103965b3b4b548f0ab2a4f6529315", + "passed": true + }, + { + "command_id": "command-52b299434a2a4354ae1a0c27f7afda73", + "passed": true + }, + { + "command_id": "command-b50b8172d84a4c2ca69c1b13799c0cd3", + "passed": true + }, + { + "command_id": "command-55878c3e64e7438786d949269a3ca632", + "passed": true + }, + { + "command_id": "command-3c4907bffd1043b7841f4f7a81e14c2e", + "passed": true + }, + { + "command_id": "command-a3d1bf0c87ed4d748bcfddf5acbcdbc8", + "passed": true + }, + { + "command_id": "command-c2820c925b7b43e48231001f02731ba6", + "passed": true + }, + { + "command_id": "command-8a9753d1c9604e3094a1478b421465f0", + "passed": true + }, + { + "command_id": "command-c21a6cfb3b154f799bb67d2695787ef8", + "passed": true + }, + { + "command_id": "command-6d7f70c0e5b14d6494d7eb02930b9d66", + "passed": true + }, + { + "command_id": "command-7d2c4e20bc454be996d99dc7edeea280", + "passed": true + }, + { + "command_id": "command-3352c7fd2fb14ffcaf69c1b27c6c53d9", + "passed": true + }, + { + "command_id": "command-896ce409c627486b9104d8d7415cefd3", + "passed": true + }, + { + "command_id": "command-1875feda0eaf4c058b771536779ccd62", + "passed": true + }, + { + "command_id": "command-844f1072836b419fa30a4c4f4a8697f1", + "passed": true + }, + { + "command_id": "command-7d6435405eaf4824b97108643856b361", + "passed": true + }, + { + "command_id": "command-7408e15ea38244a592d2800662c2b3c8", + "passed": true + }, + { + "command_id": "command-3cdfb3055b044aeeb059afc8731dac9c", + "passed": true + }, + { + "command_id": "command-59ebc979948340c1bceb69a8e01ac2aa", + "passed": true + }, + { + "command_id": "command-7761812fccc94a208c8f3e2dc26c28ec", + "passed": true + }, + { + "command_id": "command-2e37938e1f6a4a63b928572e1cf365af", + "passed": true + }, + { + "command_id": "command-e6cd1a382110409ab381d5fd4c4cc3b0", + "passed": true + }, + { + "command_id": "command-34752e8b30564841a21d087d561a5930", + "passed": true + }, + { + "command_id": "command-c966c8b893db42eb84c5b0616acca391", + "passed": true + }, + { + "command_id": "command-c8ad5019377e44159aef0ee22364b18f", + "passed": true + }, + { + "command_id": "command-10a03ed616ea4d96a26e96ae0973c32b", + "passed": true + }, + { + "command_id": "command-6b6e57d717d249c7811ef4708c6b8667", + "passed": true + }, + { + "command_id": "command-594d54e2793d428bbb42f80713e94ed3", + "passed": true + }, + { + "command_id": "command-bd5377590da74532a8333ac5ca2a72b3", + "passed": true + } + ], + "unused_admission_proof_events": [] +} diff --git a/jevdual/results/workbench-health-20261012/review-audit.json b/jevdual/results/workbench-health-20261012/review-audit.json new file mode 100644 index 0000000..8d17d05 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/review-audit.json @@ -0,0 +1,3353 @@ +{ + "audit_kind": "supplemental_post_run_read_only_review", + "audit_source_sha256": "9cece1c833596638481205ccfd8c784df4b0246fea1959673c18b6edfd3bb880", + "both_jobs_both_paths_overlap_actual_model": true, + "cancelled_calls": [], + "frozen_source_commit": "37ebfdb3054b977149d7b60b46254ddd852b749a", + "frozen_source_unchanged": true, + "healthy_checking_same_application_and_body": true, + "healthy_delayed_reads": [ + { + "delay_finished_wall": 1791831951.136652, + "delay_started_wall": 1791831949.9326327, + "genuine_upstream_elapsed_s": 0.002776124980300665, + "injected_elapsed_s": 1.2040192501153797, + "model_overlaps": [], + "path": "/identity", + "sequence": 2, + "wall_monotonic_offset_range_s": 0.00032067298889160156 + }, + { + "delay_finished_wall": 1791831952.751093, + "delay_started_wall": 1791831951.5486119, + "genuine_upstream_elapsed_s": 0.005044083809480071, + "injected_elapsed_s": 1.2024811250157654, + "model_overlaps": [], + "path": "/identity", + "sequence": 3, + "wall_monotonic_offset_range_s": 0.0005669593811035156 + }, + { + "delay_finished_wall": 1791831954.359777, + "delay_started_wall": 1791831953.1558466, + "genuine_upstream_elapsed_s": 0.0035592911299318075, + "injected_elapsed_s": 1.2039304592180997, + "model_overlaps": [], + "path": "/identity", + "sequence": 4, + "wall_monotonic_offset_range_s": 0.0003485679626464844 + }, + { + "delay_finished_wall": 1791831955.967743, + "delay_started_wall": 1791831954.7657344, + "genuine_upstream_elapsed_s": 0.0029993748757988214, + "injected_elapsed_s": 1.2020084999967366, + "model_overlaps": [], + "path": "/identity", + "sequence": 5, + "wall_monotonic_offset_range_s": 0.00022411346435546875 + }, + { + "delay_finished_wall": 1791831957.5783129, + "delay_started_wall": 1791831956.373279, + "genuine_upstream_elapsed_s": 0.0033572500105947256, + "injected_elapsed_s": 1.2050337500404567, + "model_overlaps": [], + "path": "/identity", + "sequence": 6, + "wall_monotonic_offset_range_s": 0.00037097930908203125 + }, + { + "delay_finished_wall": 1791831959.320833, + "delay_started_wall": 1791831958.1170774, + "genuine_upstream_elapsed_s": 0.09075575019232929, + "injected_elapsed_s": 1.203755582915619, + "model_overlaps": [], + "path": "/identity", + "sequence": 7, + "wall_monotonic_offset_range_s": 0.0005834102630615234 + }, + { + "delay_finished_wall": 1791831960.801874, + "delay_started_wall": 1791831959.59682, + "genuine_upstream_elapsed_s": 0.0031042909249663353, + "injected_elapsed_s": 1.2050538749899715, + "model_overlaps": [], + "path": "/identity", + "sequence": 8, + "wall_monotonic_offset_range_s": 0.0002377033233642578 + }, + { + "delay_finished_wall": 1791831962.408829, + "delay_started_wall": 1791831961.2049468, + "genuine_upstream_elapsed_s": 0.0031348341144621372, + "injected_elapsed_s": 1.203882124973461, + "model_overlaps": [], + "path": "/identity", + "sequence": 9, + "wall_monotonic_offset_range_s": 0.0003495216369628906 + }, + { + "delay_finished_wall": 1791831964.0189052, + "delay_started_wall": 1791831962.8146133, + "genuine_upstream_elapsed_s": 0.005035291891545057, + "injected_elapsed_s": 1.204291749978438, + "model_overlaps": [], + "path": "/identity", + "sequence": 10, + "wall_monotonic_offset_range_s": 0.0006890296936035156 + }, + { + "delay_finished_wall": 1791831965.624738, + "delay_started_wall": 1791831964.421972, + "genuine_upstream_elapsed_s": 0.002828457858413458, + "injected_elapsed_s": 1.2027659590821713, + "model_overlaps": [], + "path": "/identity", + "sequence": 11, + "wall_monotonic_offset_range_s": 0.00026869773864746094 + }, + { + "delay_finished_wall": 1791831967.239062, + "delay_started_wall": 1791831966.0340257, + "genuine_upstream_elapsed_s": 0.003178749931976199, + "injected_elapsed_s": 1.2050365000031888, + "model_overlaps": [], + "path": "/identity", + "sequence": 12, + "wall_monotonic_offset_range_s": 0.0002815723419189453 + }, + { + "delay_finished_wall": 1791831968.8454201, + "delay_started_wall": 1791831967.643657, + "genuine_upstream_elapsed_s": 0.00289629097096622, + "injected_elapsed_s": 1.201763124903664, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2017631530761719 + } + ], + "path": "/identity", + "sequence": 22, + "wall_monotonic_offset_range_s": 0.00020837783813476562 + }, + { + "delay_finished_wall": 1791831970.458034, + "delay_started_wall": 1791831969.252459, + "genuine_upstream_elapsed_s": 0.002508667064830661, + "injected_elapsed_s": 1.2055749581195414, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2055749893188477 + } + ], + "path": "/identity", + "sequence": 23, + "wall_monotonic_offset_range_s": 0.0002646446228027344 + }, + { + "delay_finished_wall": 1791831972.06533, + "delay_started_wall": 1791831970.8602543, + "genuine_upstream_elapsed_s": 0.003260375000536442, + "injected_elapsed_s": 1.205075625097379, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.205075740814209 + } + ], + "path": "/identity", + "sequence": 24, + "wall_monotonic_offset_range_s": 0.0003116130828857422 + }, + { + "delay_finished_wall": 1791831973.677821, + "delay_started_wall": 1791831972.4733727, + "genuine_upstream_elapsed_s": 0.003892249893397093, + "injected_elapsed_s": 1.2044482911005616, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2044482231140137 + } + ], + "path": "/identity", + "sequence": 25, + "wall_monotonic_offset_range_s": 0.0011014938354492188 + }, + { + "delay_finished_wall": 1791831975.285738, + "delay_started_wall": 1791831974.0807476, + "genuine_upstream_elapsed_s": 0.003343291115015745, + "injected_elapsed_s": 1.204990333179012, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2049903869628906 + } + ], + "path": "/identity", + "sequence": 26, + "wall_monotonic_offset_range_s": 0.00025391578674316406 + }, + { + "delay_finished_wall": 1791831976.8951979, + "delay_started_wall": 1791831975.6902578, + "genuine_upstream_elapsed_s": 0.0034893339034169912, + "injected_elapsed_s": 1.2049401660915464, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2049400806427002 + } + ], + "path": "/identity", + "sequence": 27, + "wall_monotonic_offset_range_s": 0.0002818107604980469 + }, + { + "delay_finished_wall": 1791831978.499134, + "delay_started_wall": 1791831977.2945046, + "genuine_upstream_elapsed_s": 0.003361874958500266, + "injected_elapsed_s": 1.204629332991317, + "model_overlaps": [ + { + "call": 1, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.7861673831939697 + } + ], + "path": "/identity", + "sequence": 28, + "wall_monotonic_offset_range_s": 0.0003018379211425781 + }, + { + "delay_finished_wall": 1791831980.10637, + "delay_started_wall": 1791831978.9023626, + "genuine_upstream_elapsed_s": 0.00299987499602139, + "injected_elapsed_s": 1.2040073750540614, + "model_overlaps": [ + { + "call": 2, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2040073871612549 + } + ], + "path": "/identity", + "sequence": 42, + "wall_monotonic_offset_range_s": 0.0002627372741699219 + }, + { + "delay_finished_wall": 1791831981.715692, + "delay_started_wall": 1791831980.5121903, + "genuine_upstream_elapsed_s": 0.0031940829940140247, + "injected_elapsed_s": 1.2035016249865294, + "model_overlaps": [ + { + "call": 2, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2035017013549805 + } + ], + "path": "/identity", + "sequence": 43, + "wall_monotonic_offset_range_s": 0.0002980232238769531 + }, + { + "delay_finished_wall": 1791831983.322252, + "delay_started_wall": 1791831982.121043, + "genuine_upstream_elapsed_s": 0.004062541993334889, + "injected_elapsed_s": 1.2012091248761863, + "model_overlaps": [ + { + "call": 2, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2012090682983398 + } + ], + "path": "/identity", + "sequence": 44, + "wall_monotonic_offset_range_s": 0.00027871131896972656 + }, + { + "delay_finished_wall": 1791831984.93428, + "delay_started_wall": 1791831983.73001, + "genuine_upstream_elapsed_s": 0.002662374870851636, + "injected_elapsed_s": 1.2042698750738055, + "model_overlaps": [ + { + "call": 2, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.0789592266082764 + } + ], + "path": "/identity", + "sequence": 45, + "wall_monotonic_offset_range_s": 0.00023412704467773438 + }, + { + "delay_finished_wall": 1791831986.539777, + "delay_started_wall": 1791831985.338916, + "genuine_upstream_elapsed_s": 0.0025203339755535126, + "injected_elapsed_s": 1.2008610831107944, + "model_overlaps": [ + { + "call": 3, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2008609771728516 + } + ], + "path": "/identity", + "sequence": 59, + "wall_monotonic_offset_range_s": 0.00014352798461914062 + }, + { + "delay_finished_wall": 1791831988.159762, + "delay_started_wall": 1791831986.9584835, + "genuine_upstream_elapsed_s": 0.009544290835037827, + "injected_elapsed_s": 1.2012784169055521, + "model_overlaps": [ + { + "call": 3, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2012784481048584 + } + ], + "path": "/identity", + "sequence": 60, + "wall_monotonic_offset_range_s": 0.00033092498779296875 + }, + { + "delay_finished_wall": 1791831989.7679, + "delay_started_wall": 1791831988.5628166, + "genuine_upstream_elapsed_s": 0.0035029591526836157, + "injected_elapsed_s": 1.2050834170076996, + "model_overlaps": [ + { + "call": 3, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2050833702087402 + } + ], + "path": "/identity", + "sequence": 61, + "wall_monotonic_offset_range_s": 0.00027179718017578125 + }, + { + "delay_finished_wall": 1791831991.377156, + "delay_started_wall": 1791831990.1721072, + "genuine_upstream_elapsed_s": 0.0033430408220738173, + "injected_elapsed_s": 1.2050489170942456, + "model_overlaps": [ + { + "call": 3, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2050487995147705 + } + ], + "path": "/identity", + "sequence": 62, + "wall_monotonic_offset_range_s": 0.0002346038818359375 + }, + { + "delay_finished_wall": 1791831992.996601, + "delay_started_wall": 1791831991.7964127, + "genuine_upstream_elapsed_s": 0.004118707962334156, + "injected_elapsed_s": 1.200188459130004, + "model_overlaps": [ + { + "call": 3, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.200188398361206 + } + ], + "path": "/identity", + "sequence": 63, + "wall_monotonic_offset_range_s": 0.005568981170654297 + }, + { + "delay_finished_wall": 1791831994.607224, + "delay_started_wall": 1791831993.4021256, + "genuine_upstream_elapsed_s": 0.007497084094211459, + "injected_elapsed_s": 1.2050984590314329, + "model_overlaps": [ + { + "call": 3, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2050983905792236 + } + ], + "path": "/identity", + "sequence": 64, + "wall_monotonic_offset_range_s": 0.0003578662872314453 + }, + { + "delay_finished_wall": 1791831997.214113, + "delay_started_wall": 1791831996.0115426, + "genuine_upstream_elapsed_s": 0.0029468329157680273, + "injected_elapsed_s": 1.2025703340768814, + "model_overlaps": [ + { + "call": 4, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2025704383850098 + } + ], + "path": "/identity", + "sequence": 78, + "wall_monotonic_offset_range_s": 0.00033402442932128906 + }, + { + "delay_finished_wall": 1791831998.838238, + "delay_started_wall": 1791831997.6335766, + "genuine_upstream_elapsed_s": 0.009623541962355375, + "injected_elapsed_s": 1.2046614589635283, + "model_overlaps": [ + { + "call": 4, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2046613693237305 + } + ], + "path": "/identity", + "sequence": 79, + "wall_monotonic_offset_range_s": 0.0003039836883544922 + }, + { + "delay_finished_wall": 1791832000.441949, + "delay_started_wall": 1791831999.2368922, + "genuine_upstream_elapsed_s": 0.003390332916751504, + "injected_elapsed_s": 1.2050566668622196, + "model_overlaps": [ + { + "call": 4, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2050566673278809 + } + ], + "path": "/identity", + "sequence": 80, + "wall_monotonic_offset_range_s": 0.00024771690368652344 + }, + { + "delay_finished_wall": 1791832002.051035, + "delay_started_wall": 1791832000.8462179, + "genuine_upstream_elapsed_s": 0.00339320907369256, + "injected_elapsed_s": 1.2048170422203839, + "model_overlaps": [ + { + "call": 4, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.8470175266265869 + } + ], + "path": "/identity", + "sequence": 81, + "wall_monotonic_offset_range_s": 0.0002200603485107422 + }, + { + "delay_finished_wall": 1791832003.6653, + "delay_started_wall": 1791832002.4623945, + "genuine_upstream_elapsed_s": 0.012598375091329217, + "injected_elapsed_s": 1.2029053340665996, + "model_overlaps": [ + { + "call": 5, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.1388180255889893 + } + ], + "path": "/identity", + "sequence": 96, + "wall_monotonic_offset_range_s": 0.00030541419982910156 + }, + { + "delay_finished_wall": 1791832005.285439, + "delay_started_wall": 1791832004.0819662, + "genuine_upstream_elapsed_s": 0.01664000004529953, + "injected_elapsed_s": 1.2034727919381112, + "model_overlaps": [ + { + "call": 5, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2034728527069092 + } + ], + "path": "/identity", + "sequence": 97, + "wall_monotonic_offset_range_s": 0.0003800392150878906 + }, + { + "delay_finished_wall": 1791832006.90365, + "delay_started_wall": 1791832005.698815, + "genuine_upstream_elapsed_s": 0.019909291993826628, + "injected_elapsed_s": 1.2048349999822676, + "model_overlaps": [ + { + "call": 5, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2048349380493164 + } + ], + "path": "/identity", + "sequence": 98, + "wall_monotonic_offset_range_s": 0.0004360675811767578 + }, + { + "delay_finished_wall": 1791832008.506123, + "delay_started_wall": 1791832007.3038552, + "genuine_upstream_elapsed_s": 0.016452833777293563, + "injected_elapsed_s": 1.2022679159417748, + "model_overlaps": [ + { + "call": 6, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.40236496925354004 + } + ], + "path": "/identity", + "sequence": 99, + "wall_monotonic_offset_range_s": 0.0002734661102294922 + }, + { + "delay_finished_wall": 1791832010.115941, + "delay_started_wall": 1791832008.9121234, + "genuine_upstream_elapsed_s": 0.013531167060136795, + "injected_elapsed_s": 1.2038175829220563, + "model_overlaps": [ + { + "call": 6, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.20381760597229 + } + ], + "path": "/identity", + "sequence": 113, + "wall_monotonic_offset_range_s": 0.0002827644348144531 + }, + { + "delay_finished_wall": 1791832011.727929, + "delay_started_wall": 1791832010.5238676, + "genuine_upstream_elapsed_s": 0.013567666988819838, + "injected_elapsed_s": 1.2040615831501782, + "model_overlaps": [ + { + "call": 6, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.204061508178711 + } + ], + "path": "/identity", + "sequence": 114, + "wall_monotonic_offset_range_s": 0.0003399848937988281 + }, + { + "delay_finished_wall": 1791832013.34195, + "delay_started_wall": 1791832012.1412833, + "genuine_upstream_elapsed_s": 0.021057708887383342, + "injected_elapsed_s": 1.2006667500827461, + "model_overlaps": [ + { + "call": 6, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2006666660308838 + } + ], + "path": "/identity", + "sequence": 115, + "wall_monotonic_offset_range_s": 0.000274658203125 + }, + { + "delay_finished_wall": 1791832014.943507, + "delay_started_wall": 1791832013.7429645, + "genuine_upstream_elapsed_s": 0.014461665879935026, + "injected_elapsed_s": 1.2005423330701888, + "model_overlaps": [ + { + "call": 6, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2005424499511719 + } + ], + "path": "/identity", + "sequence": 116, + "wall_monotonic_offset_range_s": 0.00047659873962402344 + }, + { + "delay_finished_wall": 1791832016.56296, + "delay_started_wall": 1791832015.3582413, + "genuine_upstream_elapsed_s": 0.017344833118841052, + "injected_elapsed_s": 1.204718666849658, + "model_overlaps": [ + { + "call": 6, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.8864147663116455 + } + ], + "path": "/identity", + "sequence": 117, + "wall_monotonic_offset_range_s": 0.0005025863647460938 + }, + { + "delay_finished_wall": 1791832018.174688, + "delay_started_wall": 1791832016.970701, + "genuine_upstream_elapsed_s": 0.019155291141942143, + "injected_elapsed_s": 1.2039870421867818, + "model_overlaps": [], + "path": "/identity", + "sequence": 126, + "wall_monotonic_offset_range_s": 0.00030732154846191406 + }, + { + "delay_finished_wall": 1791832019.776822, + "delay_started_wall": 1791832018.5753713, + "genuine_upstream_elapsed_s": 0.012994375079870224, + "injected_elapsed_s": 1.2014507499989122, + "model_overlaps": [], + "path": "/identity", + "sequence": 127, + "wall_monotonic_offset_range_s": 0.00035262107849121094 + }, + { + "delay_finished_wall": 1791832021.3976462, + "delay_started_wall": 1791832020.1962073, + "genuine_upstream_elapsed_s": 0.02700358279980719, + "injected_elapsed_s": 1.2014389999676496, + "model_overlaps": [], + "path": "/identity", + "sequence": 128, + "wall_monotonic_offset_range_s": 0.0003247261047363281 + }, + { + "delay_finished_wall": 1791832022.9915159, + "delay_started_wall": 1791832021.78787, + "genuine_upstream_elapsed_s": 0.01330141699872911, + "injected_elapsed_s": 1.2036458330694586, + "model_overlaps": [], + "path": "/identity", + "sequence": 129, + "wall_monotonic_offset_range_s": 0.0003898143768310547 + }, + { + "delay_finished_wall": 1791832024.675057, + "delay_started_wall": 1791832023.4683318, + "genuine_upstream_elapsed_s": 0.024299542186781764, + "injected_elapsed_s": 1.2067252080887556, + "model_overlaps": [], + "path": "/identity", + "sequence": 130, + "wall_monotonic_offset_range_s": 0.00021409988403320312 + }, + { + "delay_finished_wall": 1791832026.3910022, + "delay_started_wall": 1791832025.181681, + "genuine_upstream_elapsed_s": 0.033242583042010665, + "injected_elapsed_s": 1.2093212499748915, + "model_overlaps": [], + "path": "/identity", + "sequence": 131, + "wall_monotonic_offset_range_s": 0.0005581378936767578 + }, + { + "delay_finished_wall": 1791832027.847797, + "delay_started_wall": 1791832026.6440387, + "genuine_upstream_elapsed_s": 0.0233499170280993, + "injected_elapsed_s": 1.2037583331111819, + "model_overlaps": [], + "path": "/identity", + "sequence": 132, + "wall_monotonic_offset_range_s": 0.0006263256072998047 + }, + { + "delay_finished_wall": 1791832029.485719, + "delay_started_wall": 1791832028.2835376, + "genuine_upstream_elapsed_s": 0.023109209025278687, + "injected_elapsed_s": 1.2021812500897795, + "model_overlaps": [], + "path": "/identity", + "sequence": 133, + "wall_monotonic_offset_range_s": 0.0005357265472412109 + }, + { + "delay_finished_wall": 1791832081.0974221, + "delay_started_wall": 1791832079.8953402, + "genuine_upstream_elapsed_s": 0.04533958388492465, + "injected_elapsed_s": 1.2020818751771003, + "model_overlaps": [ + { + "call": 8, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2020819187164307 + } + ], + "path": "/state", + "sequence": 211, + "wall_monotonic_offset_range_s": 0.00023651123046875 + }, + { + "delay_finished_wall": 1791832082.7192068, + "delay_started_wall": 1791832081.518623, + "genuine_upstream_elapsed_s": 0.04676191695034504, + "injected_elapsed_s": 1.2005837499164045, + "model_overlaps": [ + { + "call": 8, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2005836963653564 + } + ], + "path": "/state", + "sequence": 213, + "wall_monotonic_offset_range_s": 0.0003616809844970703 + }, + { + "delay_finished_wall": 1791832084.339633, + "delay_started_wall": 1791832083.1349635, + "genuine_upstream_elapsed_s": 0.04107379191555083, + "injected_elapsed_s": 1.2046695840544999, + "model_overlaps": [ + { + "call": 8, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.20466947555542 + } + ], + "path": "/state", + "sequence": 215, + "wall_monotonic_offset_range_s": 0.0002899169921875 + }, + { + "delay_finished_wall": 1791832085.960802, + "delay_started_wall": 1791832084.7594473, + "genuine_upstream_elapsed_s": 0.04071483411826193, + "injected_elapsed_s": 1.2013546251691878, + "model_overlaps": [ + { + "call": 8, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.201354742050171 + } + ], + "path": "/state", + "sequence": 217, + "wall_monotonic_offset_range_s": 0.00032806396484375 + }, + { + "delay_finished_wall": 1791832087.585452, + "delay_started_wall": 1791832086.384062, + "genuine_upstream_elapsed_s": 0.041966750053688884, + "injected_elapsed_s": 1.2013900419697165, + "model_overlaps": [ + { + "call": 8, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.9910397529602051 + } + ], + "path": "/state", + "sequence": 219, + "wall_monotonic_offset_range_s": 0.00026154518127441406 + }, + { + "delay_finished_wall": 1791832090.22193, + "delay_started_wall": 1791832089.0204175, + "genuine_upstream_elapsed_s": 0.04945133300498128, + "injected_elapsed_s": 1.2015124999452382, + "model_overlaps": [ + { + "call": 9, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2015125751495361 + } + ], + "path": "/state", + "sequence": 234, + "wall_monotonic_offset_range_s": 0.0002675056457519531 + }, + { + "delay_finished_wall": 1791832091.845014, + "delay_started_wall": 1791832090.6439574, + "genuine_upstream_elapsed_s": 0.04513141606003046, + "injected_elapsed_s": 1.2010567500256002, + "model_overlaps": [ + { + "call": 9, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.201056718826294 + } + ], + "path": "/state", + "sequence": 236, + "wall_monotonic_offset_range_s": 0.0003001689910888672 + }, + { + "delay_finished_wall": 1791832093.470641, + "delay_started_wall": 1791832092.269904, + "genuine_upstream_elapsed_s": 0.046239792136475444, + "injected_elapsed_s": 1.2007370002102107, + "model_overlaps": [ + { + "call": 9, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2007369995117188 + } + ], + "path": "/state", + "sequence": 238, + "wall_monotonic_offset_range_s": 0.0003094673156738281 + }, + { + "delay_finished_wall": 1791832095.102564, + "delay_started_wall": 1791832093.902203, + "genuine_upstream_elapsed_s": 0.05429583298973739, + "injected_elapsed_s": 1.2003611251711845, + "model_overlaps": [ + { + "call": 9, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2003610134124756 + } + ], + "path": "/state", + "sequence": 240, + "wall_monotonic_offset_range_s": 0.0003120899200439453 + }, + { + "delay_finished_wall": 1791832096.736833, + "delay_started_wall": 1791832095.5317907, + "genuine_upstream_elapsed_s": 0.04574579210020602, + "injected_elapsed_s": 1.20504245813936, + "model_overlaps": [ + { + "call": 10, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.24535512924194336 + } + ], + "path": "/state", + "sequence": 242, + "wall_monotonic_offset_range_s": 0.004605531692504883 + }, + { + "delay_finished_wall": 1791832098.352134, + "delay_started_wall": 1791832097.1493397, + "genuine_upstream_elapsed_s": 0.04627279192209244, + "injected_elapsed_s": 1.202794292010367, + "model_overlaps": [ + { + "call": 10, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2027943134307861 + } + ], + "path": "/state", + "sequence": 257, + "wall_monotonic_offset_range_s": 0.0003185272216796875 + }, + { + "delay_finished_wall": 1791832099.991491, + "delay_started_wall": 1791832098.7868052, + "genuine_upstream_elapsed_s": 0.04723550006747246, + "injected_elapsed_s": 1.2046860409900546, + "model_overlaps": [ + { + "call": 10, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.204685926437378 + } + ], + "path": "/state", + "sequence": 259, + "wall_monotonic_offset_range_s": 0.000301361083984375 + }, + { + "delay_finished_wall": 1791832101.614825, + "delay_started_wall": 1791832100.4146428, + "genuine_upstream_elapsed_s": 0.047370166052132845, + "injected_elapsed_s": 1.2001820839941502, + "model_overlaps": [ + { + "call": 10, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2001821994781494 + } + ], + "path": "/state", + "sequence": 261, + "wall_monotonic_offset_range_s": 0.0003604888916015625 + }, + { + "delay_finished_wall": 1791832103.239155, + "delay_started_wall": 1791832102.0341103, + "genuine_upstream_elapsed_s": 0.04227699991315603, + "injected_elapsed_s": 1.2050447911024094, + "model_overlaps": [ + { + "call": 10, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2050447463989258 + } + ], + "path": "/state", + "sequence": 263, + "wall_monotonic_offset_range_s": 0.0004055500030517578 + }, + { + "delay_finished_wall": 1791832104.877825, + "delay_started_wall": 1791832103.6765485, + "genuine_upstream_elapsed_s": 0.056063625030219555, + "injected_elapsed_s": 1.2012766250409186, + "model_overlaps": [ + { + "call": 10, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.19235777854919434 + }, + { + "call": 11, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.09160304069519043 + } + ], + "path": "/state", + "sequence": 265, + "wall_monotonic_offset_range_s": 0.0004611015319824219 + }, + { + "delay_finished_wall": 1791832106.4973052, + "delay_started_wall": 1791832105.292261, + "genuine_upstream_elapsed_s": 0.046128083020448685, + "injected_elapsed_s": 1.2050441671162844, + "model_overlaps": [ + { + "call": 11, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2050442695617676 + } + ], + "path": "/state", + "sequence": 280, + "wall_monotonic_offset_range_s": 0.0004367828369140625 + }, + { + "delay_finished_wall": 1791832108.148909, + "delay_started_wall": 1791832106.9444427, + "genuine_upstream_elapsed_s": 0.0588835421949625, + "injected_elapsed_s": 1.2044663750566542, + "model_overlaps": [ + { + "call": 11, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2044663429260254 + } + ], + "path": "/state", + "sequence": 282, + "wall_monotonic_offset_range_s": 0.0002453327178955078 + }, + { + "delay_finished_wall": 1791832109.773719, + "delay_started_wall": 1791832108.572417, + "genuine_upstream_elapsed_s": 0.050575749948620796, + "injected_elapsed_s": 1.2013020410668105, + "model_overlaps": [ + { + "call": 11, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2013020515441895 + } + ], + "path": "/state", + "sequence": 284, + "wall_monotonic_offset_range_s": 0.00033092498779296875 + }, + { + "delay_finished_wall": 1791832111.400123, + "delay_started_wall": 1791832110.1959536, + "genuine_upstream_elapsed_s": 0.046160792000591755, + "injected_elapsed_s": 1.204169207951054, + "model_overlaps": [ + { + "call": 11, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2041692733764648 + } + ], + "path": "/state", + "sequence": 286, + "wall_monotonic_offset_range_s": 0.0002598762512207031 + }, + { + "delay_finished_wall": 1791832113.030656, + "delay_started_wall": 1791832111.8256311, + "genuine_upstream_elapsed_s": 0.044818750116974115, + "injected_elapsed_s": 1.205024957889691, + "model_overlaps": [ + { + "call": 11, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.0970909595489502 + } + ], + "path": "/state", + "sequence": 288, + "wall_monotonic_offset_range_s": 0.0003409385681152344 + }, + { + "delay_finished_wall": 1791832115.661109, + "delay_started_wall": 1791832114.4575708, + "genuine_upstream_elapsed_s": 0.04853254184126854, + "injected_elapsed_s": 1.203538167057559, + "model_overlaps": [ + { + "call": 12, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.203538179397583 + } + ], + "path": "/state", + "sequence": 303, + "wall_monotonic_offset_range_s": 0.0002593994140625 + }, + { + "delay_finished_wall": 1791832117.282847, + "delay_started_wall": 1791832116.0813665, + "genuine_upstream_elapsed_s": 0.04559245798736811, + "injected_elapsed_s": 1.2014803329948336, + "model_overlaps": [ + { + "call": 12, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2014803886413574 + } + ], + "path": "/state", + "sequence": 305, + "wall_monotonic_offset_range_s": 0.00033092498779296875 + }, + { + "delay_finished_wall": 1791832118.914329, + "delay_started_wall": 1791832117.7099512, + "genuine_upstream_elapsed_s": 0.048586250049993396, + "injected_elapsed_s": 1.2043779590167105, + "model_overlaps": [ + { + "call": 12, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2043778896331787 + } + ], + "path": "/state", + "sequence": 307, + "wall_monotonic_offset_range_s": 0.0003142356872558594 + }, + { + "delay_finished_wall": 1791832120.540607, + "delay_started_wall": 1791832119.338083, + "genuine_upstream_elapsed_s": 0.04915620805695653, + "injected_elapsed_s": 1.2025240000803024, + "model_overlaps": [ + { + "call": 12, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.202523946762085 + } + ], + "path": "/state", + "sequence": 309, + "wall_monotonic_offset_range_s": 0.0002961158752441406 + }, + { + "delay_finished_wall": 1791832123.173666, + "delay_started_wall": 1791832121.9724607, + "genuine_upstream_elapsed_s": 0.05501812510192394, + "injected_elapsed_s": 1.2012051669880748, + "model_overlaps": [ + { + "call": 13, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2012052536010742 + } + ], + "path": "/state", + "sequence": 324, + "wall_monotonic_offset_range_s": 0.0004284381866455078 + }, + { + "delay_finished_wall": 1791832124.797261, + "delay_started_wall": 1791832123.5969458, + "genuine_upstream_elapsed_s": 0.05386095796711743, + "injected_elapsed_s": 1.20031529199332, + "model_overlaps": [ + { + "call": 13, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.200315237045288 + } + ], + "path": "/state", + "sequence": 326, + "wall_monotonic_offset_range_s": 0.0003066062927246094 + }, + { + "delay_finished_wall": 1791832126.434151, + "delay_started_wall": 1791832125.228527, + "genuine_upstream_elapsed_s": 0.05417429096996784, + "injected_elapsed_s": 1.2056237920187414, + "model_overlaps": [ + { + "call": 13, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2056238651275635 + } + ], + "path": "/state", + "sequence": 328, + "wall_monotonic_offset_range_s": 0.0003712177276611328 + }, + { + "delay_finished_wall": 1791832128.0681322, + "delay_started_wall": 1791832126.8663008, + "genuine_upstream_elapsed_s": 0.059533082880079746, + "injected_elapsed_s": 1.2018312909640372, + "model_overlaps": [ + { + "call": 13, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.201831340789795 + } + ], + "path": "/state", + "sequence": 330, + "wall_monotonic_offset_range_s": 0.0003476142883300781 + }, + { + "delay_finished_wall": 1791832130.698807, + "delay_started_wall": 1791832129.4979184, + "genuine_upstream_elapsed_s": 0.05425262497738004, + "injected_elapsed_s": 1.2008886248804629, + "model_overlaps": [ + { + "call": 14, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2008886337280273 + } + ], + "path": "/state", + "sequence": 345, + "wall_monotonic_offset_range_s": 0.000308990478515625 + }, + { + "delay_finished_wall": 1791832132.343055, + "delay_started_wall": 1791832131.1425955, + "genuine_upstream_elapsed_s": 0.07181812496855855, + "injected_elapsed_s": 1.2004595410544425, + "model_overlaps": [ + { + "call": 14, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2004594802856445 + } + ], + "path": "/state", + "sequence": 347, + "wall_monotonic_offset_range_s": 0.0002918243408203125 + }, + { + "delay_finished_wall": 1791832133.955904, + "delay_started_wall": 1791832132.7540894, + "genuine_upstream_elapsed_s": 0.052502250066027045, + "injected_elapsed_s": 1.2018145839683712, + "model_overlaps": [ + { + "call": 14, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2018146514892578 + } + ], + "path": "/state", + "sequence": 349, + "wall_monotonic_offset_range_s": 0.0005249977111816406 + }, + { + "delay_finished_wall": 1791832135.582197, + "delay_started_wall": 1791832134.3774855, + "genuine_upstream_elapsed_s": 0.04988845787011087, + "injected_elapsed_s": 1.2047113340813667, + "model_overlaps": [ + { + "call": 14, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 1.2047114372253418 + } + ], + "path": "/state", + "sequence": 351, + "wall_monotonic_offset_range_s": 0.0002865791320800781 + }, + { + "delay_finished_wall": 1791832137.22383, + "delay_started_wall": 1791832136.022428, + "genuine_upstream_elapsed_s": 0.06868283404037356, + "injected_elapsed_s": 1.2014020418282598, + "model_overlaps": [ + { + "call": 14, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "overlap_s": 0.35050058364868164 + } + ], + "path": "/state", + "sequence": 353, + "wall_monotonic_offset_range_s": 0.0003383159637451172 + }, + { + "delay_finished_wall": 1791832138.840893, + "delay_started_wall": 1791832137.6358533, + "genuine_upstream_elapsed_s": 0.05692441714927554, + "injected_elapsed_s": 1.2050397919956595, + "model_overlaps": [ + { + "call": 1, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2050397396087646 + } + ], + "path": "/state", + "sequence": 373, + "wall_monotonic_offset_range_s": 0.00036334991455078125 + }, + { + "delay_finished_wall": 1791832140.523594, + "delay_started_wall": 1791832139.3210244, + "genuine_upstream_elapsed_s": 0.08095504203811288, + "injected_elapsed_s": 1.2025694160256535, + "model_overlaps": [ + { + "call": 1, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2025694847106934 + } + ], + "path": "/state", + "sequence": 375, + "wall_monotonic_offset_range_s": 0.0003018379211425781 + }, + { + "delay_finished_wall": 1791832142.144813, + "delay_started_wall": 1791832140.9398186, + "genuine_upstream_elapsed_s": 0.06230750004760921, + "injected_elapsed_s": 1.2049943341407925, + "model_overlaps": [ + { + "call": 1, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2049944400787354 + } + ], + "path": "/state", + "sequence": 377, + "wall_monotonic_offset_range_s": 0.00025177001953125 + }, + { + "delay_finished_wall": 1791832143.774648, + "delay_started_wall": 1791832142.5730557, + "genuine_upstream_elapsed_s": 0.05781725002452731, + "injected_elapsed_s": 1.2015922078862786, + "model_overlaps": [ + { + "call": 1, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.201592206954956 + } + ], + "path": "/state", + "sequence": 379, + "wall_monotonic_offset_range_s": 0.0005812644958496094 + }, + { + "delay_finished_wall": 1791832145.40432, + "delay_started_wall": 1791832144.1992726, + "genuine_upstream_elapsed_s": 0.05713516683317721, + "injected_elapsed_s": 1.2050473750568926, + "model_overlaps": [ + { + "call": 1, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.205047369003296 + } + ], + "path": "/state", + "sequence": 381, + "wall_monotonic_offset_range_s": 0.0003993511199951172 + }, + { + "delay_finished_wall": 1791832147.056463, + "delay_started_wall": 1791832145.8514771, + "genuine_upstream_elapsed_s": 0.07064691698178649, + "injected_elapsed_s": 1.2049859159160405, + "model_overlaps": [ + { + "call": 1, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 0.6222236156463623 + } + ], + "path": "/state", + "sequence": 383, + "wall_monotonic_offset_range_s": 0.0005612373352050781 + }, + { + "delay_finished_wall": 1791832149.664667, + "delay_started_wall": 1791832148.461329, + "genuine_upstream_elapsed_s": 0.05194795900024474, + "injected_elapsed_s": 1.2033379168715328, + "model_overlaps": [ + { + "call": 2, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2033379077911377 + } + ], + "path": "/state", + "sequence": 398, + "wall_monotonic_offset_range_s": 0.0003561973571777344 + }, + { + "delay_finished_wall": 1791832151.294802, + "delay_started_wall": 1791832150.0908835, + "genuine_upstream_elapsed_s": 0.052277165930718184, + "injected_elapsed_s": 1.2039184591267258, + "model_overlaps": [ + { + "call": 2, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.20391845703125 + } + ], + "path": "/state", + "sequence": 400, + "wall_monotonic_offset_range_s": 0.00032806396484375 + }, + { + "delay_finished_wall": 1791832152.944254, + "delay_started_wall": 1791832151.7392159, + "genuine_upstream_elapsed_s": 0.07039116695523262, + "injected_elapsed_s": 1.2050380420405418, + "model_overlaps": [ + { + "call": 2, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.205038070678711 + } + ], + "path": "/state", + "sequence": 402, + "wall_monotonic_offset_range_s": 0.0005617141723632812 + }, + { + "delay_finished_wall": 1791832154.562625, + "delay_started_wall": 1791832153.3620024, + "genuine_upstream_elapsed_s": 0.060910666128620505, + "injected_elapsed_s": 1.2006225001532584, + "model_overlaps": [ + { + "call": 2, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.20062255859375 + } + ], + "path": "/state", + "sequence": 404, + "wall_monotonic_offset_range_s": 0.0006086826324462891 + }, + { + "delay_finished_wall": 1791832157.187784, + "delay_started_wall": 1791832155.9839876, + "genuine_upstream_elapsed_s": 0.054760915925726295, + "injected_elapsed_s": 1.2037962921895087, + "model_overlaps": [ + { + "call": 3, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.20379638671875 + } + ], + "path": "/state", + "sequence": 419, + "wall_monotonic_offset_range_s": 0.002040386199951172 + }, + { + "delay_finished_wall": 1791832158.8312519, + "delay_started_wall": 1791832157.6299183, + "genuine_upstream_elapsed_s": 0.06451662513427436, + "injected_elapsed_s": 1.2013334999792278, + "model_overlaps": [ + { + "call": 3, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2013335227966309 + } + ], + "path": "/state", + "sequence": 421, + "wall_monotonic_offset_range_s": 0.0003459453582763672 + }, + { + "delay_finished_wall": 1791832160.4510279, + "delay_started_wall": 1791832159.2491255, + "genuine_upstream_elapsed_s": 0.05649487487971783, + "injected_elapsed_s": 1.2019024998880923, + "model_overlaps": [ + { + "call": 3, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2019023895263672 + } + ], + "path": "/state", + "sequence": 423, + "wall_monotonic_offset_range_s": 0.0005939006805419922 + }, + { + "delay_finished_wall": 1791832162.102963, + "delay_started_wall": 1791832160.9011786, + "genuine_upstream_elapsed_s": 0.06587324989959598, + "injected_elapsed_s": 1.2017843751236796, + "model_overlaps": [ + { + "call": 3, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.201784372329712 + } + ], + "path": "/state", + "sequence": 425, + "wall_monotonic_offset_range_s": 0.0006198883056640625 + }, + { + "delay_finished_wall": 1791832163.716691, + "delay_started_wall": 1791832162.5151398, + "genuine_upstream_elapsed_s": 0.05449808295816183, + "injected_elapsed_s": 1.2015511670615524, + "model_overlaps": [ + { + "call": 3, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2015511989593506 + } + ], + "path": "/state", + "sequence": 427, + "wall_monotonic_offset_range_s": 0.0002949237823486328 + }, + { + "delay_finished_wall": 1791832165.361527, + "delay_started_wall": 1791832164.1579523, + "genuine_upstream_elapsed_s": 0.07102287514135242, + "injected_elapsed_s": 1.203574666986242, + "model_overlaps": [ + { + "call": 3, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 0.15041804313659668 + } + ], + "path": "/state", + "sequence": 429, + "wall_monotonic_offset_range_s": 0.0006797313690185547 + }, + { + "delay_finished_wall": 1791832168.0370932, + "delay_started_wall": 1791832166.8349886, + "genuine_upstream_elapsed_s": 0.09905841713771224, + "injected_elapsed_s": 1.2021046250592917, + "model_overlaps": [ + { + "call": 4, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2021045684814453 + } + ], + "path": "/state", + "sequence": 445, + "wall_monotonic_offset_range_s": 0.0002818107604980469 + }, + { + "delay_finished_wall": 1791832169.681438, + "delay_started_wall": 1791832168.4777927, + "genuine_upstream_elapsed_s": 0.09563441714271903, + "injected_elapsed_s": 1.203645166940987, + "model_overlaps": [ + { + "call": 4, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2036452293395996 + } + ], + "path": "/state", + "sequence": 447, + "wall_monotonic_offset_range_s": 0.0003516674041748047 + }, + { + "delay_finished_wall": 1791832171.319584, + "delay_started_wall": 1791832170.1186836, + "genuine_upstream_elapsed_s": 0.09399583307094872, + "injected_elapsed_s": 1.2009002920240164, + "model_overlaps": [ + { + "call": 4, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2009003162384033 + } + ], + "path": "/state", + "sequence": 449, + "wall_monotonic_offset_range_s": 0.0005812644958496094 + }, + { + "delay_finished_wall": 1791832172.953556, + "delay_started_wall": 1791832171.753168, + "genuine_upstream_elapsed_s": 0.08927262504585087, + "injected_elapsed_s": 1.200387875083834, + "model_overlaps": [ + { + "call": 4, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 0.32279539108276367 + } + ], + "path": "/state", + "sequence": 451, + "wall_monotonic_offset_range_s": 0.0003695487976074219 + }, + { + "delay_finished_wall": 1791832175.5946329, + "delay_started_wall": 1791832174.3903122, + "genuine_upstream_elapsed_s": 0.08938425011001527, + "injected_elapsed_s": 1.2043205830268562, + "model_overlaps": [ + { + "call": 5, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2043206691741943 + } + ], + "path": "/state", + "sequence": 466, + "wall_monotonic_offset_range_s": 0.00030350685119628906 + }, + { + "delay_finished_wall": 1791832177.2314198, + "delay_started_wall": 1791832176.030892, + "genuine_upstream_elapsed_s": 0.08454362489283085, + "injected_elapsed_s": 1.2005280000157654, + "model_overlaps": [ + { + "call": 5, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2005279064178467 + } + ], + "path": "/state", + "sequence": 468, + "wall_monotonic_offset_range_s": 0.0006012916564941406 + }, + { + "delay_finished_wall": 1791832178.8902102, + "delay_started_wall": 1791832177.6890914, + "genuine_upstream_elapsed_s": 0.10279183299280703, + "injected_elapsed_s": 1.2011187500320375, + "model_overlaps": [ + { + "call": 5, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2011187076568604 + } + ], + "path": "/state", + "sequence": 470, + "wall_monotonic_offset_range_s": 0.0003154277801513672 + }, + { + "delay_finished_wall": 1791832180.523807, + "delay_started_wall": 1791832179.3190742, + "genuine_upstream_elapsed_s": 0.09166183299385011, + "injected_elapsed_s": 1.2047327919863164, + "model_overlaps": [ + { + "call": 5, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.204732894897461 + } + ], + "path": "/state", + "sequence": 472, + "wall_monotonic_offset_range_s": 0.0003097057342529297 + }, + { + "delay_finished_wall": 1791832182.173342, + "delay_started_wall": 1791832180.9683156, + "genuine_upstream_elapsed_s": 0.09823537501506507, + "injected_elapsed_s": 1.205026458017528, + "model_overlaps": [], + "path": "/state", + "sequence": 474, + "wall_monotonic_offset_range_s": 0.0003254413604736328 + }, + { + "delay_finished_wall": 1791832184.845115, + "delay_started_wall": 1791832183.6415613, + "genuine_upstream_elapsed_s": 0.1093828750308603, + "injected_elapsed_s": 1.2035536249168217, + "model_overlaps": [ + { + "call": 6, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2035536766052246 + } + ], + "path": "/state", + "sequence": 489, + "wall_monotonic_offset_range_s": 0.0005691051483154297 + }, + { + "delay_finished_wall": 1791832186.476904, + "delay_started_wall": 1791832185.2725573, + "genuine_upstream_elapsed_s": 0.09748349990695715, + "injected_elapsed_s": 1.204346542013809, + "model_overlaps": [ + { + "call": 6, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2043466567993164 + } + ], + "path": "/state", + "sequence": 491, + "wall_monotonic_offset_range_s": 0.00036978721618652344 + }, + { + "delay_finished_wall": 1791832188.116647, + "delay_started_wall": 1791832186.9127026, + "genuine_upstream_elapsed_s": 0.09679954196326435, + "injected_elapsed_s": 1.2039444581605494, + "model_overlaps": [ + { + "call": 6, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.203944444656372 + } + ], + "path": "/state", + "sequence": 493, + "wall_monotonic_offset_range_s": 0.0013127326965332031 + }, + { + "delay_finished_wall": 1791832189.75743, + "delay_started_wall": 1791832188.5523894, + "genuine_upstream_elapsed_s": 0.0957978330552578, + "injected_elapsed_s": 1.205040582921356, + "model_overlaps": [ + { + "call": 6, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.205040693283081 + } + ], + "path": "/state", + "sequence": 495, + "wall_monotonic_offset_range_s": 0.0003192424774169922 + }, + { + "delay_finished_wall": 1791832191.387458, + "delay_started_wall": 1791832190.1836927, + "genuine_upstream_elapsed_s": 0.08754870807752013, + "injected_elapsed_s": 1.2037652910221368, + "model_overlaps": [ + { + "call": 6, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2037653923034668 + } + ], + "path": "/state", + "sequence": 497, + "wall_monotonic_offset_range_s": 0.00024628639221191406 + }, + { + "delay_finished_wall": 1791832195.1446521, + "delay_started_wall": 1791832193.9439216, + "genuine_upstream_elapsed_s": 0.19697441696189344, + "injected_elapsed_s": 1.2007305829320103, + "model_overlaps": [ + { + "call": 7, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.200730562210083 + } + ], + "path": "/state", + "sequence": 512, + "wall_monotonic_offset_range_s": 0.00048828125 + }, + { + "delay_finished_wall": 1791832196.679007, + "delay_started_wall": 1791832195.4773612, + "genuine_upstream_elapsed_s": 0.09738016710616648, + "injected_elapsed_s": 1.2016459580045193, + "model_overlaps": [ + { + "call": 7, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.201645851135254 + } + ], + "path": "/state", + "sequence": 514, + "wall_monotonic_offset_range_s": 0.000316619873046875 + }, + { + "delay_finished_wall": 1791832198.432412, + "delay_started_wall": 1791832197.22811, + "genuine_upstream_elapsed_s": 0.1660980840679258, + "injected_elapsed_s": 1.2043019169941545, + "model_overlaps": [ + { + "call": 7, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2043018341064453 + } + ], + "path": "/state", + "sequence": 516, + "wall_monotonic_offset_range_s": 0.0003120899200439453 + }, + { + "delay_finished_wall": 1791832200.023287, + "delay_started_wall": 1791832198.821372, + "genuine_upstream_elapsed_s": 0.10865400009788573, + "injected_elapsed_s": 1.2019149158149958, + "model_overlaps": [ + { + "call": 7, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2019150257110596 + } + ], + "path": "/state", + "sequence": 518, + "wall_monotonic_offset_range_s": 0.0007584095001220703 + }, + { + "delay_finished_wall": 1791832201.67074, + "delay_started_wall": 1791832200.4670024, + "genuine_upstream_elapsed_s": 0.11654558405280113, + "injected_elapsed_s": 1.2037374170031399, + "model_overlaps": [ + { + "call": 7, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.0191373825073242 + } + ], + "path": "/state", + "sequence": 520, + "wall_monotonic_offset_range_s": 0.00032782554626464844 + }, + { + "delay_finished_wall": 1791832204.3241541, + "delay_started_wall": 1791832203.1212535, + "genuine_upstream_elapsed_s": 0.11968966713175178, + "injected_elapsed_s": 1.2029006250668317, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2029006481170654 + } + ], + "path": "/state", + "sequence": 535, + "wall_monotonic_offset_range_s": 0.0002541542053222656 + }, + { + "delay_finished_wall": 1791832205.943559, + "delay_started_wall": 1791832204.7405307, + "genuine_upstream_elapsed_s": 0.09816970909014344, + "injected_elapsed_s": 1.203028209041804, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2030282020568848 + } + ], + "path": "/state", + "sequence": 537, + "wall_monotonic_offset_range_s": 0.0002627372741699219 + }, + { + "delay_finished_wall": 1791832207.5819252, + "delay_started_wall": 1791832206.377185, + "genuine_upstream_elapsed_s": 0.09490183321759105, + "injected_elapsed_s": 1.2047400001902133, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.204740047454834 + } + ], + "path": "/state", + "sequence": 539, + "wall_monotonic_offset_range_s": 0.0003991127014160156 + }, + { + "delay_finished_wall": 1791832209.210984, + "delay_started_wall": 1791832208.0081983, + "genuine_upstream_elapsed_s": 0.08556483290158212, + "injected_elapsed_s": 1.2027858330402523, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2027857303619385 + } + ], + "path": "/state", + "sequence": 541, + "wall_monotonic_offset_range_s": 0.0003407001495361328 + }, + { + "delay_finished_wall": 1791832210.8634849, + "delay_started_wall": 1791832209.658373, + "genuine_upstream_elapsed_s": 0.09433629107661545, + "injected_elapsed_s": 1.2051117080263793, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2051117420196533 + } + ], + "path": "/state", + "sequence": 543, + "wall_monotonic_offset_range_s": 0.0002968311309814453 + }, + { + "delay_finished_wall": 1791832212.57867, + "delay_started_wall": 1791832211.3756423, + "genuine_upstream_elapsed_s": 0.1304359589703381, + "injected_elapsed_s": 1.2030276670120656, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2030277252197266 + } + ], + "path": "/state", + "sequence": 545, + "wall_monotonic_offset_range_s": 0.0003571510314941406 + }, + { + "delay_finished_wall": 1791832214.220621, + "delay_started_wall": 1791832213.0155869, + "genuine_upstream_elapsed_s": 0.1333520410116762, + "injected_elapsed_s": 1.205034333979711, + "model_overlaps": [ + { + "call": 8, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 0.3882322311401367 + } + ], + "path": "/state", + "sequence": 547, + "wall_monotonic_offset_range_s": 0.00029397010803222656 + }, + { + "delay_finished_wall": 1791832216.812608, + "delay_started_wall": 1791832215.61119, + "genuine_upstream_elapsed_s": 0.08992187515832484, + "injected_elapsed_s": 1.2014179578982294, + "model_overlaps": [ + { + "call": 9, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2014179229736328 + } + ], + "path": "/state", + "sequence": 562, + "wall_monotonic_offset_range_s": 0.0003185272216796875 + }, + { + "delay_finished_wall": 1791832218.466902, + "delay_started_wall": 1791832217.2640164, + "genuine_upstream_elapsed_s": 0.10495808301493526, + "injected_elapsed_s": 1.2028855830430984, + "model_overlaps": [ + { + "call": 9, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.202885627746582 + } + ], + "path": "/state", + "sequence": 564, + "wall_monotonic_offset_range_s": 0.0003886222839355469 + }, + { + "delay_finished_wall": 1791832220.111368, + "delay_started_wall": 1791832218.9085712, + "genuine_upstream_elapsed_s": 0.10092691704630852, + "injected_elapsed_s": 1.2027965830639005, + "model_overlaps": [ + { + "call": 9, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2027966976165771 + } + ], + "path": "/state", + "sequence": 566, + "wall_monotonic_offset_range_s": 0.0003330707550048828 + }, + { + "delay_finished_wall": 1791832221.7597818, + "delay_started_wall": 1791832220.5593228, + "genuine_upstream_elapsed_s": 0.10268970811739564, + "injected_elapsed_s": 1.2004589578136802, + "model_overlaps": [ + { + "call": 9, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2004590034484863 + } + ], + "path": "/state", + "sequence": 568, + "wall_monotonic_offset_range_s": 0.0002837181091308594 + }, + { + "delay_finished_wall": 1791832225.4402492, + "delay_started_wall": 1791832224.2368987, + "genuine_upstream_elapsed_s": 0.12126195780001581, + "injected_elapsed_s": 1.2033504580613226, + "model_overlaps": [ + { + "call": 10, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.20335054397583 + } + ], + "path": "/state", + "sequence": 583, + "wall_monotonic_offset_range_s": 0.0022809505462646484 + }, + { + "delay_finished_wall": 1791832227.076514, + "delay_started_wall": 1791832225.872466, + "genuine_upstream_elapsed_s": 0.12050370895303786, + "injected_elapsed_s": 1.2040479579009116, + "model_overlaps": [ + { + "call": 10, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2040479183197021 + } + ], + "path": "/state", + "sequence": 585, + "wall_monotonic_offset_range_s": 0.0003409385681152344 + }, + { + "delay_finished_wall": 1791832228.698619, + "delay_started_wall": 1791832227.4964974, + "genuine_upstream_elapsed_s": 0.09993179212324321, + "injected_elapsed_s": 1.2021215830463916, + "model_overlaps": [ + { + "call": 10, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2021214962005615 + } + ], + "path": "/state", + "sequence": 587, + "wall_monotonic_offset_range_s": 0.0005545616149902344 + }, + { + "delay_finished_wall": 1791832230.342627, + "delay_started_wall": 1791832229.1391137, + "genuine_upstream_elapsed_s": 0.1068087921012193, + "injected_elapsed_s": 1.2035132918972522, + "model_overlaps": [ + { + "call": 10, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2035133838653564 + } + ], + "path": "/state", + "sequence": 589, + "wall_monotonic_offset_range_s": 0.00033020973205566406 + }, + { + "delay_finished_wall": 1791832232.0394418, + "delay_started_wall": 1791832230.8349648, + "genuine_upstream_elapsed_s": 0.09745725011453032, + "injected_elapsed_s": 1.2044771250803024, + "model_overlaps": [ + { + "call": 10, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.204477071762085 + } + ], + "path": "/state", + "sequence": 591, + "wall_monotonic_offset_range_s": 0.00030493736267089844 + }, + { + "delay_finished_wall": 1791832233.691145, + "delay_started_wall": 1791832232.4860492, + "genuine_upstream_elapsed_s": 0.11080433311872184, + "injected_elapsed_s": 1.2050957919564098, + "model_overlaps": [ + { + "call": 10, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.1795988082885742 + } + ], + "path": "/state", + "sequence": 593, + "wall_monotonic_offset_range_s": 0.0002911090850830078 + }, + { + "delay_finished_wall": 1791832245.443464, + "delay_started_wall": 1791832244.2379303, + "genuine_upstream_elapsed_s": 0.12208491703495383, + "injected_elapsed_s": 1.2055337911006063, + "model_overlaps": [ + { + "call": 11, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.205533742904663 + } + ], + "path": "/state", + "sequence": 608, + "wall_monotonic_offset_range_s": 0.0003032684326171875 + }, + { + "delay_finished_wall": 1791832247.1843069, + "delay_started_wall": 1791832245.9779978, + "genuine_upstream_elapsed_s": 0.19989608298055828, + "injected_elapsed_s": 1.20630900003016, + "model_overlaps": [ + { + "call": 11, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2063090801239014 + } + ], + "path": "/state", + "sequence": 610, + "wall_monotonic_offset_range_s": 0.0006725788116455078 + }, + { + "delay_finished_wall": 1791832248.728961, + "delay_started_wall": 1791832247.5280106, + "genuine_upstream_elapsed_s": 0.10658545885235071, + "injected_elapsed_s": 1.2009502919390798, + "model_overlaps": [ + { + "call": 11, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2009503841400146 + } + ], + "path": "/state", + "sequence": 612, + "wall_monotonic_offset_range_s": 0.00034689903259277344 + }, + { + "delay_finished_wall": 1791832250.64911, + "delay_started_wall": 1791832249.445706, + "genuine_upstream_elapsed_s": 0.274660708848387, + "injected_elapsed_s": 1.2034040831495076, + "model_overlaps": [ + { + "call": 11, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.203404188156128 + } + ], + "path": "/state", + "sequence": 614, + "wall_monotonic_offset_range_s": 0.0013229846954345703 + }, + { + "delay_finished_wall": 1791832252.131116, + "delay_started_wall": 1791832250.9289129, + "genuine_upstream_elapsed_s": 0.11848937487229705, + "injected_elapsed_s": 1.202203124994412, + "model_overlaps": [ + { + "call": 11, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2022030353546143 + } + ], + "path": "/state", + "sequence": 616, + "wall_monotonic_offset_range_s": 0.00039649009704589844 + }, + { + "delay_finished_wall": 1791832255.648841, + "delay_started_wall": 1791832254.4483225, + "genuine_upstream_elapsed_s": 0.024830207927152514, + "injected_elapsed_s": 1.200518290977925, + "model_overlaps": [ + { + "call": 12, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2005183696746826 + } + ], + "path": "/identity", + "sequence": 630, + "wall_monotonic_offset_range_s": 0.00040221214294433594 + }, + { + "delay_finished_wall": 1791832257.266772, + "delay_started_wall": 1791832256.064008, + "genuine_upstream_elapsed_s": 0.029722166946157813, + "injected_elapsed_s": 1.2027640838641673, + "model_overlaps": [ + { + "call": 12, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2027640342712402 + } + ], + "path": "/identity", + "sequence": 631, + "wall_monotonic_offset_range_s": 0.00035691261291503906 + }, + { + "delay_finished_wall": 1791832258.873054, + "delay_started_wall": 1791832257.672685, + "genuine_upstream_elapsed_s": 0.03023020806722343, + "injected_elapsed_s": 1.2003691669087857, + "model_overlaps": [ + { + "call": 12, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.200369119644165 + } + ], + "path": "/identity", + "sequence": 632, + "wall_monotonic_offset_range_s": 0.0008234977722167969 + }, + { + "delay_finished_wall": 1791832260.484274, + "delay_started_wall": 1791832259.279202, + "genuine_upstream_elapsed_s": 0.029689125018194318, + "injected_elapsed_s": 1.2050719999242574, + "model_overlaps": [ + { + "call": 12, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2050719261169434 + } + ], + "path": "/identity", + "sequence": 633, + "wall_monotonic_offset_range_s": 0.0003199577331542969 + }, + { + "delay_finished_wall": 1791832262.09807, + "delay_started_wall": 1791832260.895133, + "genuine_upstream_elapsed_s": 0.03271045791916549, + "injected_elapsed_s": 1.2029367920476943, + "model_overlaps": [ + { + "call": 12, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 0.6850109100341797 + } + ], + "path": "/identity", + "sequence": 634, + "wall_monotonic_offset_range_s": 0.00032401084899902344 + }, + { + "delay_finished_wall": 1791832264.7211719, + "delay_started_wall": 1791832263.5168095, + "genuine_upstream_elapsed_s": 0.0447540411259979, + "injected_elapsed_s": 1.2043624999932945, + "model_overlaps": [ + { + "call": 13, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.204362392425537 + } + ], + "path": "/identity", + "sequence": 648, + "wall_monotonic_offset_range_s": 0.0003616809844970703 + }, + { + "delay_finished_wall": 1791832266.3139899, + "delay_started_wall": 1791832265.1089513, + "genuine_upstream_elapsed_s": 0.028605000115931034, + "injected_elapsed_s": 1.2050385000184178, + "model_overlaps": [ + { + "call": 13, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2050385475158691 + } + ], + "path": "/identity", + "sequence": 649, + "wall_monotonic_offset_range_s": 0.0003781318664550781 + }, + { + "delay_finished_wall": 1791832267.919033, + "delay_started_wall": 1791832266.7139947, + "genuine_upstream_elapsed_s": 0.02525570895522833, + "injected_elapsed_s": 1.2050382499583066, + "model_overlaps": [ + { + "call": 13, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.20503830909729 + } + ], + "path": "/identity", + "sequence": 650, + "wall_monotonic_offset_range_s": 0.00035643577575683594 + }, + { + "delay_finished_wall": 1791832269.526366, + "delay_started_wall": 1791832268.3221338, + "genuine_upstream_elapsed_s": 0.023437874857336283, + "injected_elapsed_s": 1.2042323329951614, + "model_overlaps": [ + { + "call": 13, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "overlap_s": 1.2042322158813477 + } + ], + "path": "/identity", + "sequence": 651, + "wall_monotonic_offset_range_s": 0.0003230571746826172 + }, + { + "delay_finished_wall": 1791832273.146421, + "delay_started_wall": 1791832271.9417524, + "genuine_upstream_elapsed_s": 0.026460542110726237, + "injected_elapsed_s": 1.2046686250250787, + "model_overlaps": [], + "path": "/identity", + "sequence": 661, + "wall_monotonic_offset_range_s": 0.0002894401550292969 + }, + { + "delay_finished_wall": 1791832274.7546492, + "delay_started_wall": 1791832273.5496144, + "genuine_upstream_elapsed_s": 0.02605204191058874, + "injected_elapsed_s": 1.2050346250180155, + "model_overlaps": [], + "path": "/identity", + "sequence": 662, + "wall_monotonic_offset_range_s": 0.00043654441833496094 + }, + { + "delay_finished_wall": 1791832276.3647099, + "delay_started_wall": 1791832275.159664, + "genuine_upstream_elapsed_s": 0.024871707893908024, + "injected_elapsed_s": 1.2050458330195397, + "model_overlaps": [], + "path": "/identity", + "sequence": 663, + "wall_monotonic_offset_range_s": 0.0003113746643066406 + }, + { + "delay_finished_wall": 1791832277.976248, + "delay_started_wall": 1791832276.7734878, + "genuine_upstream_elapsed_s": 0.027691290946677327, + "injected_elapsed_s": 1.2027602919843048, + "model_overlaps": [], + "path": "/identity", + "sequence": 664, + "wall_monotonic_offset_range_s": 0.00038623809814453125 + }, + { + "delay_finished_wall": 1791832279.578651, + "delay_started_wall": 1791832278.3785973, + "genuine_upstream_elapsed_s": 0.024854457937180996, + "injected_elapsed_s": 1.2000537081621587, + "model_overlaps": [], + "path": "/identity", + "sequence": 665, + "wall_monotonic_offset_range_s": 0.00032401084899902344 + }, + { + "delay_finished_wall": 1791832281.194527, + "delay_started_wall": 1791832279.9924984, + "genuine_upstream_elapsed_s": 0.027741792146116495, + "injected_elapsed_s": 1.2020284999161959, + "model_overlaps": [], + "path": "/identity", + "sequence": 666, + "wall_monotonic_offset_range_s": 0.0002951622009277344 + }, + { + "delay_finished_wall": 1791832282.808212, + "delay_started_wall": 1791832281.6038282, + "genuine_upstream_elapsed_s": 0.033845792058855295, + "injected_elapsed_s": 1.204383792122826, + "model_overlaps": [], + "path": "/identity", + "sequence": 667, + "wall_monotonic_offset_range_s": 0.00036454200744628906 + }, + { + "delay_finished_wall": 1791832284.4161339, + "delay_started_wall": 1791832283.211826, + "genuine_upstream_elapsed_s": 0.03321212506853044, + "injected_elapsed_s": 1.204307832987979, + "model_overlaps": [], + "path": "/identity", + "sequence": 668, + "wall_monotonic_offset_range_s": 0.00027680397033691406 + }, + { + "delay_finished_wall": 1791832286.023186, + "delay_started_wall": 1791832284.8185112, + "genuine_upstream_elapsed_s": 0.031950625125318766, + "injected_elapsed_s": 1.2046747910790145, + "model_overlaps": [], + "path": "/identity", + "sequence": 669, + "wall_monotonic_offset_range_s": 0.0003528594970703125 + }, + { + "delay_finished_wall": 1791832287.63012, + "delay_started_wall": 1791832286.425594, + "genuine_upstream_elapsed_s": 0.025775166926905513, + "injected_elapsed_s": 1.204525833018124, + "model_overlaps": [], + "path": "/identity", + "sequence": 670, + "wall_monotonic_offset_range_s": 0.0003333091735839844 + }, + { + "delay_finished_wall": 1791832289.234438, + "delay_started_wall": 1791832288.0331938, + "genuine_upstream_elapsed_s": 0.023571416968479753, + "injected_elapsed_s": 1.2012440001126379, + "model_overlaps": [], + "path": "/identity", + "sequence": 671, + "wall_monotonic_offset_range_s": 0.00030875205993652344 + }, + { + "delay_finished_wall": 1791832290.857811, + "delay_started_wall": 1791832289.6537929, + "genuine_upstream_elapsed_s": 0.03697133297100663, + "injected_elapsed_s": 1.2040182081982493, + "model_overlaps": [], + "path": "/identity", + "sequence": 672, + "wall_monotonic_offset_range_s": 0.0004439353942871094 + }, + { + "delay_finished_wall": 1791832292.550731, + "delay_started_wall": 1791832291.347129, + "genuine_upstream_elapsed_s": 0.10883808392100036, + "injected_elapsed_s": 1.203601917019114, + "model_overlaps": [], + "path": "/identity", + "sequence": 673, + "wall_monotonic_offset_range_s": 0.0004405975341796875 + }, + { + "delay_finished_wall": 1791832294.067949, + "delay_started_wall": 1791832292.8650882, + "genuine_upstream_elapsed_s": 0.024111542152240872, + "injected_elapsed_s": 1.2028609160333872, + "model_overlaps": [], + "path": "/identity", + "sequence": 674, + "wall_monotonic_offset_range_s": 0.00026679039001464844 + }, + { + "delay_finished_wall": 1791832295.688381, + "delay_started_wall": 1791832294.487985, + "genuine_upstream_elapsed_s": 0.035032999934628606, + "injected_elapsed_s": 1.2003959577996284, + "model_overlaps": [], + "path": "/identity", + "sequence": 675, + "wall_monotonic_offset_range_s": 0.00037360191345214844 + }, + { + "delay_finished_wall": 1791832297.2962801, + "delay_started_wall": 1791832296.0939546, + "genuine_upstream_elapsed_s": 0.03289383416995406, + "injected_elapsed_s": 1.2023255832027644, + "model_overlaps": [], + "path": "/identity", + "sequence": 676, + "wall_monotonic_offset_range_s": 0.0004773139953613281 + }, + { + "delay_finished_wall": 1791832298.916022, + "delay_started_wall": 1791832297.7152755, + "genuine_upstream_elapsed_s": 0.04468645807355642, + "injected_elapsed_s": 1.2007464580237865, + "model_overlaps": [], + "path": "/identity", + "sequence": 677, + "wall_monotonic_offset_range_s": 0.000461578369140625 + }, + { + "delay_finished_wall": 1791832300.50497, + "delay_started_wall": 1791832299.302549, + "genuine_upstream_elapsed_s": 0.024397874949499965, + "injected_elapsed_s": 1.2024210828822106, + "model_overlaps": [], + "path": "/identity", + "sequence": 678, + "wall_monotonic_offset_range_s": 0.0003190040588378906 + }, + { + "delay_finished_wall": 1791832302.12138, + "delay_started_wall": 1791832300.916171, + "genuine_upstream_elapsed_s": 0.0270841249730438, + "injected_elapsed_s": 1.205209042178467, + "model_overlaps": [], + "path": "/identity", + "sequence": 679, + "wall_monotonic_offset_range_s": 0.00034427642822265625 + }, + { + "delay_finished_wall": 1791832303.727721, + "delay_started_wall": 1791832302.5242665, + "genuine_upstream_elapsed_s": 0.025939082959666848, + "injected_elapsed_s": 1.2034545419737697, + "model_overlaps": [], + "path": "/identity", + "sequence": 680, + "wall_monotonic_offset_range_s": 0.00041365623474121094 + }, + { + "delay_finished_wall": 1791832305.343824, + "delay_started_wall": 1791832304.138787, + "genuine_upstream_elapsed_s": 0.03202695888467133, + "injected_elapsed_s": 1.2050369170028716, + "model_overlaps": [], + "path": "/identity", + "sequence": 681, + "wall_monotonic_offset_range_s": 0.00038743019104003906 + }, + { + "delay_finished_wall": 1791832306.948216, + "delay_started_wall": 1791832305.7446063, + "genuine_upstream_elapsed_s": 0.026920791948214173, + "injected_elapsed_s": 1.2036097499076277, + "model_overlaps": [], + "path": "/identity", + "sequence": 682, + "wall_monotonic_offset_range_s": 0.00027108192443847656 + }, + { + "delay_finished_wall": 1791832308.563554, + "delay_started_wall": 1791832307.3598785, + "genuine_upstream_elapsed_s": 0.031036084052175283, + "injected_elapsed_s": 1.2036756249144673, + "model_overlaps": [], + "path": "/identity", + "sequence": 683, + "wall_monotonic_offset_range_s": 0.0003514289855957031 + }, + { + "delay_finished_wall": 1791832310.165736, + "delay_started_wall": 1791832308.9651709, + "genuine_upstream_elapsed_s": 0.024415332823991776, + "injected_elapsed_s": 1.200565000064671, + "model_overlaps": [], + "path": "/identity", + "sequence": 684, + "wall_monotonic_offset_range_s": 0.0003516674041748047 + }, + { + "delay_finished_wall": 1791832311.7797499, + "delay_started_wall": 1791832310.576029, + "genuine_upstream_elapsed_s": 0.02451429096981883, + "injected_elapsed_s": 1.203720834106207, + "model_overlaps": [], + "path": "/identity", + "sequence": 685, + "wall_monotonic_offset_range_s": 0.00028967857360839844 + } + ], + "jobs": [ + { + "both_paths_overlap_model": true, + "final_status": "completed", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "path_coverage": { + "/identity": { + "healthy_injected_reads_overlapping_model": 30, + "model_calls": [ + 1, + 2, + 3, + 4, + 5, + 6 + ], + "relay_sequences": [ + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 42, + 43, + 44, + 45, + 59, + 60, + 61, + 62, + 63, + 64, + 78, + 79, + 80, + 81, + 96, + 97, + 98, + 99, + 113, + 114, + 115, + 116, + 117 + ] + }, + "/state": { + "healthy_injected_reads_overlapping_model": 33, + "model_calls": [ + 8, + 9, + 10, + 11, + 12, + 13, + 14 + ], + "relay_sequences": [ + 211, + 213, + 215, + 217, + 219, + 234, + 236, + 238, + 240, + 242, + 257, + 259, + 261, + 263, + 265, + 280, + 282, + 284, + 286, + 288, + 303, + 305, + 307, + 309, + 324, + 326, + 328, + 330, + 345, + 347, + 349, + 351, + 353 + ] + } + } + }, + { + "both_paths_overlap_model": true, + "final_status": "completed", + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "path_coverage": { + "/identity": { + "healthy_injected_reads_overlapping_model": 9, + "model_calls": [ + 12, + 13 + ], + "relay_sequences": [ + 630, + 631, + 632, + 633, + 634, + 648, + 649, + 650, + 651 + ] + }, + "/state": { + "healthy_injected_reads_overlapping_model": 56, + "model_calls": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11 + ], + "relay_sequences": [ + 373, + 375, + 377, + 379, + 381, + 383, + 398, + 400, + 402, + 404, + 419, + 421, + 423, + 425, + 427, + 429, + 445, + 447, + 449, + 451, + 466, + 468, + 470, + 472, + 489, + 491, + 493, + 495, + 497, + 512, + 514, + 516, + 518, + 520, + 535, + 537, + 539, + 541, + 543, + 545, + 547, + 562, + 564, + 566, + 568, + 583, + 585, + 587, + 589, + 591, + 593, + 608, + 610, + 612, + 614, + 616 + ] + } + } + } + ], + "limits": [ + "This supplementary audit was authored after source freeze; it does not replace the frozen qualifier.", + "Model intervals use provider started_at plus monotonic elapsed_s; delay windows use recorded send time minus measured wait.", + "Wall/monotonic offset spread is retained to expose timing precision limits.", + "Overlap establishes exposure during model planning; it does not prove the cause of the historical SIM11 timeout." + ], + "measured_control_events": [ + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 66, + "time": 1791832016.9072719 + }, + { + "intent": "", + "kind": "disconnect", + "outcome": "Transport only; no body reset or replay", + "seq": 67, + "time": 1791832033.026589 + }, + { + "intent": "", + "kind": "reconnect", + "outcome": "No command sent or replayed", + "seq": 68, + "time": 1791832051.478241 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 71, + "time": 1791832069.452853 + } + ], + "measured_cutoff_event_seq": 284, + "measured_cutoff_wall": 1791832270.9544058, + "measured_unexpected_halts": [], + "model_calls": [ + { + "call": 1, + "elapsed_s": 8.888634625123814, + "finished_wall": 1791832146.4737008, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832137.585066 + }, + { + "call": 2, + "elapsed_s": 7.371869457885623, + "finished_wall": 1791832154.8865297, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832147.5146601 + }, + { + "call": 3, + "elapsed_s": 8.43007241608575, + "finished_wall": 1791832164.3083704, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832155.878298 + }, + { + "call": 4, + "elapsed_s": 6.128192666918039, + "finished_wall": 1791832172.0759635, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832165.9477708 + }, + { + "call": 5, + "elapsed_s": 7.361145915929228, + "finished_wall": 1791832180.94438, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832173.583234 + }, + { + "call": 6, + "elapsed_s": 8.747338708955795, + "finished_wall": 1791832191.5603619, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832182.813023 + }, + { + "call": 7, + "elapsed_s": 8.426699750125408, + "finished_wall": 1791832201.4861398, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832193.0594401 + }, + { + "call": 8, + "elapsed_s": 10.435514916898683, + "finished_wall": 1791832213.403819, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832202.9683042 + }, + { + "call": 9, + "elapsed_s": 6.884068874875084, + "finished_wall": 1791832221.844614, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832214.960545 + }, + { + "call": 10, + "elapsed_s": 10.11140904109925, + "finished_wall": 1791832233.665648, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832223.554239 + }, + { + "call": 11, + "elapsed_s": 8.000156666850671, + "finished_wall": 1791832252.1834106, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832244.183254 + }, + { + "call": 12, + "elapsed_s": 7.8165919170714915, + "finished_wall": 1791832261.580144, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832253.763552 + }, + { + "call": 13, + "elapsed_s": 6.307222834089771, + "finished_wall": 1791832269.6300058, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832263.322783 + }, + { + "call": 1, + "elapsed_s": 10.586642124922946, + "finished_wall": 1791831978.080672, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791831967.49403 + }, + { + "call": 2, + "elapsed_s": 6.294448249973357, + "finished_wall": 1791831984.8089693, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791831978.5145211 + }, + { + "call": 3, + "elapsed_s": 9.542235415894538, + "finished_wall": 1791831994.7687895, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791831985.2265542 + }, + { + "call": 4, + "elapsed_s": 6.4462542498949915, + "finished_wall": 1791832001.6932354, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791831995.2469811 + }, + { + "call": 5, + "elapsed_s": 4.764401749940589, + "finished_wall": 1791832007.2908835, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832002.5264819 + }, + { + "call": 6, + "elapsed_s": 8.140898042125627, + "finished_wall": 1791832016.244656, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832008.103758 + }, + { + "call": 7, + "elapsed_s": 8.440171458059922, + "finished_wall": 1791832078.4239416, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832069.9837701 + }, + { + "call": 8, + "elapsed_s": 8.132765874965116, + "finished_wall": 1791832087.3751018, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832079.242336 + }, + { + "call": 9, + "elapsed_s": 7.226740124868229, + "finished_wall": 1791832095.51877, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832088.2920299 + }, + { + "call": 10, + "elapsed_s": 7.37742824992165, + "finished_wall": 1791832103.8689063, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832096.491478 + }, + { + "call": 11, + "elapsed_s": 8.136500083142892, + "finished_wall": 1791832112.922722, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832104.786222 + }, + { + "call": 12, + "elapsed_s": 6.762274875072762, + "finished_wall": 1791832120.609567, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832113.847292 + }, + { + "call": 13, + "elapsed_s": 6.597302458947524, + "finished_wall": 1791832128.1300006, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832121.5326982 + }, + { + "call": 14, + "elapsed_s": 7.210898624965921, + "finished_wall": 1791832136.3729286, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "outcome": "decision", + "process_exit_observed": true, + "started_wall": 1791832129.16203 + } + ], + "passed": true, + "posttrial_handoff_control_events": [ + { + "intent": "", + "kind": "halt", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "seq": 285, + "time": 1791832363.512448 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 287, + "time": 1791832405.32576 + } + ], + "proof_count": 83, + "proof_events_unique": true, + "strengthened_admission_bindings": [ + { + "command_id": "command-accc773656494216b8991d02cab9b603", + "passed": true + }, + { + "command_id": "command-85d8b423ec7c4a53bb1987fb1a62bdf6", + "passed": true + }, + { + "command_id": "command-b93638f4643f4d35b5cde6fde06ed807", + "passed": true + }, + { + "command_id": "command-a44e60b9d8c24bdaa49e479c4b0ef780", + "passed": true + }, + { + "command_id": "command-79dcaf5d02e048449c681748167c446a", + "passed": true + }, + { + "command_id": "command-069d2460341443e69bbcd0b5f39b91f2", + "passed": true + }, + { + "command_id": "command-20fde1f33f97495cade8fce105d16ae4", + "passed": true + }, + { + "command_id": "command-107896fd188d4b72be6d2a6debd2ba0c", + "passed": true + }, + { + "command_id": "command-1bc0e6eb4e064fe1b7b66dfbf750968a", + "passed": true + }, + { + "command_id": "command-70f9b8c19b59452693061139bda6c71b", + "passed": true + }, + { + "command_id": "command-16683e1227d84968879714ddd0556488", + "passed": true + }, + { + "command_id": "command-1fb29328cf0a4c7e800022c949b77f7a", + "passed": true + }, + { + "command_id": "command-f1e1760598fa4cd89339d2d2091f8d1a", + "passed": true + }, + { + "command_id": "command-1a4dc2ad405846ecbdfaed4b2d825f3e", + "passed": true + }, + { + "command_id": "command-55007cfbc1d7410d92d582397af2555b", + "passed": true + }, + { + "command_id": "command-ee7b8dee4444416c98f550c3e5a7c01f", + "passed": true + }, + { + "command_id": "command-77279a97fd4a4ff2aee720c0b7a46c40", + "passed": true + }, + { + "command_id": "command-3211cda07dda4c5c8dd2cc700fdaad98", + "passed": true + }, + { + "command_id": "command-e52c9ef5005a410fb80600b4ad7f947a", + "passed": true + }, + { + "command_id": "command-97245de4fa73480c87503051fc90a8a9", + "passed": true + }, + { + "command_id": "command-5457db2a5bcf4cd3ba1b306e5182ede1", + "passed": true + }, + { + "command_id": "command-d4045c653de542aea3cd3f100603a0dd", + "passed": true + }, + { + "command_id": "command-127c700baa7e4224a9f4d4d1654380fc", + "passed": true + }, + { + "command_id": "command-12d35e26325248a5881960ffcbd97a6f", + "passed": true + }, + { + "command_id": "command-fb95362e732c42c793f505dd7673ea38", + "passed": true + }, + { + "command_id": "command-5365431f8f094a738c04898a662544f5", + "passed": true + }, + { + "command_id": "command-1ca372f4627f44ca820de2e16d58a939", + "passed": true + }, + { + "command_id": "command-8074e0e06a9d46f68b944f8a3949a7d5", + "passed": true + }, + { + "command_id": "command-58c9af8646c94c0b9e453fb32e4622f1", + "passed": true + }, + { + "command_id": "command-706d43a193d349b0abb2a1a2e213670d", + "passed": true + }, + { + "command_id": "command-f01b770705654cf0b746795ef56a5e51", + "passed": true + }, + { + "command_id": "command-01c5b18c0f4848ab83a3a506c6164bd7", + "passed": true + }, + { + "command_id": "command-455161d907e44cc2a0c00c4713404a47", + "passed": true + }, + { + "command_id": "command-c085b10cc40c44539a927aa06c7a4339", + "passed": true + }, + { + "command_id": "command-7c91d1e39cc047049a3c4453e8b35923", + "passed": true + }, + { + "command_id": "command-02d0ee9bb015446e974769918792b02f", + "passed": true + }, + { + "command_id": "command-15941d3c6bb74b9a8916ec43a2fe0e1f", + "passed": true + }, + { + "command_id": "command-f615d864a28e4af88536d43c26a06e7c", + "passed": true + }, + { + "command_id": "command-d11cdb7e2538490784e2e8a2425572b7", + "passed": true + }, + { + "command_id": "command-cdf687139f2e43cab73f3afa8053018d", + "passed": true + }, + { + "command_id": "command-6db96f5e11fc4baebb1607c174a5898f", + "passed": true + }, + { + "command_id": "command-000b1a6737a14842a6b79c23e0a5ca92", + "passed": true + }, + { + "command_id": "command-72de9f33d999438198fc8b176ee89f6d", + "passed": true + }, + { + "command_id": "command-155208c1dd824b43a411bf0d9f879dc9", + "passed": true + }, + { + "command_id": "command-debb71a7f11f45b1b2093c2da309bb26", + "passed": true + }, + { + "command_id": "command-8fe46178c8564d2b8af7b48dd51ceb69", + "passed": true + }, + { + "command_id": "command-1fdd5229845447258a05de892ad29824", + "passed": true + }, + { + "command_id": "command-7e14ebef15814262bf16ee0a417469ea", + "passed": true + }, + { + "command_id": "command-a6931c3bf281480fb0dd090384b20178", + "passed": true + }, + { + "command_id": "command-5a3fb52b0a34409da8c43ce284d498f8", + "passed": true + }, + { + "command_id": "command-6371aeae6cdb432c851d1a095b64f85e", + "passed": true + }, + { + "command_id": "command-020d09130bdf4e10b2e30aed41ee93fd", + "passed": true + }, + { + "command_id": "command-185d0f856fbc4990be03fb5a4d680444", + "passed": true + }, + { + "command_id": "command-271d260ff225464ca18069efe6b89a3f", + "passed": true + }, + { + "command_id": "command-8bc103965b3b4b548f0ab2a4f6529315", + "passed": true + }, + { + "command_id": "command-52b299434a2a4354ae1a0c27f7afda73", + "passed": true + }, + { + "command_id": "command-b50b8172d84a4c2ca69c1b13799c0cd3", + "passed": true + }, + { + "command_id": "command-55878c3e64e7438786d949269a3ca632", + "passed": true + }, + { + "command_id": "command-3c4907bffd1043b7841f4f7a81e14c2e", + "passed": true + }, + { + "command_id": "command-a3d1bf0c87ed4d748bcfddf5acbcdbc8", + "passed": true + }, + { + "command_id": "command-c2820c925b7b43e48231001f02731ba6", + "passed": true + }, + { + "command_id": "command-8a9753d1c9604e3094a1478b421465f0", + "passed": true + }, + { + "command_id": "command-c21a6cfb3b154f799bb67d2695787ef8", + "passed": true + }, + { + "command_id": "command-6d7f70c0e5b14d6494d7eb02930b9d66", + "passed": true + }, + { + "command_id": "command-7d2c4e20bc454be996d99dc7edeea280", + "passed": true + }, + { + "command_id": "command-3352c7fd2fb14ffcaf69c1b27c6c53d9", + "passed": true + }, + { + "command_id": "command-896ce409c627486b9104d8d7415cefd3", + "passed": true + }, + { + "command_id": "command-1875feda0eaf4c058b771536779ccd62", + "passed": true + }, + { + "command_id": "command-844f1072836b419fa30a4c4f4a8697f1", + "passed": true + }, + { + "command_id": "command-7d6435405eaf4824b97108643856b361", + "passed": true + }, + { + "command_id": "command-7408e15ea38244a592d2800662c2b3c8", + "passed": true + }, + { + "command_id": "command-3cdfb3055b044aeeb059afc8731dac9c", + "passed": true + }, + { + "command_id": "command-59ebc979948340c1bceb69a8e01ac2aa", + "passed": true + }, + { + "command_id": "command-7761812fccc94a208c8f3e2dc26c28ec", + "passed": true + }, + { + "command_id": "command-2e37938e1f6a4a63b928572e1cf365af", + "passed": true + }, + { + "command_id": "command-e6cd1a382110409ab381d5fd4c4cc3b0", + "passed": true + }, + { + "command_id": "command-34752e8b30564841a21d087d561a5930", + "passed": true + }, + { + "command_id": "command-c966c8b893db42eb84c5b0616acca391", + "passed": true + }, + { + "command_id": "command-c8ad5019377e44159aef0ee22364b18f", + "passed": true + }, + { + "command_id": "command-10a03ed616ea4d96a26e96ae0973c32b", + "passed": true + }, + { + "command_id": "command-6b6e57d717d249c7811ef4708c6b8667", + "passed": true + }, + { + "command_id": "command-594d54e2793d428bbb42f80713e94ed3", + "passed": true + }, + { + "command_id": "command-bd5377590da74532a8333ac5ca2a72b3", + "passed": true + } + ], + "trace_scope_note": "Append-only journals include later paused handoff administration. Events through the retained final-public snapshot are measured; subsequent events are reported separately.", + "unused_admission_proof_events": [] +} diff --git a/jevdual/results/workbench-health-20261012/review-audit.py b/jevdual/results/workbench-health-20261012/review-audit.py new file mode 100644 index 0000000..0164084 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/review-audit.py @@ -0,0 +1,176 @@ +"""Supplemental post-run, read-only evidence audit; writes only its own result. + +This audit is not the preregistered frozen qualifier. It checks causal coverage +that the frozen qualifier's aggregate delay counter alone cannot establish. +""" +import hashlib +import json +import os +import sys +from pathlib import Path + +sys.dont_write_bytecode = True +ROOT = Path(__file__).resolve().parent +SOURCE_DIR = ROOT.parents[1] +REPO = SOURCE_DIR.parents[1] +sys.path.insert(0, str(SOURCE_DIR)) + + +def read(path): + return json.loads(path.read_bytes()) + + +def sha(content): + return hashlib.sha256(content).hexdigest() + + +def overlap(left_start, left_end, right_start, right_end): + return max(0.0, min(left_end, right_end) - max(left_start, right_start)) + + +def audit(): + frozen = read(ROOT / 'source-freeze.json') + unchanged = all(sha((REPO / name).read_bytes()) == digest for name, digest in frozen['files'].items()) + if not unchanged: + raise RuntimeError('Frozen evidence helpers changed; do not run a changed scorer against this trial') + import score_workbench_health as scorer + # Invoke only the helper functions with no persistence, dispatch or native-state access. + process = read(ROOT / 'process-ledger.json') + final = read(ROOT / 'final-public.json') + identity = read(ROOT / 'identity.json') + records, relay_valid = scorer.relay_records(ROOT) + final_sequence = max(event['seq'] for event in final['activity']) + final_wall = max(event['time'] for event in final['activity']) + all_events = [read(path) for path in sorted((ROOT / 'operator' / 'events').glob('*.json'))] + events = [event for event in all_events if event['seq'] <= final_sequence] + posttrial_events = [event for event in all_events if event['seq'] > final_sequence] + measured_request_count = read(ROOT / 'health-relay' / 'cleanup.json')['request_count'] + records = [record for record in records if record['request']['sequence'] <= measured_request_count] + proofs = scorer.fresh_admissions(records, events, identity) + measured_calls = {job['job_id']: job['call_count'] for job in final['jobs']} + calls = [] + for directory in sorted((ROOT / 'operator' / 'calls').iterdir()): + if not directory.is_dir(): + continue + reservation = read(directory / 'reservation.json') + result = read(directory / 'outcome.json') + if reservation['job_id'] not in measured_calls or reservation['call'] > measured_calls[reservation['job_id']]: + continue + calls.append({'job_id': reservation['job_id'], 'call': reservation['call'], + 'started_wall': result['started_at'], 'finished_wall': result['started_at'] + result['elapsed_s'], + 'elapsed_s': result['elapsed_s'], 'outcome': result['outcome'], + 'process_exit_observed': result['process_exit_observed']}) + delays = [] + for record in records: + request, upstream, result = record['request'], record['upstream'], record['result'] + if not (request['tagged_health'] and request['injected_delay_s'] == 1.2 and upstream and + upstream['http_status'] == 200 and result.get('completed') and result.get('delivered') and + not result.get('delay_interrupted') and result.get('delay_elapsed_s', 0) >= 1.2): + continue + remote = json.loads(record['content']) + healthy = remote == identity if request['path'] == '/identity' else remote.get('identity') == identity and remote.get('state') == 'ready' + if not healthy: + continue + # The delay ends immediately before send/record. Subtracting the measured + # monotonic duration maps the real wait into the recorded wall interval. + ended = result['finished_wall'] + started = ended - result['delay_elapsed_s'] + matches = [{'job_id': call['job_id'], 'call': call['call'], + 'overlap_s': overlap(started, ended, call['started_wall'], call['finished_wall'])} + for call in calls if overlap(started, ended, call['started_wall'], call['finished_wall']) > 0] + offsets = [request['started_wall'] - request['started_monotonic'], + upstream['wall'] - upstream['finished_monotonic'], + result['finished_wall'] - result['finished_monotonic']] + delays.append({'sequence': request['sequence'], 'path': request['path'], + 'delay_started_wall': started, 'delay_finished_wall': ended, + 'injected_elapsed_s': result['delay_elapsed_s'], + 'genuine_upstream_elapsed_s': upstream['upstream_elapsed_s'], + 'wall_monotonic_offset_range_s': max(offsets) - min(offsets), 'model_overlaps': matches}) + jobs = [] + for job in final['jobs']: + per_path = {} + for path in ('/identity', '/state'): + matches = [delay for delay in delays if delay['path'] == path and + any(match['job_id'] == job['job_id'] for match in delay['model_overlaps'])] + per_path[path] = {'healthy_injected_reads_overlapping_model': len(matches), + 'relay_sequences': [delay['sequence'] for delay in matches], + 'model_calls': sorted({match['call'] for delay in matches for match in delay['model_overlaps'] + if match['job_id'] == job['job_id']})} + jobs.append({'job_id': job['job_id'], 'label': job['label'], 'final_status': job['status'], + 'path_coverage': per_path, 'both_paths_overlap_model': all(value['healthy_injected_reads_overlapping_model'] for value in per_path.values())}) + selected_proofs = [proof['proof_event'] for proof in proofs] + selected_reads = [proof['state_read_sequence'] for proof in proofs] + admission_events = [event for event in events if event['kind'] == 'admission_proof'] + unique_proofs = (bool(proofs) and all(proof['passed'] for proof in proofs) and + len(selected_proofs) == len(set(selected_proofs)) and len(selected_reads) == len(set(selected_reads))) + # Tighten the trace check beyond merely finding a matching ready digest: + # each chosen full-state reply must complete, reach its caller, and bind the + # relay request/lease/command identity to the same immutable body. + by_sequence = {record['request']['sequence']: record for record in records} + strengthened = [] + expected_headers = {'x-body-id': identity['body_id'], 'x-adapter-id': identity['adapter_id'], + 'x-source-sha256': identity['source_sha256']} + for proof in proofs: + state = by_sequence.get(proof['state_read_sequence']) + lease = by_sequence.get(proof['lease_sequence']) + command = by_sequence.get(proof['command_sequence']) + ok = bool(proof['passed'] and state and lease and command) + if ok: + command_payload, lease_payload = json.loads(command['payload']), json.loads(lease['payload']) + state_headers = {key.lower(): value for key, value in state['request']['headers'].items()} + command_headers = {key.lower(): value for key, value in command['request']['headers'].items()} + lease_result = json.loads(lease['content']) + ok = bool(state['result'].get('completed') and state['result'].get('delivered') and + lease['result'].get('completed') and lease['result'].get('delivered') and + all(state_headers.get(key) == command_headers.get(key) == value for key, value in expected_headers.items()) and + all(command_payload[key] == identity[key] for key in ('body_id', 'adapter_id', 'source_sha256', 'epoch')) and + command_payload['client_id'] == lease_payload['client_id'] == lease_result['client_id']) + strengthened.append({'command_id': proof['command_id'], 'passed': ok}) + cancelled = [call for call in calls if call['outcome'] == 'cancelled'] + control_kinds = ('pause', 'disconnect', 'reconnect', 'halt', 'resume') + control_events = [event for event in events if event['kind'] in control_kinds] + posttrial_controls = [event for event in posttrial_events if event['kind'] in control_kinds] + steps = {step['name']:step for step in read(ROOT / 'ui-ledger.json')['steps']} + expected_unknown_reason = steps['uncertain']['state']['recovery']['reason'] + unexpected_halts = [event for event in control_events if event['kind'] == 'halt' and event['outcome'] != expected_unknown_reason] + checking = next((step for step in read(ROOT / 'ui-ledger.json')['steps'] if step['name'] == 'healthy_checking'), None) + checking_same_identity = bool(checking and checking['state']['app_id'] == final['app_id'] and + checking['state']['connection']['body_id'] == identity['body_id'] and + checking['state']['active_job_id'] in {job['job_id'] for job in final['jobs']}) + coverage = len(jobs) == 2 and all(job['both_paths_overlap_model'] for job in jobs) + result = {'audit_kind': 'supplemental_post_run_read_only_review', 'frozen_source_commit': frozen['commit'], + 'audit_source_sha256': sha(Path(__file__).read_bytes()), 'frozen_source_unchanged': unchanged, + 'passed': bool(coverage and not cancelled and unique_proofs and all(value['passed'] for value in strengthened) and + checking_same_identity and relay_valid and process['owned_processes_exited'] and not unexpected_halts), + 'both_jobs_both_paths_overlap_actual_model': coverage, 'jobs': jobs, + 'healthy_delayed_reads': delays, 'model_calls': calls, 'cancelled_calls': cancelled, + 'proof_count': len(proofs), 'proof_events_unique': unique_proofs, + 'strengthened_admission_bindings': strengthened, + 'unused_admission_proof_events': [event['seq'] for event in admission_events if event['seq'] not in selected_proofs], + 'healthy_checking_same_application_and_body': checking_same_identity, + 'measured_control_events': control_events, 'measured_unexpected_halts': unexpected_halts, + 'posttrial_handoff_control_events': posttrial_controls, + 'measured_cutoff_event_seq': final_sequence, 'measured_cutoff_wall': final_wall, + 'trace_scope_note': 'Append-only journals include later paused handoff administration. Events through the retained final-public snapshot are measured; subsequent events are reported separately.', + 'limits': ['This supplementary audit was authored after source freeze; it does not replace the frozen qualifier.', + 'Model intervals use provider started_at plus monotonic elapsed_s; delay windows use recorded send time minus measured wait.', + 'Wall/monotonic offset spread is retained to expose timing precision limits.', + 'Overlap establishes exposure during model planning; it does not prove the cause of the historical SIM11 timeout.']} + destination = ROOT / 'review-audit.json' + original = ROOT / 'review-audit-unscoped.json' + if destination.exists() and not original.exists(): + original_fd = os.open(original, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600) + with os.fdopen(original_fd, 'wb') as output: + output.write(destination.read_bytes()) + staged = ROOT / 'review-audit-scoped.json' + fd = os.open(staged, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o600) + with os.fdopen(fd, 'w') as output: + json.dump(result, output, sort_keys=True, indent=2, allow_nan=False) + output.write('\n') + os.replace(staged, destination) + print(json.dumps({key: result[key] for key in ('passed', 'both_jobs_both_paths_overlap_actual_model', + 'proof_count', 'proof_events_unique', 'healthy_checking_same_application_and_body')})) + + +if __name__ == '__main__': + audit() diff --git a/jevdual/results/workbench-health-20261012/source-freeze.json b/jevdual/results/workbench-health-20261012/source-freeze.json new file mode 100644 index 0000000..e88bb54 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/source-freeze.json @@ -0,0 +1,33 @@ +{ + "commit": "37ebfdb3054b977149d7b60b46254ddd852b749a", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/body_client.py": "40388c238192d923888c9852b363314801dcf65c7266aadc8c377d3810aeb33d", + "tools/physical-workcell/body_network.py": "95fd21597bcdd9e982a698859cd1219ae4522062e332c50fea233629dbfb343c", + "tools/physical-workcell/body_protocol.py": "1d24066768105c28b5cf6f4d46458e4e1b9d86cbde45ed6ce466b30da7ff974f", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "40fe73e01d97ee1e5cfdb813650b11edadbea29534784ab10cc1ef4e7f4d5418", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/network_bench_operator.html": "75ca82328967ea65cf3c3f7f7d60c109a4201099ae3a24d3834e0ecf19b4d897", + "tools/physical-workcell/network_bench_operator.py": "9079729ea4e8b56ccdf2a6d187b0c5d79a23a33688748fe55d92d10922500b40", + "tools/physical-workcell/network_fault_proxy.py": "44b0b52f7c213c0f5f93b8997ba72be4e750bc9f1a4f7f6d7b2a8c4f1233b595", + "tools/physical-workcell/network_operator_runtime.py": "001511aa25fef7f1078bce444b9a0f7d0a09d26d4e5fba9e9819ee3ed98cb6b9", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/qualify_network_body.py": "2800cf38afd21b86f8b035c46e994b6f96d966db4ccba1bba703e9e1a22f7c2b", + "tools/physical-workcell/qualify_network_workbench.py": "e5c72acda2ea8fae3658faba907448765df2f1e34e05749fbe85faed28ef003b", + "tools/physical-workcell/qualify_workbench_health.py": "2b73c4b57319dc24afcde062b2782e572fd6abf8d6d3db558133eefc60271e08", + "tools/physical-workcell/record_network_workbench.py": "7604865caa309c7002c01a2384da84eb62d8b22dcd63ab256618c536bc5fedc4", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_network_body.py": "d1f7150a5ff23775664b5656fd21379502ba71175487fd7aadb2b51bffcdaf8c", + "tools/physical-workcell/score_network_workbench.py": "67a53586081fe425bd14b0a0fbbf3d7f0fc1e66daccda06765bb88f3d9d4d84c", + "tools/physical-workcell/score_workbench_health.py": "0b5f7a047004f9d566451b88a2c3101403641b0fd0a432da3d93e827d306843a", + "tools/physical-workcell/workbench_link.py": "79571f62c5a9b5fb843a852bc34a2a509b0dc6dc1a6e091fa9f948ab98fae420", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/workbench-health-20261012/summary.json b/jevdual/results/workbench-health-20261012/summary.json new file mode 100644 index 0000000..ec4cd7e --- /dev/null +++ b/jevdual/results/workbench-health-20261012/summary.json @@ -0,0 +1,32 @@ +{ + "measured_final_event": { + "intent": "Health inspection 02", + "kind": "rpc_applied", + "outcome": "dispatch: command-bd5377590da74532a8333ac5ca2a72b3", + "seq": 284, + "time": 1791832270.9544058 + }, + "health_passed": true, + "complete_jobs": 2, + "actual_model_calls": 27, + "commands": 83, + "cancelled": [], + "unexpected_halts": [], + "delayed_healthy_reads": 173, + "natural_upstream_long_reads": 0, + "fresh_proof_every_rpc": true, + "jobs": [ + { + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "model_calls": 14, + "reset_generation": 11, + "contact_samples": 3166 + }, + { + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "model_calls": 13, + "reset_generation": 21, + "contact_samples": 3304 + } + ] +} diff --git a/jevdual/results/workbench-health-20261012/ui-ledger.json b/jevdual/results/workbench-health-20261012/ui-ledger.json new file mode 100644 index 0000000..568b4f8 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/ui-ledger.json @@ -0,0 +1,758 @@ +{ + "steps": [ + { + "browser_action": "Observed active image planning with Checking body under tagged 1.2 second reply delay", + "client_commands": 11, + "name": "healthy_checking", + "pending_command_ids": [], + "screenshot": "healthy_checking.png", + "state": { + "active_job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "app_id": "433d80f43f4148ef87d341b6d117396d", + "connection": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "fault": null, + "reason": "Health read delayed; fresh body proof is required before any command", + "state": "checking" + }, + "current_frame": { + "available": true, + "generation": 3, + "image_url": "/api/frame?generation=3&sha256=abd56127579f6a8c8dd685d44e7d633a4b7f230837d5892d1b0d10b71bbddd4b" + }, + "jobs": [ + { + "call_count": 4, + "disturbance_test": false, + "error": null, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "last_intent": "Retract the pusher left to clear contact before measuring the centered block.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": true, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No uncertain command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791832001.6553578, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t60 checkbox Health inspection 01 Working, Value: 1\n\t\t\t61 checkbox Health inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t62 text Health inspection 01\n\t\t63 text Running\n\t\t16 button Pause, ID: pause\n\t\t64 text A health reply is delayed. Planning can continue; commands require a fresh body check.\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t65 text Checking body\n\t\t\t66 text Health read delayed; fresh body proof is required before any command\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t23 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t24 text Test the simulated connection\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t67 text Last observation \u00b7 health unconfirmed\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t68 text Last captured \u00b7 health unconfirmed \u00b7 generation 2\n\t\t69 text AI IS PLANNING\n\t\t70 text Planning the next move from the latest camera image.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t71 text Pending\n\t\t\t39 text The part\u2019s visible footprint will be measured after its placement is verified." + }, + { + "browser_action": "Lost exactly one dispatch reply in the browser; successor blocked", + "client_commands": 19, + "name": "uncertain", + "pending_command_ids": [ + "command-e52c9ef5005a410fb80600b4ad7f947a" + ], + "screenshot": "uncertain.png", + "state": { + "active_job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "app_id": "433d80f43f4148ef87d341b6d117396d", + "connection": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "fault": null, + "reason": "Health read delayed; fresh body proof is required before any command", + "state": "checking" + }, + "current_frame": { + "available": true, + "generation": 3, + "image_url": "/api/frame?generation=3&sha256=abd56127579f6a8c8dd685d44e7d633a4b7f230837d5892d1b0d10b71bbddd4b" + }, + "jobs": [ + { + "call_count": 6, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "last_intent": "Move the pusher into the upper bypass lane before crossing to the block's right side.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "resetting", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-e52c9ef5005a410fb80600b4ad7f947a", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791832029.627487, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t87 checkbox Health inspection 01 Needs attention, Value: 1\n\t\t\t88 checkbox Health inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t89 text Health inspection 01\n\t\t90 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t91 text reply is uncertain; reconcile exact command without resending\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t92 text Checking body\n\t\t\t93 text Health read delayed; fresh body proof is required before any command\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button Check receipt, ID: reconcile\n\t\t\t66 container\n\t\t\t\t67 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t68 text Test the simulated connection\n\t\t\t\t69 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t70 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t71 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t72 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t94 text No reply fault armed.\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t95 text Last observation \u00b7 health unconfirmed\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t96 text Last captured \u00b7 health unconfirmed \u00b7 generation 3\n\t\t97 text OUTCOME UNCERTAIN\n\t\t98 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t99 text Historical result\n\t\t\t79 container measurement-result\n\t\t\t\t100 text 1,726\n\t\t\t\t81 text Orange pixels\n\t\t\t\t82 container\n\t\t\t\t\t101 text 4,794.4\n\t\t\t\t\t84 text mm\u00b2\n\t\t\t\t85 text Apparent footprint\n\t\t\t\t86 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment." + }, + { + "browser_action": "Clicked Disconnect with the exact pending command retained", + "client_commands": 19, + "name": "disconnected", + "pending_command_ids": [ + "command-e52c9ef5005a410fb80600b4ad7f947a" + ], + "screenshot": "disconnected.png", + "state": { + "active_job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "app_id": "433d80f43f4148ef87d341b6d117396d", + "connection": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "fault": null, + "reason": "Diagnostic link disconnected; cached observations are retained", + "state": "disconnected" + }, + "current_frame": { + "available": true, + "generation": 3, + "image_url": "/api/frame?generation=3&sha256=abd56127579f6a8c8dd685d44e7d633a4b7f230837d5892d1b0d10b71bbddd4b" + }, + "jobs": [ + { + "call_count": 6, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "last_intent": "Move the pusher into the upper bypass lane before crossing to the block's right side.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "resetting", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-e52c9ef5005a410fb80600b4ad7f947a", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791832038.488682, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t87 checkbox Health inspection 01 Needs attention, Value: 1\n\t\t\t88 checkbox Health inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t89 text Health inspection 01\n\t\t90 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t91 text reply is uncertain; reconcile exact command without resending\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t92 text Body disconnected\n\t\t\t93 text Diagnostic link disconnected; cached observations are retained\n\t\t\t21 button Reconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t66 container\n\t\t\t\t67 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t68 text Test the simulated connection\n\t\t\t\t69 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t70 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t71 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t72 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t94 text No reply fault armed.\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t95 text Retained observation \u00b7 body offline\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t96 text Retained \u00b7 offline \u00b7 generation 3\n\t\t97 text DISCONNECTED\n\t\t98 text The body connection is unavailable. The last camera observation is retained.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t99 text Historical result\n\t\t\t79 container measurement-result\n\t\t\t\t100 text 1,726\n\t\t\t\t81 text Orange pixels\n\t\t\t\t82 container\n\t\t\t\t\t101 text 4,794.4\n\t\t\t\t\t84 text mm\u00b2\n\t\t\t\t85 text Apparent footprint\n\t\t\t\t86 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment." + }, + { + "browser_action": "Observed Check receipt disabled offline; no effect or receipt request sent", + "client_commands": 19, + "name": "offline_receipt", + "pending_command_ids": [ + "command-e52c9ef5005a410fb80600b4ad7f947a" + ], + "screenshot": "offline_receipt.png", + "state": { + "active_job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "app_id": "433d80f43f4148ef87d341b6d117396d", + "connection": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "fault": null, + "reason": "Diagnostic link disconnected; cached observations are retained", + "state": "disconnected" + }, + "current_frame": { + "available": true, + "generation": 3, + "image_url": "/api/frame?generation=3&sha256=abd56127579f6a8c8dd685d44e7d633a4b7f230837d5892d1b0d10b71bbddd4b" + }, + "jobs": [ + { + "call_count": 6, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "last_intent": "Move the pusher into the upper bypass lane before crossing to the block's right side.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "resetting", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-e52c9ef5005a410fb80600b4ad7f947a", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791832047.263253, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t105 checkbox Health inspection 01 Needs attention, Value: 1\n\t\t\t106 checkbox Health inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t107 text Health inspection 01\n\t\t108 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t109 text reply is uncertain; reconcile exact command without resending\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t110 text Body disconnected\n\t\t\t111 text Diagnostic link disconnected; cached observations are retained\n\t\t\t21 button Reconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t66 container\n\t\t\t\t67 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t68 text Test the simulated connection\n\t\t\t\t69 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t70 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t71 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t72 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t112 text No reply fault armed.\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t113 text Retained observation \u00b7 body offline\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t114 text Retained \u00b7 offline \u00b7 generation 3\n\t\t115 text DISCONNECTED\n\t\t116 text The body connection is unavailable. The last camera observation is retained.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t117 text Historical result\n\t\t\t79 container measurement-result\n\t\t\t\t118 text 1,726\n\t\t\t\t81 text Orange pixels\n\t\t\t\t82 container\n\t\t\t\t\t119 text 4,794.4\n\t\t\t\t\t84 text mm\u00b2\n\t\t\t\t85 text Apparent footprint\n\t\t\t\t86 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment." + }, + { + "browser_action": "Clicked Reconnect; same pending command retained and runner still stopped", + "client_commands": 19, + "name": "reconnected", + "pending_command_ids": [ + "command-e52c9ef5005a410fb80600b4ad7f947a" + ], + "screenshot": "reconnected.png", + "state": { + "active_job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "app_id": "433d80f43f4148ef87d341b6d117396d", + "connection": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 3, + "image_url": "/api/frame?generation=3&sha256=abd56127579f6a8c8dd685d44e7d633a4b7f230837d5892d1b0d10b71bbddd4b" + }, + "jobs": [ + { + "call_count": 6, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "last_intent": "Move the pusher into the upper bypass lane before crossing to the block's right side.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "resetting", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-e52c9ef5005a410fb80600b4ad7f947a", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791832056.7812228, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t105 checkbox Health inspection 01 Needs attention, Value: 1\n\t\t\t106 checkbox Health inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t107 text Health inspection 01\n\t\t108 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t109 text reply is uncertain; reconcile exact command without resending\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t110 text Body connected\n\t\t\t111 text Body identity verified by read-only TCP health\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button Check receipt, ID: reconcile\n\t\t\t66 container\n\t\t\t\t67 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t68 text Test the simulated connection\n\t\t\t\t69 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t70 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t71 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t72 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t112 text No reply fault armed.\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t113 text Last observation \u00b7 Health inspection 01\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t114 text Captured \u00b7 generation 3\n\t\t115 text OUTCOME UNCERTAIN\n\t\t116 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t117 text Recorded\n\t\t\t79 container measurement-result\n\t\t\t\t118 text 1,726\n\t\t\t\t81 text Orange pixels\n\t\t\t\t82 container\n\t\t\t\t\t119 text 4,794.4\n\t\t\t\t\t84 text mm\u00b2\n\t\t\t\t85 text Apparent footprint\n\t\t\t\t86 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment." + }, + { + "browser_action": "Clicked Check receipt; exact result recovered while paused with unchanged command count", + "client_commands": 19, + "name": "recovered", + "pending_command_ids": [], + "screenshot": "recovered.png", + "state": { + "active_job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "app_id": "433d80f43f4148ef87d341b6d117396d", + "connection": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 3, + "image_url": "/api/frame?generation=3&sha256=abd56127579f6a8c8dd685d44e7d633a4b7f230837d5892d1b0d10b71bbddd4b" + }, + "jobs": [ + { + "call_count": 6, + "disturbance_test": false, + "error": null, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "last_intent": "Move the pusher into the upper bypass lane before crossing to the block's right side.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "resetting", + "reset": null, + "status": "paused", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-e52c9ef5005a410fb80600b4ad7f947a", + "operation": "dispatch", + "reason": "Exact historical stage recovered; explicit Resume observes fresh evidence", + "state": "recovered" + }, + "runner_status": "paused" + }, + "time": 1791832066.375594, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t105 checkbox Health inspection 01 Paused, Value: 1\n\t\t\t106 checkbox Health inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t107 text Health inspection 01\n\t\t108 text Paused\n\t\t16 button Resume, ID: pause\n\t\t109 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue.\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t110 text Body connected\n\t\t\t111 text Body identity verified by read-only TCP health\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t66 container\n\t\t\t\t67 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t68 text Test the simulated connection\n\t\t\t\t69 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t70 button Lose next reply, ID: fault-drop\n\t\t\t\t71 button Delay next reply, ID: fault-delay\n\t\t\t\t72 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t112 text No reply fault armed.\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t113 text Last observation \u00b7 Health inspection 01\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t114 text Captured \u00b7 generation 3\n\t\t115 text RECEIPT RECOVERED\n\t\t116 text The exact command receipt was recovered. The agent is paused until you resume.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t117 text Recorded\n\t\t\t79 container measurement-result\n\t\t\t\t118 text 1,726\n\t\t\t\t81 text Orange pixels\n\t\t\t\t82 container\n\t\t\t\t\t119 text 4,794.4\n\t\t\t\t\t84 text mm\u00b2\n\t\t\t\t85 text Apparent footprint\n\t\t\t\t86 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment." + }, + { + "browser_action": "Explicit Resume checks body boot and obtains a fresh camera image", + "client_commands": 20, + "name": "resumed", + "pending_command_ids": [], + "screenshot": "resumed.png", + "state": { + "active_job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "app_id": "433d80f43f4148ef87d341b6d117396d", + "connection": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 4, + "image_url": "/api/frame?generation=4&sha256=6d7772846a1b09beea21e40b6dbcbfd87a77a3c2bb5158871088d89f9fc03518" + }, + "jobs": [ + { + "call_count": 7, + "disturbance_test": false, + "error": null, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "last_intent": "Move the pusher into the upper bypass lane before crossing to the block's right side.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "resetting", + "reset": null, + "status": "running", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": true, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791832078.293568, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t105 checkbox Health inspection 01 Paused, Value: 1\n\t\t\t106 checkbox Health inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t107 text Health inspection 01\n\t\t108 text Running\n\t\t16 button Pause, ID: pause\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t109 text Body connected\n\t\t\t110 text Current body boot verified for explicit Resume\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t66 container\n\t\t\t\t67 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t68 text Test the simulated connection\n\t\t\t\t69 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t70 button Lose next reply, ID: fault-drop\n\t\t\t\t71 button Delay next reply, ID: fault-delay\n\t\t\t\t72 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t111 text No reply fault armed.\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t112 text Last observation \u00b7 Health inspection 01\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t113 text Captured \u00b7 generation 3\n\t\t114 text AGENT\n\t\t115 text Move the pusher into the upper bypass lane before crossing to the block's right side.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t116 text Recorded\n\t\t\t79 container measurement-result\n\t\t\t\t117 text 1,726\n\t\t\t\t81 text Orange pixels\n\t\t\t\t82 container\n\t\t\t\t\t118 text 4,794.4\n\t\t\t\t\t84 text mm\u00b2\n\t\t\t\t85 text Apparent footprint\n\t\t\t\t86 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment." + }, + { + "browser_action": "Observed second job still running and AI planning under identity-only health delay", + "client_commands": 83, + "name": "successive_checking", + "pending_command_ids": [], + "screenshot": "second_identity_delay.png", + "state": { + "active_job_id": null, + "app_id": "433d80f43f4148ef87d341b6d117396d", + "connection": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "fault": null, + "reason": "Health read delayed; fresh body proof is required before any command", + "state": "checking" + }, + "current_frame": { + "available": true, + "generation": 21, + "image_url": "/api/frame?generation=21&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "last_intent": "Finish reset with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "block_home_verified": true, + "generation": 11, + "image_sha256": "e47008c08f81d1ed137d87f021a4273e354a0c3d79e0c9b1da21e6dbeaf28429", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "kind": "finish_reset", + "native_time_s": 22.28199999999853, + "operation_id": "agent-53ec814ec65e409f894ccaf9c1dd2c2a", + "pusher_home_verified": true, + "reset_contact_samples": 3166, + "reset_native_steps": 7940, + "state_sha256": "0de60aca2a69c72c36eb8396ce0c3e420c60000ca6190ec36315207e14c804a4" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "last_intent": "Verify the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "block_home_verified": true, + "generation": 21, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "kind": "finish_reset", + "native_time_s": 43.03000000000662, + "operation_id": "agent-6ec3f611bd9042a5b9a1acc3867b74e7", + "pusher_home_verified": true, + "reset_contact_samples": 3304, + "reset_native_steps": 8106, + "state_sha256": "d3bdf81922c73fc0177d3453905a0376c365e73434d7708da14061adba34ddf7" + }, + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791832279.422046, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t125 checkbox Health inspection 01 Completed, Value: 0\n\t\t\t126 checkbox Health inspection 02 Working, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t127 text Health inspection 02\n\t\t128 text Running\n\t\t16 button Pause, ID: pause\n\t\t129 text A health reply is delayed. Planning can continue; commands require a fresh body check.\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t130 text Checking body\n\t\t\t131 text Health read delayed; fresh body proof is required before any command\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t66 container\n\t\t\t\t67 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t68 text Test the simulated connection\n\t\t\t\t69 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t70 button Lose next reply, ID: fault-drop\n\t\t\t\t71 button Delay next reply, ID: fault-delay\n\t\t\t\t72 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t132 text No reply fault armed.\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t133 text Last observation \u00b7 health unconfirmed\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t134 text Last captured \u00b7 health unconfirmed \u00b7 generation 21\n\t\t135 text AI IS PLANNING\n\t\t136 text Planning the next move from the latest camera image.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t137 text Historical result\n\t\t\t117 container measurement-result\n\t\t\t\t138 text 1,729\n\t\t\t\t119 text Orange pixels\n\t\t\t\t120 container\n\t\t\t\t\t139 text 4,802.8\n\t\t\t\t\t122 text mm\u00b2\n\t\t\t\t123 text Apparent footprint\n\t\t\t\t124 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment." + }, + { + "browser_action": "Both actual image-guided jobs completed their measurement and contact reset", + "client_commands": 83, + "name": "completed", + "pending_command_ids": [], + "screenshot": "completed.png", + "state": { + "active_job_id": null, + "app_id": "433d80f43f4148ef87d341b6d117396d", + "connection": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "fault": null, + "reason": "Health read delayed; fresh body proof is required before any command", + "state": "checking" + }, + "current_frame": { + "available": true, + "generation": 21, + "image_url": "/api/frame?generation=21&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "last_intent": "Finish reset with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "block_home_verified": true, + "generation": 11, + "image_sha256": "e47008c08f81d1ed137d87f021a4273e354a0c3d79e0c9b1da21e6dbeaf28429", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "kind": "finish_reset", + "native_time_s": 22.28199999999853, + "operation_id": "agent-53ec814ec65e409f894ccaf9c1dd2c2a", + "pusher_home_verified": true, + "reset_contact_samples": 3166, + "reset_native_steps": 7940, + "state_sha256": "0de60aca2a69c72c36eb8396ce0c3e420c60000ca6190ec36315207e14c804a4" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "last_intent": "Verify the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "block_home_verified": true, + "generation": 21, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "kind": "finish_reset", + "native_time_s": 43.03000000000662, + "operation_id": "agent-6ec3f611bd9042a5b9a1acc3867b74e7", + "pusher_home_verified": true, + "reset_contact_samples": 3304, + "reset_native_steps": 8106, + "state_sha256": "d3bdf81922c73fc0177d3453905a0376c365e73434d7708da14061adba34ddf7" + }, + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791832296.68832, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t125 checkbox Health inspection 01 Completed, Value: 0\n\t\t\t126 checkbox Health inspection 02 Completed, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t127 text Health inspection 02\n\t\t128 text Running\n\t\t16 button Pause, ID: pause\n\t\t129 text A health reply is delayed. Planning can continue; commands require a fresh body check.\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t130 text Checking body\n\t\t\t131 text Health read delayed; fresh body proof is required before any command\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t66 container\n\t\t\t\t67 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t68 text Test the simulated connection\n\t\t\t\t69 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t70 button Lose next reply, ID: fault-drop\n\t\t\t\t71 button Delay next reply, ID: fault-delay\n\t\t\t\t72 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t132 text No reply fault armed.\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t133 text Last observation \u00b7 health unconfirmed\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t134 text Last captured \u00b7 health unconfirmed \u00b7 generation 21\n\t\t135 text COMPLETED\n\t\t136 text Measurement recorded and reset verified. The bench is ready for another job.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t137 text Historical result\n\t\t\t117 container measurement-result\n\t\t\t\t138 text 1,729\n\t\t\t\t119 text Orange pixels\n\t\t\t\t120 container\n\t\t\t\t\t139 text 4,802.8\n\t\t\t\t\t122 text mm\u00b2\n\t\t\t\t123 text Apparent footprint\n\t\t\t\t124 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment.\n\t\t\t140 button Repeat job, ID: repeat" + }, + { + "browser_action": "Reloaded the browser; both completed jobs and command count retained", + "client_commands": 83, + "name": "reloaded", + "pending_command_ids": [], + "screenshot": "reloaded.png", + "state": { + "active_job_id": null, + "app_id": "433d80f43f4148ef87d341b6d117396d", + "connection": { + "adapter_id": "20f1c0ad0a0d40f38e1dee2ac094251e", + "body_id": "eea757a5363c43ca81de4a2f78715534", + "epoch": 1, + "fault": null, + "reason": "Health read delayed; fresh body proof is required before any command", + "state": "checking" + }, + "current_frame": { + "available": true, + "generation": 21, + "image_url": "/api/frame?generation=21&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "label": "Health inspection 01", + "last_intent": "Finish reset with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "block_home_verified": true, + "generation": 11, + "image_sha256": "e47008c08f81d1ed137d87f021a4273e354a0c3d79e0c9b1da21e6dbeaf28429", + "job_id": "6a210963-8657-4adc-8b6b-bfffd52f3743", + "kind": "finish_reset", + "native_time_s": 22.28199999999853, + "operation_id": "agent-53ec814ec65e409f894ccaf9c1dd2c2a", + "pusher_home_verified": true, + "reset_contact_samples": 3166, + "reset_native_steps": 7940, + "state_sha256": "0de60aca2a69c72c36eb8396ce0c3e420c60000ca6190ec36315207e14c804a4" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 13, + "disturbance_test": false, + "error": null, + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "label": "Health inspection 02", + "last_intent": "Verify the block center is in the home zone and the pusher is at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "eea757a5363c43ca81de4a2f78715534", + "block_home_verified": true, + "generation": 21, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "18dcf629-14a7-43a8-b47f-ecd98a1a85e1", + "kind": "finish_reset", + "native_time_s": 43.03000000000662, + "operation_id": "agent-6ec3f611bd9042a5b9a1acc3867b74e7", + "pusher_home_verified": true, + "reset_contact_samples": 3304, + "reset_native_steps": 8106, + "state_sha256": "d3bdf81922c73fc0177d3453905a0376c365e73434d7708da14061adba34ddf7" + }, + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791832310.4273899, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t9 checkbox Health inspection 01 Completed, Value: 0\n\t\t\t10 checkbox Health inspection 02 Completed, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t14 text Health inspection 02\n\t\t15 text Running\n\t\t16 button Pause, ID: pause\n\t\t17 text A health reply is delayed. Planning can continue; commands require a fresh body check.\n\t\t18 container Description: Body connection, ID: network-card\n\t\t\t19 text Checking body\n\t\t\t20 text Health read delayed; fresh body proof is required before any command\n\t\t\t21 button Disconnect, ID: disconnect\n\t\t\t22 button (disabled) Check receipt, ID: reconcile\n\t\t\t23 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t24 text Test the simulated connection\n\t\t25 container Camera observation\n\t\t\t26 text OVERHEAD CAMERA\n\t\t\t27 text Last observation \u00b7 health unconfirmed\n\t\t\t28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t29 text Orange part Blue pusher Green task region\n\t\t\t30 text Last captured \u00b7 health unconfirmed \u00b7 generation 21\n\t\t31 text COMPLETED\n\t\t32 text Measurement recorded and reset verified. The bench is ready for another job.\n\t\t33 container Job progress\n\t\t\t34 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t35 container Measurement\n\t\t\t36 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t37 text Measurement\n\t\t\t38 text Historical result\n\t\t\t39 container measurement-result\n\t\t\t\t40 text 1,729\n\t\t\t\t41 text Orange pixels\n\t\t\t\t42 container\n\t\t\t\t\t43 text 4,802.8\n\t\t\t\t\t44 text mm\u00b2\n\t\t\t\t45 text Apparent footprint\n\t\t\t\t46 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment.\n\t\t\t47 button Repeat job, ID: repeat" + } + ] +} diff --git a/jevdual/results/workbench-health-20261012/ui/completed.png b/jevdual/results/workbench-health-20261012/ui/completed.png new file mode 100644 index 0000000..98e256c Binary files /dev/null and b/jevdual/results/workbench-health-20261012/ui/completed.png differ diff --git a/jevdual/results/workbench-health-20261012/ui/completed.txt b/jevdual/results/workbench-health-20261012/ui/completed.txt new file mode 100644 index 0000000..227c448 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/ui/completed.txt @@ -0,0 +1,88 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 125 checkbox Health inspection 01 Completed, Value: 0 + 126 checkbox Health inspection 02 Completed, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 127 text Health inspection 02 + 128 text Running + 16 button Pause, ID: pause + 129 text A health reply is delayed. Planning can continue; commands require a fresh body check. + 18 container Description: Body connection, ID: network-card + 130 text Checking body + 131 text Health read delayed; fresh body proof is required before any command + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 66 container + 67 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 68 text Test the simulated connection + 69 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 70 button Lose next reply, ID: fault-drop + 71 button Delay next reply, ID: fault-delay + 72 button Duplicate next command, ID: fault-duplicate + 132 text No reply fault armed. + 25 container Camera observation + 26 text OVERHEAD CAMERA + 133 text Last observation · health unconfirmed + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 134 text Last captured · health unconfirmed · generation 21 + 135 text COMPLETED + 136 text Measurement recorded and reset verified. The bench is ready for another job. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 137 text Historical result + 117 container measurement-result + 138 text 1,729 + 119 text Orange pixels + 120 container + 139 text 4,802.8 + 122 text mm² + 123 text Apparent footprint + 124 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 140 button Repeat job, ID: repeat + 40 container Activity + 41 heading Activity, Value: 2, ID: activity-heading + 42 text Activity + 141 text 24 events + 104 content list activity + 142 container + 143 text 03:11 PM Action receipt checked Health inspection 02 + 144 container + 145 text 03:11 PM Sending a guarded action + 146 container + 147 text 03:11 PM Fresh body check verified The next command uses current body evidence. + 148 container + 149 text 03:11 PM Action proposal accepted Health inspection 02 + 150 container + 151 text 03:11 PM Checking the planned action + 152 container + 153 text 03:11 PM Fresh body check verified The next command uses current body evidence. + 154 container + 155 text 03:11 PM AI planning requested Health inspection 02 + 156 container + 157 text 03:11 PM Camera observation received Health inspection 02 + 158 container + 159 text 03:11 PM Requesting camera observation + 160 container + 161 text 03:11 PM Fresh body check verified The next command uses current body evidence. + 162 container + 163 text 03:11 PM Action receipt checked Health inspection 02 + 164 container + 165 text 03:11 PM Sending a guarded action + 44 button (collapsed) Session details, Secondary Actions: Expand + 45 text Session details + 166 text 27 model requests · Reported tokens: 352,734 input / 4,129 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-health-20261012/ui/handoff.png b/jevdual/results/workbench-health-20261012/ui/handoff.png new file mode 100644 index 0000000..1f75899 Binary files /dev/null and b/jevdual/results/workbench-health-20261012/ui/handoff.png differ diff --git a/jevdual/results/workbench-health-20261012/ui/handoff.txt b/jevdual/results/workbench-health-20261012/ui/handoff.txt new file mode 100644 index 0000000..dd2ae1e --- /dev/null +++ b/jevdual/results/workbench-health-20261012/ui/handoff.txt @@ -0,0 +1,81 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 9 checkbox Health inspection 01 Completed, Value: 0 + 10 checkbox Health inspection 02 Completed, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 14 text Health inspection 02 + 15 text Paused + 16 button Resume, ID: pause + 17 container Description: Body connection, ID: network-card + 18 text Body connected + 19 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 22 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 23 text Test the simulated connection + 24 container Camera observation + 25 text OVERHEAD CAMERA + 26 text Last camera observation + 27 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 29 text Captured · generation 21 + 30 text COMPLETED + 31 text Measurement recorded and reset verified. The bench is ready for another job. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 37 text Historical result + 38 container measurement-result + 39 text 1,729 + 40 text Orange pixels + 41 container + 42 text 4,802.8 + 43 text mm² + 44 text Apparent footprint + 45 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 46 button Repeat job, ID: repeat + 47 container Activity + 48 heading Activity, Value: 2, ID: activity-heading + 49 text Activity + 50 text 24 events + 51 content list activity + 52 container + 53 text 03:13 PM Agent stopped for review Remote end closed connection without response + 54 container + 55 text 03:12 PM Body restarted 1 -> 2 + 56 container + 57 text 03:12 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 58 container + 59 text 03:11 PM Action receipt checked Health inspection 02 + 60 container + 61 text 03:11 PM Sending a guarded action + 62 container + 63 text 03:11 PM Fresh body check verified The next command uses current body evidence. + 64 container + 65 text 03:11 PM Action proposal accepted Health inspection 02 + 66 container + 67 text 03:11 PM Checking the planned action + 68 container + 69 text 03:11 PM Fresh body check verified The next command uses current body evidence. + 70 container + 71 text 03:11 PM AI planning requested Health inspection 02 + 72 container + 73 text 03:11 PM Camera observation received Health inspection 02 + 74 container + 75 text 03:11 PM Requesting camera observation + 76 button (collapsed) Session details, Secondary Actions: Expand + 77 text Session details + 78 text 27 model requests · Reported tokens: 352,734 input / 4,129 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-health-20261012/ui/healthy_checking.png b/jevdual/results/workbench-health-20261012/ui/healthy_checking.png new file mode 100644 index 0000000..855e040 Binary files /dev/null and b/jevdual/results/workbench-health-20261012/ui/healthy_checking.png differ diff --git a/jevdual/results/workbench-health-20261012/ui/healthy_checking.txt b/jevdual/results/workbench-health-20261012/ui/healthy_checking.txt new file mode 100644 index 0000000..31b4d58 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/ui/healthy_checking.txt @@ -0,0 +1,74 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 60 checkbox Health inspection 01 Working, Value: 1 + 61 checkbox Health inspection 02 Queued, Value: 0 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 62 text Health inspection 01 + 63 text Running + 16 button Pause, ID: pause + 64 text A health reply is delayed. Planning can continue; commands require a fresh body check. + 18 container Description: Body connection, ID: network-card + 65 text Checking body + 66 text Health read delayed; fresh body proof is required before any command + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 23 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 24 text Test the simulated connection + 25 container Camera observation + 26 text OVERHEAD CAMERA + 67 text Last observation · health unconfirmed + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 68 text Last captured · health unconfirmed · generation 2 + 69 text AI IS PLANNING + 70 text Planning the next move from the latest camera image. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 71 text Pending + 39 text The part’s visible footprint will be measured after its placement is verified. + 40 container Activity + 41 heading Activity, Value: 2, ID: activity-heading + 42 text Activity + 72 text 24 events + 59 content list activity + 73 container + 74 text 03:06 PM AI planning requested Health inspection 01 + 75 container + 76 text 03:06 PM Camera observation received Health inspection 01 + 77 container + 78 text 03:06 PM Requesting camera observation + 79 container + 80 text 03:06 PM Fresh body check verified The next command uses current body evidence. + 81 container + 82 text 03:06 PM Action receipt checked Health inspection 01 + 83 container + 84 text 03:06 PM Sending a guarded action + 85 container + 86 text 03:06 PM Fresh body check verified The next command uses current body evidence. + 87 container + 88 text 03:06 PM Action proposal accepted Health inspection 01 + 89 container + 90 text 03:06 PM Checking the planned action + 91 container + 92 text 03:06 PM Fresh body check verified The next command uses current body evidence. + 93 container + 94 text 03:06 PM AI planning requested Health inspection 01 + 95 container + 96 text 03:06 PM Camera observation received Health inspection 01 + 44 button (collapsed) Session details, Secondary Actions: Expand + 45 text Session details + 97 text 3 model requests · Reported tokens: 25,735 input / 336 output · Reporting incomplete (2 of 3 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-health-20261012/ui/recovered.png b/jevdual/results/workbench-health-20261012/ui/recovered.png new file mode 100644 index 0000000..a1b4b72 Binary files /dev/null and b/jevdual/results/workbench-health-20261012/ui/recovered.png differ diff --git a/jevdual/results/workbench-health-20261012/ui/recovered.txt b/jevdual/results/workbench-health-20261012/ui/recovered.txt new file mode 100644 index 0000000..1da7932 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/ui/recovered.txt @@ -0,0 +1,87 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 105 checkbox Health inspection 01 Paused, Value: 1 + 106 checkbox Health inspection 02 Queued, Value: 0 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 107 text Health inspection 01 + 108 text Paused + 16 button Resume, ID: pause + 109 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue. + 18 container Description: Body connection, ID: network-card + 110 text Body connected + 111 text Body identity verified by read-only TCP health + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 66 container + 67 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 68 text Test the simulated connection + 69 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 70 button Lose next reply, ID: fault-drop + 71 button Delay next reply, ID: fault-delay + 72 button Duplicate next command, ID: fault-duplicate + 112 text No reply fault armed. + 25 container Camera observation + 26 text OVERHEAD CAMERA + 113 text Last observation · Health inspection 01 + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 114 text Captured · generation 3 + 115 text RECEIPT RECOVERED + 116 text The exact command receipt was recovered. The agent is paused until you resume. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 117 text Recorded + 79 container measurement-result + 118 text 1,726 + 81 text Orange pixels + 82 container + 119 text 4,794.4 + 84 text mm² + 85 text Apparent footprint + 86 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 40 container Activity + 41 heading Activity, Value: 2, ID: activity-heading + 42 text Activity + 120 text 24 events + 104 content list activity + 121 container + 122 text 03:07 PM Command receipt recovered The agent remains paused until you resume. + 123 container + 124 text 03:07 PM Action receipt checked Health inspection 01 + 125 container + 126 text 03:07 PM Body reconnecting No command sent or replayed + 127 container + 128 text 03:07 PM Body disconnected Transport only; no body reset or replay + 129 container + 130 text 03:06 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 131 container + 132 text 03:06 PM Sending a guarded action + 133 container + 134 text 03:06 PM Fresh body check verified The next command uses current body evidence. + 135 container + 136 text 03:06 PM Action proposal accepted Health inspection 01 + 137 container + 138 text 03:06 PM Checking the planned action + 139 container + 140 text 03:06 PM Fresh body check verified The next command uses current body evidence. + 141 container + 142 text 03:06 PM Reply fault armed The next command reply will be lost. + 143 container + 144 text 03:06 PM AI planning requested Health inspection 01 + 44 button (collapsed) Session details, Secondary Actions: Expand + 45 text Session details + 145 text 6 model requests · Reported tokens: 78,086 input / 888 output + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-health-20261012/ui/second_identity_delay.png b/jevdual/results/workbench-health-20261012/ui/second_identity_delay.png new file mode 100644 index 0000000..7f9bd56 Binary files /dev/null and b/jevdual/results/workbench-health-20261012/ui/second_identity_delay.png differ diff --git a/jevdual/results/workbench-health-20261012/ui/second_identity_delay.txt b/jevdual/results/workbench-health-20261012/ui/second_identity_delay.txt new file mode 100644 index 0000000..8d4d859 --- /dev/null +++ b/jevdual/results/workbench-health-20261012/ui/second_identity_delay.txt @@ -0,0 +1,87 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 125 checkbox Health inspection 01 Completed, Value: 0 + 126 checkbox Health inspection 02 Working, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 127 text Health inspection 02 + 128 text Running + 16 button Pause, ID: pause + 129 text A health reply is delayed. Planning can continue; commands require a fresh body check. + 18 container Description: Body connection, ID: network-card + 130 text Checking body + 131 text Health read delayed; fresh body proof is required before any command + 21 button Disconnect, ID: disconnect + 22 button (disabled) Check receipt, ID: reconcile + 66 container + 67 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 68 text Test the simulated connection + 69 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 70 button Lose next reply, ID: fault-drop + 71 button Delay next reply, ID: fault-delay + 72 button Duplicate next command, ID: fault-duplicate + 132 text No reply fault armed. + 25 container Camera observation + 26 text OVERHEAD CAMERA + 133 text Last observation · health unconfirmed + 28 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 29 text Orange part Blue pusher Green task region + 134 text Last captured · health unconfirmed · generation 21 + 135 text AI IS PLANNING + 136 text Planning the next move from the latest camera image. + 33 container Job progress + 34 text 1 Prepare 2 Measure 3 Reset 4 Ready + 35 container Measurement + 36 heading Measurement, Value: 2, ID: measurement-heading + 37 text Measurement + 137 text Historical result + 117 container measurement-result + 138 text 1,729 + 119 text Orange pixels + 120 container + 139 text 4,802.8 + 122 text mm² + 123 text Apparent footprint + 124 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 40 container Activity + 41 heading Activity, Value: 2, ID: activity-heading + 42 text Activity + 140 text 24 events + 104 content list activity + 141 container + 142 text 03:11 PM AI planning requested Health inspection 02 + 143 container + 144 text 03:11 PM Camera observation received Health inspection 02 + 145 container + 146 text 03:11 PM Requesting camera observation + 147 container + 148 text 03:11 PM Fresh body check verified The next command uses current body evidence. + 149 container + 150 text 03:11 PM Action receipt checked Health inspection 02 + 151 container + 152 text 03:11 PM Sending a guarded action + 153 container + 154 text 03:11 PM Fresh body check verified The next command uses current body evidence. + 155 container + 156 text 03:11 PM Action proposal accepted Health inspection 02 + 157 container + 158 text 03:11 PM Checking the planned action + 159 container + 160 text 03:11 PM Fresh body check verified The next command uses current body evidence. + 161 container + 162 text 03:10 PM AI planning requested Health inspection 02 + 163 container + 164 text 03:10 PM Camera observation received Health inspection 02 + 44 button (collapsed) Session details, Secondary Actions: Expand + 45 text Session details + 165 text 27 model requests · Reported tokens: 339,682 input / 4,017 output · Reporting incomplete (26 of 27 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/attempt-ledger.json b/jevdual/results/workbench-restarts-20261012/attempt-ledger.json new file mode 100644 index 0000000..ed8c3a3 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/attempt-ledger.json @@ -0,0 +1,37 @@ +{ + "actual_model_attempts": [ + { + "additional_health_timeout_pause": true, + "attempt": 1, + "cancelled_calls": 3, + "commands": 83, + "completed_jobs": 2, + "model_calls": 29, + "owned_trial_processes_reaped": true, + "passed": true, + "restart_pairs": 4, + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2", + "source_commit": "d7072107d576d7a09643229ae584a3864d2fe0f6" + } + ], + "preserved_previous_product": "tools/physical-workcell/output/sim11-preserved-baseline/preserved.json", + "retained_development_failures": [ + { + "diagnosis": "old decisions survived fast boot change; unfinished/recovered job labels were inconsistent", + "failed_checks": 4, + "log": "tools/physical-workcell/output/sim11-epoch-before-fix.log" + } + ], + "startup_attempts": [ + { + "attempt": 1, + "model_calls": 0, + "observed_exit_code": 1, + "outcome": "refused before source freeze or process launch; full source SHA required", + "provenance": "Codex exec result chunk 1cd3d5; derived record, not reconstructed stdout", + "root": "tools/physical-workcell/output/sim11-browser-restarts-v1", + "source_argument": "d707210" + } + ], + "unmeasured_test_fixture_change": "The helper reader fixture moved from a temporary system directory to owned project output; measured source hashes stayed unchanged." +} diff --git a/jevdual/results/workbench-restarts-20261012/conformance-summary.json b/jevdual/results/workbench-restarts-20261012/conformance-summary.json new file mode 100644 index 0000000..ae8f616 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/conformance-summary.json @@ -0,0 +1,27 @@ +{ + "additional_checks": [ + "Ruff", + "JavaScript syntax", + "notebook schema", + "diff check" + ], + "failed_final": 0, + "passed": 103, + "suites": [ + { + "checks": 19, + "log": "tools/physical-workcell/output/sim11-epoch-after-fix-runtime.log", + "scope": "real-socket runtime, fast reboot epoch paths, historical recovery and job status" + }, + { + "checks": 80, + "log": "tools/physical-workcell/output/sim11-core-network-regression.log", + "scope": "core/body/client/link/fault-matrix/HTTP regressions" + }, + { + "checks": 4, + "log": "tools/physical-workcell/output/sim11-harness-tests-v2/unittest.log", + "scope": "restart control predicates, ownership and bounded readers; synthetic controls" + } + ] +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000001.request.json b/jevdual/results/workbench-restarts-20261012/control/000001.request.json new file mode 100644 index 0000000..e9c3abf --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000001.request.json @@ -0,0 +1,16 @@ +{ + "action": "stop-role", + "expect": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": true, + "recovery_command_id": null, + "runner_status": "running", + "usage_calls": 1 + }, + "predicate": "planning", + "requested_time": 1791828631.2835982, + "role": "workbench", + "sequence": 1 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000001.result.json b/jevdual/results/workbench-restarts-20261012/control/000001.result.json new file mode 100644 index 0000000..94eae0f --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000001.result.json @@ -0,0 +1,34 @@ +{ + "action_performed": true, + "after": "000001-after.json", + "before": "000001-before.json", + "passed": true, + "process": { + "boot": 1, + "exit_observed": true, + "initialized": false, + "log": "workbench-01.log", + "pid": 25794, + "port": 51377, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator", + "stopped_time": 1791828631.66827 + }, + "request": { + "action": "stop-role", + "expect": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": true, + "recovery_command_id": null, + "runner_status": "running", + "usage_calls": 1 + }, + "predicate": "planning", + "requested_time": 1791828631.2835982, + "role": "workbench", + "sequence": 1 + } +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000002.request.json b/jevdual/results/workbench-restarts-20261012/control/000002.request.json new file mode 100644 index 0000000..aa9d2de --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000002.request.json @@ -0,0 +1,8 @@ +{ + "action": "start-role", + "expect": null, + "predicate": "none", + "requested_time": 1791828682.922372, + "role": "workbench", + "sequence": 2 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000002.result.json b/jevdual/results/workbench-restarts-20261012/control/000002.result.json new file mode 100644 index 0000000..919ea88 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000002.result.json @@ -0,0 +1,17 @@ +{ + "action_performed": true, + "after": "000002-after.json", + "before": "000002-before.json", + "passed": true, + "pid": 30888, + "predicate": "planning", + "request": { + "action": "start-role", + "expect": null, + "predicate": "none", + "requested_time": 1791828682.922372, + "role": "workbench", + "sequence": 2 + }, + "stopped_sequence": 1 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000003.request.json b/jevdual/results/workbench-restarts-20261012/control/000003.request.json new file mode 100644 index 0000000..512aa51 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000003.request.json @@ -0,0 +1,16 @@ +{ + "action": "stop-role", + "expect": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "runner_status": "blocked", + "usage_calls": 2 + }, + "predicate": "uncertain", + "requested_time": 1791828767.342037, + "role": "workbench", + "sequence": 3 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000003.result.json b/jevdual/results/workbench-restarts-20261012/control/000003.result.json new file mode 100644 index 0000000..03de9ad --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000003.result.json @@ -0,0 +1,34 @@ +{ + "action_performed": true, + "after": "000003-after.json", + "before": "000003-before.json", + "passed": true, + "process": { + "boot": 2, + "exit_observed": true, + "initialized": false, + "log": "workbench-02.log", + "pid": 30888, + "port": 51377, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator", + "stopped_time": 1791828768.2145522 + }, + "request": { + "action": "stop-role", + "expect": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "runner_status": "blocked", + "usage_calls": 2 + }, + "predicate": "uncertain", + "requested_time": 1791828767.342037, + "role": "workbench", + "sequence": 3 + } +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000004.request.json b/jevdual/results/workbench-restarts-20261012/control/000004.request.json new file mode 100644 index 0000000..9c51ca9 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000004.request.json @@ -0,0 +1,8 @@ +{ + "action": "start-role", + "expect": null, + "predicate": "none", + "requested_time": 1791828779.372716, + "role": "workbench", + "sequence": 4 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000004.result.json b/jevdual/results/workbench-restarts-20261012/control/000004.result.json new file mode 100644 index 0000000..f79ce05 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000004.result.json @@ -0,0 +1,17 @@ +{ + "action_performed": true, + "after": "000004-after.json", + "before": "000004-before.json", + "passed": true, + "pid": 39967, + "predicate": "uncertain", + "request": { + "action": "start-role", + "expect": null, + "predicate": "none", + "requested_time": 1791828779.372716, + "role": "workbench", + "sequence": 4 + }, + "stopped_sequence": 3 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000005.request.json b/jevdual/results/workbench-restarts-20261012/control/000005.request.json new file mode 100644 index 0000000..bd01e01 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000005.request.json @@ -0,0 +1,16 @@ +{ + "action": "stop-role", + "expect": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": true, + "recovery_command_id": null, + "runner_status": "running", + "usage_calls": 15 + }, + "predicate": "planning", + "requested_time": 1791828952.679998, + "role": "body", + "sequence": 5 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000005.result.json b/jevdual/results/workbench-restarts-20261012/control/000005.result.json new file mode 100644 index 0000000..9c98fc8 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000005.result.json @@ -0,0 +1,34 @@ +{ + "action_performed": true, + "after": "000005-after.json", + "before": "000005-before.json", + "passed": true, + "process": { + "boot": 1, + "exit_observed": true, + "initialized": true, + "log": "body-01.log", + "pid": 25787, + "port": 51373, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/body", + "stopped_time": 1791828952.994875 + }, + "request": { + "action": "stop-role", + "expect": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": true, + "recovery_command_id": null, + "runner_status": "running", + "usage_calls": 15 + }, + "predicate": "planning", + "requested_time": 1791828952.679998, + "role": "body", + "sequence": 5 + } +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000006.request.json b/jevdual/results/workbench-restarts-20261012/control/000006.request.json new file mode 100644 index 0000000..ed87819 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000006.request.json @@ -0,0 +1,16 @@ +{ + "action": "start-role", + "expect": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": null, + "runner_status": "paused", + "usage_calls": 15 + }, + "predicate": "none", + "requested_time": 1791829008.869165, + "role": "body", + "sequence": 6 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000006.result.json b/jevdual/results/workbench-restarts-20261012/control/000006.result.json new file mode 100644 index 0000000..6ec2f26 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000006.result.json @@ -0,0 +1,25 @@ +{ + "action_performed": true, + "after": "000006-after.json", + "before": "000006-before.json", + "passed": true, + "pid": 71324, + "predicate": "planning", + "request": { + "action": "start-role", + "expect": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": null, + "runner_status": "paused", + "usage_calls": 15 + }, + "predicate": "none", + "requested_time": 1791829008.869165, + "role": "body", + "sequence": 6 + }, + "stopped_sequence": 5 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000007.request.json b/jevdual/results/workbench-restarts-20261012/control/000007.request.json new file mode 100644 index 0000000..01912cf --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000007.request.json @@ -0,0 +1,16 @@ +{ + "action": "stop-role", + "expect": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "runner_status": "blocked", + "usage_calls": 16 + }, + "predicate": "uncertain", + "requested_time": 1791829041.8267992, + "role": "body", + "sequence": 7 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000007.result.json b/jevdual/results/workbench-restarts-20261012/control/000007.result.json new file mode 100644 index 0000000..d8bbbab --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000007.result.json @@ -0,0 +1,34 @@ +{ + "action_performed": true, + "after": "000007-after.json", + "before": "000007-before.json", + "passed": true, + "process": { + "boot": 2, + "exit_observed": true, + "initialized": false, + "log": "body-02.log", + "pid": 71324, + "port": 51373, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/body", + "stopped_time": 1791829042.1212611 + }, + "request": { + "action": "stop-role", + "expect": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "runner_status": "blocked", + "usage_calls": 16 + }, + "predicate": "uncertain", + "requested_time": 1791829041.8267992, + "role": "body", + "sequence": 7 + } +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000008.request.json b/jevdual/results/workbench-restarts-20261012/control/000008.request.json new file mode 100644 index 0000000..3b5d2da --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000008.request.json @@ -0,0 +1,16 @@ +{ + "action": "start-role", + "expect": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "runner_status": "paused", + "usage_calls": 16 + }, + "predicate": "none", + "requested_time": 1791829079.469436, + "role": "body", + "sequence": 8 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000008.result.json b/jevdual/results/workbench-restarts-20261012/control/000008.result.json new file mode 100644 index 0000000..6c508e3 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000008.result.json @@ -0,0 +1,25 @@ +{ + "action_performed": true, + "after": "000008-after.json", + "before": "000008-before.json", + "passed": true, + "pid": 74921, + "predicate": "uncertain", + "request": { + "action": "start-role", + "expect": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "runner_status": "paused", + "usage_calls": 16 + }, + "predicate": "none", + "requested_time": 1791829079.469436, + "role": "body", + "sequence": 8 + }, + "stopped_sequence": 7 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000009.request.json b/jevdual/results/workbench-restarts-20261012/control/000009.request.json new file mode 100644 index 0000000..c4c1813 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000009.request.json @@ -0,0 +1,16 @@ +{ + "action": "finish", + "expect": { + "active_job_id": null, + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": null, + "runner_status": "running", + "usage_calls": 29 + }, + "predicate": "none", + "requested_time": 1791829364.559857, + "role": null, + "sequence": 9 +} diff --git a/jevdual/results/workbench-restarts-20261012/control/000009.result.json b/jevdual/results/workbench-restarts-20261012/control/000009.result.json new file mode 100644 index 0000000..97785a0 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/control/000009.result.json @@ -0,0 +1,22 @@ +{ + "action_performed": false, + "after": "000009-after.json", + "before": "000009-before.json", + "passed": true, + "request": { + "action": "finish", + "expect": { + "active_job_id": null, + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": null, + "runner_status": "running", + "usage_calls": 29 + }, + "predicate": "none", + "requested_time": 1791829364.559857, + "role": null, + "sequence": 9 + } +} diff --git a/jevdual/results/workbench-restarts-20261012/handoff.json b/jevdual/results/workbench-restarts-20261012/handoff.json new file mode 100644 index 0000000..7b0fac7 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/handoff.json @@ -0,0 +1,38 @@ +{ + "processes": [ + { + "role": "body", + "pid": 8139, + "arguments": [ + "body_network.py", + "--root", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/body", + "--port", + "51373" + ] + }, + { + "role": "workbench", + "pid": 8145, + "arguments": [ + "network_bench_operator.py", + "serve", + "--root", + "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator", + "--port", + "51377", + "--backend", + "codex" + ] + } + ], + "browser_origin": "http://127.0.0.1:51377", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "epoch": 4, + "runner_status": "paused", + "client_commands": 83, + "completed_jobs": 2, + "model_request_pending": false, + "source_commit": "d7072107d576d7a09643229ae584a3864d2fe0f6" +} \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/health-timeout-observation.json b/jevdual/results/workbench-restarts-20261012/health-timeout-observation.json new file mode 100644 index 0000000..4530889 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/health-timeout-observation.json @@ -0,0 +1,11 @@ +{ + "cause": "unresolved; do not infer a specific network or scheduler cause", + "evidence": [ + "ui/transient-health-pause.txt", + "ui/transient-health-resumed.txt", + "qualification-score.json" + ], + "observed": "Health reads timed out during second-job reset and cancelled one additional model call; later read-only identity returned in 0.023494 seconds and connection was connected. Explicit Resume took a fresh observation and the job completed.", + "replayed_commands": 0, + "source_edited_during_trial": false +} diff --git a/jevdual/results/workbench-restarts-20261012/process-ledger.json b/jevdual/results/workbench-restarts-20261012/process-ledger.json new file mode 100644 index 0000000..cda8183 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/process-ledger.json @@ -0,0 +1,91 @@ +{ + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "body_origin": "http://127.0.0.1:51373", + "browser_origin": "http://127.0.0.1:51377", + "owned_processes_exited": true, + "planner": "actual_codex_cli_images_over_TCP", + "processes": [ + { + "boot": 1, + "exit_observed": true, + "initialized": true, + "log": "body-01.log", + "pid": 25787, + "port": 51373, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/body", + "stopped_time": 1791828952.994875 + }, + { + "exit_observed": true, + "pid": 25789, + "returncode": 0, + "role": "initialization" + }, + { + "boot": 1, + "exit_observed": true, + "initialized": false, + "log": "workbench-01.log", + "pid": 25794, + "port": 51377, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator", + "stopped_time": 1791828631.66827 + }, + { + "boot": 2, + "exit_observed": true, + "initialized": false, + "log": "workbench-02.log", + "pid": 30888, + "port": 51377, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator", + "stopped_time": 1791828768.2145522 + }, + { + "boot": 3, + "exit_observed": true, + "initialized": false, + "log": "workbench-03.log", + "pid": 39967, + "port": 51377, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator", + "stopped_time": 1791829365.605063 + }, + { + "boot": 2, + "exit_observed": true, + "initialized": false, + "log": "body-02.log", + "pid": 71324, + "port": 51373, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/body", + "stopped_time": 1791829042.1212611 + }, + { + "boot": 3, + "exit_observed": true, + "initialized": false, + "log": "body-03.log", + "pid": 74921, + "port": 51373, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/body", + "stopped_time": 1791829365.6788201 + } + ], + "ready": true, + "simulation": true, + "source_commit": "d7072107d576d7a09643229ae584a3864d2fe0f6" +} diff --git a/jevdual/results/workbench-restarts-20261012/qualification-score.json b/jevdual/results/workbench-restarts-20261012/qualification-score.json new file mode 100644 index 0000000..0f12bed --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/qualification-score.json @@ -0,0 +1,744 @@ +{ + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "browser_transitions_passed": true, + "calls": [ + { + "action": null, + "call": 1, + "core_receipt_bound": false, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": false, + "usage": null, + "withheld": true + }, + { + "action": "move", + "call": 2, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10880, + "input_tokens": 13063, + "output_tokens": 199 + }, + "withheld": false + }, + { + "action": "move", + "call": 3, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13123, + "output_tokens": 131 + }, + "withheld": false + }, + { + "action": "measure", + "call": 4, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13192, + "output_tokens": 136 + }, + "withheld": false + }, + { + "action": "begin_reset", + "call": 5, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13262, + "output_tokens": 38 + }, + "withheld": false + }, + { + "action": "move", + "call": 6, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13323, + "output_tokens": 191 + }, + "withheld": false + }, + { + "action": "move", + "call": 7, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13406, + "output_tokens": 152 + }, + "withheld": false + }, + { + "action": "move", + "call": 8, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13466, + "output_tokens": 246 + }, + "withheld": false + }, + { + "action": "move", + "call": 9, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13017, + "output_tokens": 165 + }, + "withheld": false + }, + { + "action": "move", + "call": 10, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13017, + "output_tokens": 193 + }, + "withheld": false + }, + { + "action": "move", + "call": 11, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13018, + "output_tokens": 195 + }, + "withheld": false + }, + { + "action": "move", + "call": 12, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13495, + "output_tokens": 138 + }, + "withheld": false + }, + { + "action": "move", + "call": 13, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10880, + "input_tokens": 13494, + "output_tokens": 105 + }, + "withheld": false + }, + { + "action": "finish_reset", + "call": 14, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13496, + "output_tokens": 119 + }, + "withheld": false + }, + { + "action": null, + "call": 1, + "core_receipt_bound": false, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": false, + "usage": null, + "withheld": true + }, + { + "action": "move", + "call": 2, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13084, + "output_tokens": 214 + }, + "withheld": false + }, + { + "action": "move", + "call": 3, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 0, + "input_tokens": 13144, + "output_tokens": 147 + }, + "withheld": false + }, + { + "action": "measure", + "call": 4, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 12758, + "output_tokens": 127 + }, + "withheld": false + }, + { + "action": "begin_reset", + "call": 5, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13294, + "output_tokens": 42 + }, + "withheld": false + }, + { + "action": "move", + "call": 6, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12893, + "output_tokens": 146 + }, + "withheld": false + }, + { + "action": "move", + "call": 7, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 12968, + "output_tokens": 189 + }, + "withheld": false + }, + { + "action": "move", + "call": 8, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13041, + "output_tokens": 161 + }, + "withheld": false + }, + { + "action": "move", + "call": 9, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13049, + "output_tokens": 166 + }, + "withheld": false + }, + { + "action": "move", + "call": 10, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10880, + "input_tokens": 13519, + "output_tokens": 165 + }, + "withheld": false + }, + { + "action": "move", + "call": 11, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13057, + "output_tokens": 202 + }, + "withheld": false + }, + { + "action": "move", + "call": 12, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10368, + "input_tokens": 13065, + "output_tokens": 164 + }, + "withheld": false + }, + { + "action": "move", + "call": 13, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 3968, + "input_tokens": 13530, + "output_tokens": 120 + }, + "withheld": false + }, + { + "action": null, + "call": 14, + "core_receipt_bound": false, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": false, + "usage": null, + "withheld": true + }, + { + "action": "finish_reset", + "call": 15, + "core_receipt_bound": true, + "exact_TCP_image": true, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "model_process_reaped": true, + "no_private_pose": true, + "one_client_send_bound": true, + "usage": { + "cached_input_tokens": 10880, + "input_tokens": 13536, + "output_tokens": 112 + }, + "withheld": false + } + ], + "completed_cycles": 2, + "lost_reply_reconciled_without_resend": true, + "model_boundaries_passed": true, + "owned_processes_exited": true, + "passed": true, + "scope": "browser UI and actual image policy over loopback TCP simulation", + "scores": [ + { + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.46799999999962, + "operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "source_kind": "mujoco_simulation", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9", + "version": 1, + "worker_pid": 51928 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.06582870900153087, + -8.38971234022521e-11, + 0.03499573045536974 + ], + "external_force_cleared": true, + "generation": 2, + "native_time_s": 5.46799999999962, + "pusher_home": false, + "pusher_xyz": [ + -0.03999996967021979, + 1.8297702229966647e-25, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9" + }, + "model_calls": 14, + "motion_count": 10, + "passed": true, + "reset_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11582763630839087, + -2.3619261859293773e-09, + 0.03499573045536983 + ], + "external_force_cleared": true, + "generation": 10, + "native_time_s": 21.679999999998863, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + } + }, + { + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "measurement_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 12, + "image_sha256": "60d8074b8c92a02b009c072c57bc1340098311df51c08f9d9d02d41f596c342e", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 26.349999999996275, + "operation_id": "agent-12d303b3be23401d9cdd1b1296f3c87a", + "source_kind": "mujoco_simulation", + "state_sha256": "7ced546541611d509086f34fc585c77771e272c65f3bbb518c94fb1942dbcd16", + "version": 1, + "worker_pid": 77998 + }, + "measurement_state": { + "block_center_in_zone": true, + "block_xyz": [ + 0.07578825420376317, + -2.361855509899145e-09, + 0.03499573045536977 + ], + "external_force_cleared": true, + "generation": 12, + "native_time_s": 26.349999999996275, + "pusher_home": false, + "pusher_xyz": [ + -0.049999969400252586, + 1.975335742467142e-26, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "7ced546541611d509086f34fc585c77771e272c65f3bbb518c94fb1942dbcd16" + }, + "model_calls": 15, + "motion_count": 10, + "passed": true, + "reset_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 20, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "kind": "finish_reset", + "native_time_s": 42.630000000006135, + "operation_id": "agent-7acee30a3cb442d9a3a2b3a9bfec10ed", + "pusher_home_verified": true, + "reset_contact_samples": 3314, + "reset_native_steps": 8140, + "state_sha256": "02c49f9f03f16f8de552b4b4ce57b6d78c85e291fc3dbf1b6acb0ce4b7cd638b" + }, + "reset_state": { + "block_center_in_zone": true, + "block_xyz": [ + -0.11582731939863683, + -9.491113827050927e-09, + 0.03499573045536984 + ], + "external_force_cleared": true, + "generation": 20, + "native_time_s": 42.630000000006135, + "pusher_home": true, + "pusher_xyz": [ + -0.3, + 3.071149644023488e-08, + 0.06 + ], + "role": "independent_native_state_scorer", + "state_sha256": "02c49f9f03f16f8de552b4b4ce57b6d78c85e291fc3dbf1b6acb0ce4b7cd638b" + } + } + ], + "source_commit": "d7072107d576d7a09643229ae584a3864d2fe0f6", + "source_unchanged": true, + "sqlite": { + "count": 2, + "role": "independent_read_only_sqlite_scorer", + "rows": [ + { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement": { + "block_box": [ + 339, + 159, + 381, + 201 + ], + "block_center_xy": [ + 0.06666666666666667, + 0.0 + ], + "centroid_pixel_xy": [ + 359.0486674391657, + 179.5393974507532 + ], + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.46799999999962, + "operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "source_kind": "mujoco_simulation", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9", + "version": 1, + "worker_pid": 51928 + }, + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9" + }, + { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 12, + "image_sha256": "60d8074b8c92a02b009c072c57bc1340098311df51c08f9d9d02d41f596c342e", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "operation_id": "agent-12d303b3be23401d9cdd1b1296f3c87a", + "receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 12, + "image_sha256": "60d8074b8c92a02b009c072c57bc1340098311df51c08f9d9d02d41f596c342e", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "measurement": { + "block_box": [ + 345, + 159, + 387, + 201 + ], + "block_center_xy": [ + 0.07666666666666666, + 0.0 + ], + "centroid_pixel_xy": [ + 365.085020242915, + 179.50722961249278 + ], + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 26.349999999996275, + "operation_id": "agent-12d303b3be23401d9cdd1b1296f3c87a", + "source_kind": "mujoco_simulation", + "state_sha256": "7ced546541611d509086f34fc585c77771e272c65f3bbb518c94fb1942dbcd16", + "version": 1, + "worker_pid": 77998 + }, + "state_sha256": "7ced546541611d509086f34fc585c77771e272c65f3bbb518c94fb1942dbcd16" + } + ] + }, + "transport_command_count": 83, + "transport_journal_bound": true, + "ui_steps": [ + "body_uncertain_recovered", + "body_uncertain_resumed", + "completed", + "disconnected", + "offline_receipt", + "reconnected", + "recovered", + "reloaded", + "resumed", + "uncertain" + ] +} diff --git a/jevdual/results/workbench-restarts-20261012/report.md b/jevdual/results/workbench-restarts-20261012/report.md new file mode 100644 index 0000000..a79ebb7 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/report.md @@ -0,0 +1,29 @@ +# Mid-job restart recovery + +All four graceful process restart cases passed in the actual browser with actual Codex planning. The first model trial ran on frozen source `d7072107d576d7a09643229ae584a3864d2fe0f6`; measured source stayed unchanged. Two jobs completed on one continuous simulated body, with independently checked placement, one SQLite measurement each and contact resets. No uncertain action was resent. + +| Interruption | Observed recovery | Independent check | +| --- | --- | --- | +| Workbench while planning | Browser disconnected; owned planner exited; retained job returned paused | Same identity and command records; explicit Resume preceded fresh observation | +| Workbench with uncertain dispatch | Same pending command returned blocked; Check receipt recovered it and left the job paused | Five commands throughout restart, disconnect, reconnect and receipt recovery; one send of the lost command | +| Body while planning | Browser showed Body unavailable; planner cancelled; reboot returned paused | Body identity and motion state retained; epoch advanced from one to two; fresh planning followed Resume | +| Body with uncertain dispatch | Same command stayed unresolved through outage and reboot; historical receipt recovered while paused | Boot-two command recovered during boot three with 46 command records unchanged; one send; fresh observation after Resume | + +The [restart score](restart-score.json) passed every boundary and all ten required visible states. The [native/model score](qualification-score.json) bound each exact TCP image to its model input, every applied decision to its typed proposal and lower receipt, and each command to one client send. It ran after all seven owned trial processes had exited. Two cancelled planning calls and a later health cancellation were reaped and withheld; none supplied an action. + +| Job | Model requests | Reset generation | Native time at reset | Contact samples | +| --- | ---: | ---: | ---: | ---: | +| Restart inspection 01 | 14 | 10 | 21.680 s | 3,174 | +| Restart inspection 02 | 15 | 20 | 42.630 s | 3,314 | + +The run retained 83 command records and 29 model requests, including three cancellations. Browser reload preserved both completed jobs and sent no command. After scoring, both services restarted at the same roots and ports for handoff: [workbench](http://127.0.0.1:51377/), body port 51373. The host is paused, body boot four is verified, both results remain visible, and no job, model request or uncertain command is pending. Owned handoff PIDs and identities are in [handoff.json](handoff.json). + +Testing first reproduced a fast-reboot defect: an old decision could acquire a new boot's lease when its outage escaped polling. The runtime now invalidates that decision at either health readback or command admission, requiring explicit Resume and a fresh observation. Restarted and recovered unfinished jobs show Paused. Body/core/client freshness, identity, lease and effect guards were not edited. + +Conformance passed 103 checks: nineteen runtime socket cases, eighty core/network/HTTP regressions and four synthetic harness control/ownership checks. Ruff, JavaScript syntax, notebook and diff checks passed. Before-fix failures, an abbreviated-SHA startup refusal before any process or model launch, and the preserved previous product are recorded in [attempt-ledger.json](attempt-ledger.json). + +One additional health timeout during reset cancelled a model call and paused the second job. Later read-only health succeeded; explicit Resume used a fresh image and completed the job. Its cause remains unresolved. This is a product reliability limitation retained in [health-timeout-observation.json](health-timeout-observation.json), rather than an excluded attempt. No source changed during that recovery. + +Scope is graceful process restarts in same-Mac loopback simulation. Sudden power loss, process kill during native persistence, hardware and authenticated remote access remain untested. Full browser captures and snapshots accompany the compact UI ledger; later ledger entries use main-panel excerpts to stay within its existing 64 KiB bound. Private model prompts and process output remain in ignored trial storage. Reproduce with the [runbook](../../../tools/physical-workcell/NETWORK_WORKBENCH.md) and [preregistered experiment](../../docs/experiments/SIM11-workbench-restarts.md). + +![Historical command recovered after body reboot; agent remains paused](ui/body-uncertain-recovered.jpg) diff --git a/jevdual/results/workbench-restarts-20261012/restart-ledger.json b/jevdual/results/workbench-restarts-20261012/restart-ledger.json new file mode 100644 index 0000000..a35e0e9 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/restart-ledger.json @@ -0,0 +1,246 @@ +{ + "boundaries": [ + { + "action_performed": true, + "after": "000001-after.json", + "before": "000001-before.json", + "passed": true, + "process": { + "boot": 1, + "exit_observed": true, + "initialized": false, + "log": "workbench-01.log", + "pid": 25794, + "port": 51377, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator", + "stopped_time": 1791828631.66827 + }, + "request": { + "action": "stop-role", + "expect": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": true, + "recovery_command_id": null, + "runner_status": "running", + "usage_calls": 1 + }, + "predicate": "planning", + "requested_time": 1791828631.2835982, + "role": "workbench", + "sequence": 1 + } + }, + { + "action_performed": true, + "after": "000002-after.json", + "before": "000002-before.json", + "passed": true, + "pid": 30888, + "predicate": "planning", + "request": { + "action": "start-role", + "expect": null, + "predicate": "none", + "requested_time": 1791828682.922372, + "role": "workbench", + "sequence": 2 + }, + "stopped_sequence": 1 + }, + { + "action_performed": true, + "after": "000003-after.json", + "before": "000003-before.json", + "passed": true, + "process": { + "boot": 2, + "exit_observed": true, + "initialized": false, + "log": "workbench-02.log", + "pid": 30888, + "port": 51377, + "returncode": 0, + "role": "workbench", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator", + "stopped_time": 1791828768.2145522 + }, + "request": { + "action": "stop-role", + "expect": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "runner_status": "blocked", + "usage_calls": 2 + }, + "predicate": "uncertain", + "requested_time": 1791828767.342037, + "role": "workbench", + "sequence": 3 + } + }, + { + "action_performed": true, + "after": "000004-after.json", + "before": "000004-before.json", + "passed": true, + "pid": 39967, + "predicate": "uncertain", + "request": { + "action": "start-role", + "expect": null, + "predicate": "none", + "requested_time": 1791828779.372716, + "role": "workbench", + "sequence": 4 + }, + "stopped_sequence": 3 + }, + { + "action_performed": true, + "after": "000005-after.json", + "before": "000005-before.json", + "passed": true, + "process": { + "boot": 1, + "exit_observed": true, + "initialized": true, + "log": "body-01.log", + "pid": 25787, + "port": 51373, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/body", + "stopped_time": 1791828952.994875 + }, + "request": { + "action": "stop-role", + "expect": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": true, + "recovery_command_id": null, + "runner_status": "running", + "usage_calls": 15 + }, + "predicate": "planning", + "requested_time": 1791828952.679998, + "role": "body", + "sequence": 5 + } + }, + { + "action_performed": true, + "after": "000006-after.json", + "before": "000006-before.json", + "passed": true, + "pid": 71324, + "predicate": "planning", + "request": { + "action": "start-role", + "expect": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": null, + "runner_status": "paused", + "usage_calls": 15 + }, + "predicate": "none", + "requested_time": 1791829008.869165, + "role": "body", + "sequence": 6 + }, + "stopped_sequence": 5 + }, + { + "action_performed": true, + "after": "000007-after.json", + "before": "000007-before.json", + "passed": true, + "process": { + "boot": 2, + "exit_observed": true, + "initialized": false, + "log": "body-02.log", + "pid": 71324, + "port": 51373, + "returncode": 0, + "role": "body", + "root": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/body", + "stopped_time": 1791829042.1212611 + }, + "request": { + "action": "stop-role", + "expect": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "runner_status": "blocked", + "usage_calls": 16 + }, + "predicate": "uncertain", + "requested_time": 1791829041.8267992, + "role": "body", + "sequence": 7 + } + }, + { + "action_performed": true, + "after": "000008-after.json", + "before": "000008-before.json", + "passed": true, + "pid": 74921, + "predicate": "uncertain", + "request": { + "action": "start-role", + "expect": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "runner_status": "paused", + "usage_calls": 16 + }, + "predicate": "none", + "requested_time": 1791829079.469436, + "role": "body", + "sequence": 8 + }, + "stopped_sequence": 7 + }, + { + "action_performed": false, + "after": "000009-after.json", + "before": "000009-before.json", + "passed": true, + "request": { + "action": "finish", + "expect": { + "active_job_id": null, + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "model_request_pending": false, + "recovery_command_id": null, + "runner_status": "running", + "usage_calls": 29 + }, + "predicate": "none", + "requested_time": 1791829364.559857, + "role": null, + "sequence": 9 + } + } + ] +} diff --git a/jevdual/results/workbench-restarts-20261012/restart-score.json b/jevdual/results/workbench-restarts-20261012/restart-score.json new file mode 100644 index 0000000..d5756f7 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/restart-score.json @@ -0,0 +1,99 @@ +{ + "legacy_qualification": { + "browser_transitions_passed": true, + "completed_cycles": 2, + "lost_reply_reconciled_without_resend": true, + "model_boundaries_passed": true, + "passed": true, + "source_unchanged": true + }, + "owned_processes_exited": true, + "passed": true, + "restart_boundaries": [ + { + "command_id": null, + "epoch_bound": true, + "fresh_observation_after_explicit_resume": true, + "historical_receipt_without_replay": true, + "model_reaped": true, + "outage_observed": true, + "owned_process_reaped": true, + "passed": true, + "paused_after_restart": true, + "pending_bound": true, + "predicate": "planning", + "role": "workbench", + "stable_ids": true, + "start_sequence": 2, + "stop_sequence": 1, + "unchanged_sends": true + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "epoch_bound": true, + "fresh_observation_after_explicit_resume": true, + "historical_receipt_without_replay": true, + "model_reaped": true, + "outage_observed": true, + "owned_process_reaped": true, + "passed": true, + "paused_after_restart": true, + "pending_bound": true, + "predicate": "uncertain", + "role": "workbench", + "stable_ids": true, + "start_sequence": 4, + "stop_sequence": 3, + "unchanged_sends": true + }, + { + "command_id": null, + "epoch_bound": true, + "fresh_observation_after_explicit_resume": true, + "historical_receipt_without_replay": true, + "model_reaped": true, + "outage_observed": true, + "owned_process_reaped": true, + "passed": true, + "paused_after_restart": true, + "pending_bound": true, + "predicate": "planning", + "role": "body", + "stable_ids": true, + "start_sequence": 6, + "stop_sequence": 5, + "unchanged_sends": true + }, + { + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "epoch_bound": true, + "fresh_observation_after_explicit_resume": true, + "historical_receipt_without_replay": true, + "model_reaped": true, + "outage_observed": true, + "owned_process_reaped": true, + "passed": true, + "paused_after_restart": true, + "pending_bound": true, + "predicate": "uncertain", + "role": "body", + "stable_ids": true, + "start_sequence": 8, + "stop_sequence": 7, + "unchanged_sends": true + } + ], + "scope": "four graceful owned mid-job restarts; actual Codex browser; loopback simulation", + "visible_restart_states": { + "body-planning-offline": true, + "body-planning-restored": true, + "body-uncertain-offline": true, + "body-uncertain-recovered": true, + "body-uncertain-restored": true, + "body-uncertain-resumed": true, + "workbench-planning-offline": true, + "workbench-planning-restored": true, + "workbench-uncertain-offline": true, + "workbench-uncertain-restored": true + } +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000001-after.json b/jevdual/results/workbench-restarts-20261012/snapshots/000001-after.json new file mode 100644 index 0000000..c812945 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000001-after.json @@ -0,0 +1,140 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "moves": 0, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "epoch": 1, + "expires_in_s": 24.078790416941047, + "lease_id": "08011306c044435da2272672ebbe5ba5" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "08011306c044435da2272672ebbe5ba5" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-134e7f48aa154ee1843c5a5d9250af9b-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "runner_status": "stopped", + "sequence": 10, + "stage": null, + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + } + ], + "processes": { + "body": 25787, + "workbench": null + }, + "public": null, + "public_error": "[Errno 61] Connection refused", + "time": 1791828631.668277 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000001-before.json b/jevdual/results/workbench-restarts-20261012/snapshots/000001-before.json new file mode 100644 index 0000000..ecfcc66 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000001-before.json @@ -0,0 +1,270 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "moves": 0, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "epoch": 1, + "expires_in_s": 24.430517291883007, + "lease_id": "08011306c044435da2272672ebbe5ba5" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "08011306c044435da2272672ebbe5ba5" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-134e7f48aa154ee1843c5a5d9250af9b-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "runner_status": "running", + "sequence": 9, + "stage": null, + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + } + ], + "processes": { + "body": 25787, + "workbench": 25794 + }, + "public": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "activity": [ + { + "intent": "", + "kind": "pause", + "outcome": "Pause requested; a command already admitted by this host may finish within body bounds", + "seq": 1, + "time": 1791828609.2092621 + }, + { + "intent": "Restart inspection 01", + "kind": "queued", + "outcome": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "seq": 2, + "time": 1791828615.833963 + }, + { + "intent": "Restart inspection 02", + "kind": "queued", + "outcome": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "seq": 3, + "time": 1791828616.64305 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 4, + "time": 1791828625.7236679 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 5, + "time": 1791828625.734359 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "start_job: command-36da4220dfcd4e85b9a4cee555ccd7aa", + "seq": 6, + "time": 1791828625.7491488 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 7, + "time": 1791828625.759552 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-e897a3cba83b4d83b460a556be5e459b", + "seq": 8, + "time": 1791828625.8979042 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "1", + "seq": 9, + "time": 1791828625.8994381 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": true, + "pending_operation": null, + "readiness": { + "ready": true, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0, + "token_usage_complete": false + } + }, + "time": 1791828631.315989 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000002-after.json b/jevdual/results/workbench-restarts-20261012/snapshots/000002-after.json new file mode 100644 index 0000000..1c2b672 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000002-after.json @@ -0,0 +1,277 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "moves": 0, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "epoch": 1, + "expires_in_s": 0, + "lease_id": "08011306c044435da2272672ebbe5ba5" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "08011306c044435da2272672ebbe5ba5" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-134e7f48aa154ee1843c5a5d9250af9b-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "runner_status": "paused", + "sequence": 10, + "stage": null, + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + } + ], + "processes": { + "body": 25787, + "workbench": 30888 + }, + "public": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "activity": [ + { + "intent": "", + "kind": "pause", + "outcome": "Pause requested; a command already admitted by this host may finish within body bounds", + "seq": 1, + "time": 1791828609.2092621 + }, + { + "intent": "Restart inspection 01", + "kind": "queued", + "outcome": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "seq": 2, + "time": 1791828615.833963 + }, + { + "intent": "Restart inspection 02", + "kind": "queued", + "outcome": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "seq": 3, + "time": 1791828616.64305 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 4, + "time": 1791828625.7236679 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 5, + "time": 1791828625.734359 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "start_job: command-36da4220dfcd4e85b9a4cee555ccd7aa", + "seq": 6, + "time": 1791828625.7491488 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 7, + "time": 1791828625.759552 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-e897a3cba83b4d83b460a556be5e459b", + "seq": 8, + "time": 1791828625.8979042 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "1", + "seq": 9, + "time": 1791828625.8994381 + }, + { + "intent": "", + "kind": "halt", + "outcome": "operator_pause_requested", + "seq": 10, + "time": 1791828631.352569 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 1, + "disturbance_test": false, + "error": "operator_pause_requested", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": false, + "reason": "Restarted; inspect the retained stage before explicit Resume" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "paused", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0, + "token_usage_complete": false + } + }, + "time": 1791828683.136389 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000002-before.json b/jevdual/results/workbench-restarts-20261012/snapshots/000002-before.json new file mode 100644 index 0000000..25d0091 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000002-before.json @@ -0,0 +1,140 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "moves": 0, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 0, + "jobs": {}, + "native_time_s": 1.0000000000000007, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "epoch": 1, + "expires_in_s": 0, + "lease_id": "08011306c044435da2272672ebbe5ba5" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "08011306c044435da2272672ebbe5ba5" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-134e7f48aa154ee1843c5a5d9250af9b-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "runner_status": "stopped", + "sequence": 10, + "stage": null, + "usage": { + "calls": 1, + "input_tokens": 0, + "output_tokens": 0, + "reported_calls": 0 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + } + ], + "processes": { + "body": 25787, + "workbench": null + }, + "public": null, + "public_error": "[Errno 61] Connection refused", + "time": 1791828682.949166 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000003-after.json b/jevdual/results/workbench-restarts-20261012/snapshots/000003-after.json new file mode 100644 index 0000000..a9d0a26 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000003-after.json @@ -0,0 +1,199 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "moves": 1, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 1, + "jobs": {}, + "native_time_s": 4.0999999999997705, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "epoch": 1, + "expires_in_s": 11.691162209026515, + "lease_id": "e7dc313216964cb388797a7054076bb8" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "unknown" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "e7dc313216964cb388797a7054076bb8" + }, + "pending_command_ids": [ + "command-24230a0f96bc4e84a84f720b1c974054" + ] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-8f4e7c93a38f43518d06e8ca79652ee1-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "runner_status": "stopped", + "sequence": 19, + "stage": { + "arguments": { + "proposal_id": "proposal-7638971758c9474484d815accb5fd532" + }, + "before_commands": [ + "command-1e2386315d6c4c7692405dac7dfa7bd1", + "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "command-933c11248d6a48079ead24676a2df7fb", + "command-e897a3cba83b4d83b460a556be5e459b" + ], + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "context": { + "action": { + "action": "move", + "intent": "Push the block right so its center enters the measurement zone.", + "target_x": 0.015, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/calls/20eb6586-e73a-4db4-aaac-db3e5d537329-02", + "decision_epoch": 1, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329" + }, + "control_epoch": 1, + "operation": "dispatch" + }, + "usage": { + "calls": 2, + "input_tokens": 13063, + "output_tokens": 199, + "reported_calls": 1 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + } + ], + "processes": { + "body": 25787, + "workbench": null + }, + "public": null, + "public_error": "[Errno 61] Connection refused", + "time": 1791828768.214575 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000003-before.json b/jevdual/results/workbench-restarts-20261012/snapshots/000003-before.json new file mode 100644 index 0000000..b85a59e --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000003-before.json @@ -0,0 +1,399 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "moves": 1, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 1, + "jobs": {}, + "native_time_s": 4.0999999999997705, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "epoch": 1, + "expires_in_s": 12.518944750074297, + "lease_id": "e7dc313216964cb388797a7054076bb8" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "unknown" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "e7dc313216964cb388797a7054076bb8" + }, + "pending_command_ids": [ + "command-24230a0f96bc4e84a84f720b1c974054" + ] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-8f4e7c93a38f43518d06e8ca79652ee1-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "runner_status": "blocked", + "sequence": 19, + "stage": { + "arguments": { + "proposal_id": "proposal-7638971758c9474484d815accb5fd532" + }, + "before_commands": [ + "command-1e2386315d6c4c7692405dac7dfa7bd1", + "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "command-933c11248d6a48079ead24676a2df7fb", + "command-e897a3cba83b4d83b460a556be5e459b" + ], + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "context": { + "action": { + "action": "move", + "intent": "Push the block right so its center enters the measurement zone.", + "target_x": 0.015, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/calls/20eb6586-e73a-4db4-aaac-db3e5d537329-02", + "decision_epoch": 1, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329" + }, + "control_epoch": 1, + "operation": "dispatch" + }, + "usage": { + "calls": 2, + "input_tokens": 13063, + "output_tokens": 199, + "reported_calls": 1 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + } + ], + "processes": { + "body": 25787, + "workbench": 30888 + }, + "public": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "activity": [ + { + "intent": "", + "kind": "pause", + "outcome": "Pause requested; a command already admitted by this host may finish within body bounds", + "seq": 1, + "time": 1791828609.2092621 + }, + { + "intent": "Restart inspection 01", + "kind": "queued", + "outcome": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "seq": 2, + "time": 1791828615.833963 + }, + { + "intent": "Restart inspection 02", + "kind": "queued", + "outcome": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "seq": 3, + "time": 1791828616.64305 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 4, + "time": 1791828625.7236679 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 5, + "time": 1791828625.734359 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "start_job: command-36da4220dfcd4e85b9a4cee555ccd7aa", + "seq": 6, + "time": 1791828625.7491488 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 7, + "time": 1791828625.759552 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-e897a3cba83b4d83b460a556be5e459b", + "seq": 8, + "time": 1791828625.8979042 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "1", + "seq": 9, + "time": 1791828625.8994381 + }, + { + "intent": "", + "kind": "halt", + "outcome": "operator_pause_requested", + "seq": 10, + "time": 1791828631.352569 + }, + { + "intent": "", + "kind": "fault_armed", + "outcome": "drop_after", + "seq": 11, + "time": 1791828738.0263119 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 12, + "time": 1791828738.312355 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 13, + "time": 1791828738.326447 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-933c11248d6a48079ead24676a2df7fb", + "seq": 14, + "time": 1791828738.4128268 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "2", + "seq": 15, + "time": 1791828738.414289 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 16, + "time": 1791828749.914588 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-1e2386315d6c4c7692405dac7dfa7bd1", + "seq": 17, + "time": 1791828749.932271 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 18, + "time": 1791828749.944431 + }, + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 19, + "time": 1791828750.08527 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 2, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Push the block right so its center enters the measurement zone.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "reply is uncertain; reconcile exact command without resending" + }, + "recovery": { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 2, + "input_tokens": 13063, + "output_tokens": 199, + "reported_calls": 1, + "token_usage_complete": false + } + }, + "time": 1791828767.3916018 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000004-after.json b/jevdual/results/workbench-restarts-20261012/snapshots/000004-after.json new file mode 100644 index 0000000..5840704 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000004-after.json @@ -0,0 +1,406 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "moves": 1, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 1, + "jobs": {}, + "native_time_s": 4.0999999999997705, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "epoch": 1, + "expires_in_s": 0.25754929217509925, + "lease_id": "e7dc313216964cb388797a7054076bb8" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "unknown" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "e7dc313216964cb388797a7054076bb8" + }, + "pending_command_ids": [ + "command-24230a0f96bc4e84a84f720b1c974054" + ] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-8f4e7c93a38f43518d06e8ca79652ee1-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "runner_status": "blocked", + "sequence": 20, + "stage": { + "arguments": { + "proposal_id": "proposal-7638971758c9474484d815accb5fd532" + }, + "before_commands": [ + "command-1e2386315d6c4c7692405dac7dfa7bd1", + "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "command-933c11248d6a48079ead24676a2df7fb", + "command-e897a3cba83b4d83b460a556be5e459b" + ], + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "context": { + "action": { + "action": "move", + "intent": "Push the block right so its center enters the measurement zone.", + "target_x": 0.015, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/calls/20eb6586-e73a-4db4-aaac-db3e5d537329-02", + "decision_epoch": 1, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329" + }, + "control_epoch": 1, + "operation": "dispatch" + }, + "usage": { + "calls": 2, + "input_tokens": 13063, + "output_tokens": 199, + "reported_calls": 1 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + } + ], + "processes": { + "body": 25787, + "workbench": 39967 + }, + "public": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "activity": [ + { + "intent": "", + "kind": "pause", + "outcome": "Pause requested; a command already admitted by this host may finish within body bounds", + "seq": 1, + "time": 1791828609.2092621 + }, + { + "intent": "Restart inspection 01", + "kind": "queued", + "outcome": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "seq": 2, + "time": 1791828615.833963 + }, + { + "intent": "Restart inspection 02", + "kind": "queued", + "outcome": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "seq": 3, + "time": 1791828616.64305 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 4, + "time": 1791828625.7236679 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 5, + "time": 1791828625.734359 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "start_job: command-36da4220dfcd4e85b9a4cee555ccd7aa", + "seq": 6, + "time": 1791828625.7491488 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 7, + "time": 1791828625.759552 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-e897a3cba83b4d83b460a556be5e459b", + "seq": 8, + "time": 1791828625.8979042 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "1", + "seq": 9, + "time": 1791828625.8994381 + }, + { + "intent": "", + "kind": "halt", + "outcome": "operator_pause_requested", + "seq": 10, + "time": 1791828631.352569 + }, + { + "intent": "", + "kind": "fault_armed", + "outcome": "drop_after", + "seq": 11, + "time": 1791828738.0263119 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 12, + "time": 1791828738.312355 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 13, + "time": 1791828738.326447 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-933c11248d6a48079ead24676a2df7fb", + "seq": 14, + "time": 1791828738.4128268 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "2", + "seq": 15, + "time": 1791828738.414289 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 16, + "time": 1791828749.914588 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-1e2386315d6c4c7692405dac7dfa7bd1", + "seq": 17, + "time": 1791828749.932271 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 18, + "time": 1791828749.944431 + }, + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 19, + "time": 1791828750.08527 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Restarted with a retained command stage; check its exact receipt", + "seq": 20, + "time": 1791828779.589948 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 2, + "disturbance_test": false, + "error": "Restarted with a retained command stage; check its exact receipt", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Push the block right so its center enters the measurement zone.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "Restarted with a retained command stage; check its exact receipt" + }, + "recovery": { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "operation": "dispatch", + "reason": "Restarted with a retained command stage; check its exact receipt", + "state": "uncertain" + }, + "runner_status": "blocked", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 2, + "input_tokens": 13063, + "output_tokens": 199, + "reported_calls": 1, + "token_usage_complete": false + } + }, + "time": 1791828779.6568692 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000004-before.json b/jevdual/results/workbench-restarts-20261012/snapshots/000004-before.json new file mode 100644 index 0000000..5516de1 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000004-before.json @@ -0,0 +1,199 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "moves": 1, + "phase": "prepare", + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 1, + "jobs": {}, + "native_time_s": 4.0999999999997705, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "epoch": 1, + "expires_in_s": 0.5128371671307832, + "lease_id": "e7dc313216964cb388797a7054076bb8" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "unknown" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "e7dc313216964cb388797a7054076bb8" + }, + "pending_command_ids": [ + "command-24230a0f96bc4e84a84f720b1c974054" + ] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 0, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-8f4e7c93a38f43518d06e8ca79652ee1-4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7.png", + "sha256": "4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "runner_status": "stopped", + "sequence": 19, + "stage": { + "arguments": { + "proposal_id": "proposal-7638971758c9474484d815accb5fd532" + }, + "before_commands": [ + "command-1e2386315d6c4c7692405dac7dfa7bd1", + "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "command-933c11248d6a48079ead24676a2df7fb", + "command-e897a3cba83b4d83b460a556be5e459b" + ], + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "context": { + "action": { + "action": "move", + "intent": "Push the block right so its center enters the measurement zone.", + "target_x": 0.015, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/calls/20eb6586-e73a-4db4-aaac-db3e5d537329-02", + "decision_epoch": 1, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329" + }, + "control_epoch": 1, + "operation": "dispatch" + }, + "usage": { + "calls": 2, + "input_tokens": 13063, + "output_tokens": 199, + "reported_calls": 1 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + } + ], + "processes": { + "body": 25787, + "workbench": null + }, + "public": null, + "public_error": "[Errno 61] Connection refused", + "time": 1791828779.410984 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000005-after.json b/jevdual/results/workbench-restarts-20261012/snapshots/000005-after.json new file mode 100644 index 0000000..b72ccf6 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000005-after.json @@ -0,0 +1,784 @@ +{ + "body_identity": null, + "body_identity_error": "[Errno 61] Connection refused", + "body_state": null, + "body_state_error": "[Errno 61] Connection refused", + "client": { + "commands": { + "command-015d5d03d0624f14ba43d889871c45ee": { + "operation": "propose", + "request_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "send_count": 1, + "state": "completed" + }, + "command-097e6bec1ce24ef2bc8f37c40f80d98f": { + "operation": "dispatch", + "request_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "send_count": 1, + "state": "completed" + }, + "command-0e35d66b16a64168b11d255a1d871e71": { + "operation": "observe", + "request_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "send_count": 1, + "state": "completed" + }, + "command-1936c404000d4b55a094c49a2fd9eeb3": { + "operation": "observe", + "request_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "send_count": 1, + "state": "completed" + }, + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-210150e3ac9f45adae24e89f22a66286": { + "operation": "dispatch", + "request_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "completed" + }, + "command-248b9798493e4a43a51259929e135e29": { + "operation": "observe", + "request_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "send_count": 1, + "state": "completed" + }, + "command-2a6e74bdbe424b3caa700dc047365a1c": { + "operation": "dispatch", + "request_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "send_count": 1, + "state": "completed" + }, + "command-2d0b436b61124502a9d8558816bda68d": { + "operation": "observe", + "request_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "send_count": 1, + "state": "completed" + }, + "command-3083d955014b4e5282809a47046c53db": { + "operation": "propose", + "request_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "send_count": 1, + "state": "completed" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-38a300b989a649fd9ee243704cdc65a9": { + "operation": "observe", + "request_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "send_count": 1, + "state": "completed" + }, + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e": { + "operation": "propose", + "request_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "send_count": 1, + "state": "completed" + }, + "command-553be6e82b274944b5e6ea9e2cd3902e": { + "operation": "propose", + "request_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "send_count": 1, + "state": "completed" + }, + "command-5ca0b6a7e99f4263b2239a95b02ca7e7": { + "operation": "propose", + "request_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "send_count": 1, + "state": "completed" + }, + "command-5ced22700795486fa68a50fcda4767b8": { + "operation": "propose", + "request_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "send_count": 1, + "state": "completed" + }, + "command-5e27abcb6c914c5ca0ae77ddee974bbc": { + "operation": "dispatch", + "request_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "send_count": 1, + "state": "completed" + }, + "command-7661dbdae1c34bd797ca17b3b5321a5e": { + "operation": "propose", + "request_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "send_count": 1, + "state": "completed" + }, + "command-7ce7b5eb9b5b4b1d953e624d2312327b": { + "operation": "dispatch", + "request_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "send_count": 1, + "state": "completed" + }, + "command-84c016d3c2564ecea6025edaea7d23e7": { + "operation": "propose", + "request_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "send_count": 1, + "state": "completed" + }, + "command-8c948c9df6074ee5969ef8b19a3eaddb": { + "operation": "propose", + "request_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-9dd2a0c9603f4667956ec976e822b74a": { + "operation": "observe", + "request_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "send_count": 1, + "state": "completed" + }, + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee": { + "operation": "dispatch", + "request_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "send_count": 1, + "state": "completed" + }, + "command-ab4679dd21e14ce08f70b1ef63bd5868": { + "operation": "observe", + "request_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "send_count": 1, + "state": "completed" + }, + "command-b17181d5918a492a916e0e3c5a2f3a11": { + "operation": "propose", + "request_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "send_count": 1, + "state": "completed" + }, + "command-b182534626d34766a52d5e11d48239e7": { + "operation": "observe", + "request_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "send_count": 1, + "state": "completed" + }, + "command-bdc662ab7f4645d79c02f84921f3c9f6": { + "operation": "dispatch", + "request_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "send_count": 1, + "state": "completed" + }, + "command-c80c18cc647b45fe8e9faf2fa890283c": { + "operation": "dispatch", + "request_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "send_count": 1, + "state": "completed" + }, + "command-c8be0caf76504410a68a595296d56bd3": { + "operation": "observe", + "request_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "send_count": 1, + "state": "completed" + }, + "command-d9922eefd392420b96bf0a62d7eea982": { + "operation": "dispatch", + "request_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "send_count": 1, + "state": "completed" + }, + "command-df199bef22b442168d0a42242c00c5c4": { + "operation": "observe", + "request_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "send_count": 1, + "state": "completed" + }, + "command-e19f5d23dd944e40b806e930f7d44b35": { + "operation": "dispatch", + "request_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "send_count": 1, + "state": "completed" + }, + "command-e1af2fbe5a254692bf212769ddfea045": { + "operation": "observe", + "request_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + }, + "command-ede04073e3654f4fa17f0a0056db9004": { + "operation": "dispatch", + "request_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "send_count": 1, + "state": "completed" + }, + "command-ef48ba673c2b4ae08b09fbbc4c56fca8": { + "operation": "start_job", + "request_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "send_count": 1, + "state": "completed" + }, + "command-f008b852a29348218bc57e931887b963": { + "operation": "dispatch", + "request_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "send_count": 1, + "state": "completed" + }, + "command-f0d26ac5d6914f1bb038a57ee515b7f0": { + "operation": "observe", + "request_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "send_count": 1, + "state": "completed" + }, + "command-f4c202be91064d3e8f4c929f1175450e": { + "operation": "propose", + "request_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "send_count": 1, + "state": "completed" + }, + "command-f7b8d8e5cddb47ecbac988938ccee2a0": { + "operation": "observe", + "request_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "send_count": 1, + "state": "completed" + }, + "command-fda8dcacccc940c493511804050cc0ec": { + "operation": "propose", + "request_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "f816c68447d947988c70c3362c619003" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 10, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-71f5f95d2a1f4b91ab54de93a921a68c-118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa.png", + "sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "runner_status": "running", + "sequence": 114, + "stage": null, + "usage": { + "calls": 15, + "input_tokens": 172372, + "output_tokens": 2008, + "reported_calls": 13 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + }, + { + "command_id": "command-0e35d66b16a64168b11d255a1d871e71", + "payload_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "sequence": 67 + }, + { + "command_id": "command-fda8dcacccc940c493511804050cc0ec", + "payload_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "sequence": 79 + }, + { + "command_id": "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "payload_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "sequence": 87 + }, + { + "command_id": "command-c8be0caf76504410a68a595296d56bd3", + "payload_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "sequence": 95 + }, + { + "command_id": "command-f4c202be91064d3e8f4c929f1175450e", + "payload_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "sequence": 107 + }, + { + "command_id": "command-e19f5d23dd944e40b806e930f7d44b35", + "payload_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "sequence": 115 + }, + { + "command_id": "command-1936c404000d4b55a094c49a2fd9eeb3", + "payload_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "sequence": 123 + }, + { + "command_id": "command-8c948c9df6074ee5969ef8b19a3eaddb", + "payload_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "sequence": 135 + }, + { + "command_id": "command-bdc662ab7f4645d79c02f84921f3c9f6", + "payload_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "sequence": 143 + }, + { + "command_id": "command-e1af2fbe5a254692bf212769ddfea045", + "payload_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "sequence": 151 + }, + { + "command_id": "command-b17181d5918a492a916e0e3c5a2f3a11", + "payload_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "sequence": 163 + }, + { + "command_id": "command-2a6e74bdbe424b3caa700dc047365a1c", + "payload_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "sequence": 171 + }, + { + "command_id": "command-df199bef22b442168d0a42242c00c5c4", + "payload_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "sequence": 179 + }, + { + "command_id": "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "payload_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "sequence": 191 + }, + { + "command_id": "command-f008b852a29348218bc57e931887b963", + "payload_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "sequence": 199 + }, + { + "command_id": "command-2d0b436b61124502a9d8558816bda68d", + "payload_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "sequence": 207 + }, + { + "command_id": "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "payload_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "sequence": 219 + }, + { + "command_id": "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "payload_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "sequence": 227 + }, + { + "command_id": "command-b182534626d34766a52d5e11d48239e7", + "payload_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "sequence": 235 + }, + { + "command_id": "command-84c016d3c2564ecea6025edaea7d23e7", + "payload_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "sequence": 247 + }, + { + "command_id": "command-ede04073e3654f4fa17f0a0056db9004", + "payload_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "sequence": 255 + }, + { + "command_id": "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "payload_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "sequence": 263 + }, + { + "command_id": "command-5ced22700795486fa68a50fcda4767b8", + "payload_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "sequence": 275 + }, + { + "command_id": "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "payload_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "sequence": 283 + }, + { + "command_id": "command-9dd2a0c9603f4667956ec976e822b74a", + "payload_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "sequence": 291 + }, + { + "command_id": "command-3083d955014b4e5282809a47046c53db", + "payload_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "sequence": 303 + }, + { + "command_id": "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "payload_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "sequence": 311 + }, + { + "command_id": "command-38a300b989a649fd9ee243704cdc65a9", + "payload_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "sequence": 319 + }, + { + "command_id": "command-553be6e82b274944b5e6ea9e2cd3902e", + "payload_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "sequence": 331 + }, + { + "command_id": "command-d9922eefd392420b96bf0a62d7eea982", + "payload_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "sequence": 339 + }, + { + "command_id": "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "payload_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "sequence": 347 + }, + { + "command_id": "command-7661dbdae1c34bd797ca17b3b5321a5e", + "payload_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "sequence": 359 + }, + { + "command_id": "command-c80c18cc647b45fe8e9faf2fa890283c", + "payload_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "sequence": 367 + }, + { + "command_id": "command-248b9798493e4a43a51259929e135e29", + "payload_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "sequence": 375 + }, + { + "command_id": "command-015d5d03d0624f14ba43d889871c45ee", + "payload_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "sequence": 387 + }, + { + "command_id": "command-210150e3ac9f45adae24e89f22a66286", + "payload_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "sequence": 395 + }, + { + "command_id": "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "payload_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "sequence": 403 + }, + { + "command_id": "command-ab4679dd21e14ce08f70b1ef63bd5868", + "payload_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "sequence": 411 + } + ], + "processes": { + "body": null, + "workbench": 39967 + }, + "public": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "activity": [ + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "12", + "seq": 91, + "time": 1791828921.078729 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 92, + "time": 1791828929.681102 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-553be6e82b274944b5e6ea9e2cd3902e", + "seq": 93, + "time": 1791828929.76928 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 94, + "time": 1791828929.815944 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-d9922eefd392420b96bf0a62d7eea982", + "seq": 95, + "time": 1791828930.0683122 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 96, + "time": 1791828930.114275 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-f7b8d8e5cddb47ecbac988938ccee2a0", + "seq": 97, + "time": 1791828930.315923 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "13", + "seq": 98, + "time": 1791828930.318308 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 99, + "time": 1791828937.617022 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-7661dbdae1c34bd797ca17b3b5321a5e", + "seq": 100, + "time": 1791828937.772455 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 101, + "time": 1791828937.839915 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-c80c18cc647b45fe8e9faf2fa890283c", + "seq": 102, + "time": 1791828938.114055 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 103, + "time": 1791828938.266146 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-248b9798493e4a43a51259929e135e29", + "seq": 104, + "time": 1791828938.604191 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "14", + "seq": 105, + "time": 1791828938.608861 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 106, + "time": 1791828950.833822 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-015d5d03d0624f14ba43d889871c45ee", + "seq": 107, + "time": 1791828950.9307368 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 108, + "time": 1791828950.97887 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-210150e3ac9f45adae24e89f22a66286", + "seq": 109, + "time": 1791828951.468124 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 110, + "time": 1791828951.5941691 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "start_job: command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "seq": 111, + "time": 1791828951.745876 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 112, + "time": 1791828951.8522959 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-ab4679dd21e14ce08f70b1ef63bd5868", + "seq": 113, + "time": 1791828952.1878028 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "1", + "seq": 114, + "time": 1791828952.191465 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 1, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "B" + } + ], + "model_request_pending": true, + "pending_operation": null, + "readiness": { + "ready": true, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 15, + "input_tokens": 172372, + "output_tokens": 2008, + "reported_calls": 13, + "token_usage_complete": false + } + }, + "time": 1791828952.994922 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000005-before.json b/jevdual/results/workbench-restarts-20261012/snapshots/000005-before.json new file mode 100644 index 0000000..049cc57 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000005-before.json @@ -0,0 +1,892 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "moves": 0, + "phase": "prepare", + "started_generation": 10, + "started_native_time_s": 21.679999999998863, + "target_zone": { + "x_max": 0.115, + "x_min": 0.045, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 10, + "jobs": { + "20eb6586-e73a-4db4-aaac-db3e5d537329": { + "completed_generation": 10, + "completed_native_time_s": 21.679999999998863, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement_operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "measurement_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.46799999999962, + "operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "source_kind": "mujoco_simulation", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9", + "version": 1, + "worker_pid": 51928 + }, + "moves": 10, + "phase": "completed", + "reset_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "reset_started_generation": 2, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 21.679999999998863, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "epoch": 1, + "expires_in_s": 29.00711249979213, + "lease_id": "f816c68447d947988c70c3362c619003" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-015d5d03d0624f14ba43d889871c45ee": { + "operation": "propose", + "request_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "send_count": 1, + "state": "completed" + }, + "command-097e6bec1ce24ef2bc8f37c40f80d98f": { + "operation": "dispatch", + "request_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "send_count": 1, + "state": "completed" + }, + "command-0e35d66b16a64168b11d255a1d871e71": { + "operation": "observe", + "request_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "send_count": 1, + "state": "completed" + }, + "command-1936c404000d4b55a094c49a2fd9eeb3": { + "operation": "observe", + "request_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "send_count": 1, + "state": "completed" + }, + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-210150e3ac9f45adae24e89f22a66286": { + "operation": "dispatch", + "request_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "completed" + }, + "command-248b9798493e4a43a51259929e135e29": { + "operation": "observe", + "request_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "send_count": 1, + "state": "completed" + }, + "command-2a6e74bdbe424b3caa700dc047365a1c": { + "operation": "dispatch", + "request_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "send_count": 1, + "state": "completed" + }, + "command-2d0b436b61124502a9d8558816bda68d": { + "operation": "observe", + "request_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "send_count": 1, + "state": "completed" + }, + "command-3083d955014b4e5282809a47046c53db": { + "operation": "propose", + "request_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "send_count": 1, + "state": "completed" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-38a300b989a649fd9ee243704cdc65a9": { + "operation": "observe", + "request_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "send_count": 1, + "state": "completed" + }, + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e": { + "operation": "propose", + "request_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "send_count": 1, + "state": "completed" + }, + "command-553be6e82b274944b5e6ea9e2cd3902e": { + "operation": "propose", + "request_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "send_count": 1, + "state": "completed" + }, + "command-5ca0b6a7e99f4263b2239a95b02ca7e7": { + "operation": "propose", + "request_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "send_count": 1, + "state": "completed" + }, + "command-5ced22700795486fa68a50fcda4767b8": { + "operation": "propose", + "request_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "send_count": 1, + "state": "completed" + }, + "command-5e27abcb6c914c5ca0ae77ddee974bbc": { + "operation": "dispatch", + "request_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "send_count": 1, + "state": "completed" + }, + "command-7661dbdae1c34bd797ca17b3b5321a5e": { + "operation": "propose", + "request_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "send_count": 1, + "state": "completed" + }, + "command-7ce7b5eb9b5b4b1d953e624d2312327b": { + "operation": "dispatch", + "request_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "send_count": 1, + "state": "completed" + }, + "command-84c016d3c2564ecea6025edaea7d23e7": { + "operation": "propose", + "request_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "send_count": 1, + "state": "completed" + }, + "command-8c948c9df6074ee5969ef8b19a3eaddb": { + "operation": "propose", + "request_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-9dd2a0c9603f4667956ec976e822b74a": { + "operation": "observe", + "request_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "send_count": 1, + "state": "completed" + }, + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee": { + "operation": "dispatch", + "request_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "send_count": 1, + "state": "completed" + }, + "command-ab4679dd21e14ce08f70b1ef63bd5868": { + "operation": "observe", + "request_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "send_count": 1, + "state": "completed" + }, + "command-b17181d5918a492a916e0e3c5a2f3a11": { + "operation": "propose", + "request_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "send_count": 1, + "state": "completed" + }, + "command-b182534626d34766a52d5e11d48239e7": { + "operation": "observe", + "request_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "send_count": 1, + "state": "completed" + }, + "command-bdc662ab7f4645d79c02f84921f3c9f6": { + "operation": "dispatch", + "request_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "send_count": 1, + "state": "completed" + }, + "command-c80c18cc647b45fe8e9faf2fa890283c": { + "operation": "dispatch", + "request_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "send_count": 1, + "state": "completed" + }, + "command-c8be0caf76504410a68a595296d56bd3": { + "operation": "observe", + "request_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "send_count": 1, + "state": "completed" + }, + "command-d9922eefd392420b96bf0a62d7eea982": { + "operation": "dispatch", + "request_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "send_count": 1, + "state": "completed" + }, + "command-df199bef22b442168d0a42242c00c5c4": { + "operation": "observe", + "request_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "send_count": 1, + "state": "completed" + }, + "command-e19f5d23dd944e40b806e930f7d44b35": { + "operation": "dispatch", + "request_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "send_count": 1, + "state": "completed" + }, + "command-e1af2fbe5a254692bf212769ddfea045": { + "operation": "observe", + "request_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + }, + "command-ede04073e3654f4fa17f0a0056db9004": { + "operation": "dispatch", + "request_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "send_count": 1, + "state": "completed" + }, + "command-ef48ba673c2b4ae08b09fbbc4c56fca8": { + "operation": "start_job", + "request_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "send_count": 1, + "state": "completed" + }, + "command-f008b852a29348218bc57e931887b963": { + "operation": "dispatch", + "request_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "send_count": 1, + "state": "completed" + }, + "command-f0d26ac5d6914f1bb038a57ee515b7f0": { + "operation": "observe", + "request_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "send_count": 1, + "state": "completed" + }, + "command-f4c202be91064d3e8f4c929f1175450e": { + "operation": "propose", + "request_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "send_count": 1, + "state": "completed" + }, + "command-f7b8d8e5cddb47ecbac988938ccee2a0": { + "operation": "observe", + "request_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "send_count": 1, + "state": "completed" + }, + "command-fda8dcacccc940c493511804050cc0ec": { + "operation": "propose", + "request_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "f816c68447d947988c70c3362c619003" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 10, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-71f5f95d2a1f4b91ab54de93a921a68c-118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa.png", + "sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "runner_status": "running", + "sequence": 114, + "stage": null, + "usage": { + "calls": 15, + "input_tokens": 172372, + "output_tokens": 2008, + "reported_calls": 13 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + }, + { + "command_id": "command-0e35d66b16a64168b11d255a1d871e71", + "payload_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "sequence": 67 + }, + { + "command_id": "command-fda8dcacccc940c493511804050cc0ec", + "payload_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "sequence": 79 + }, + { + "command_id": "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "payload_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "sequence": 87 + }, + { + "command_id": "command-c8be0caf76504410a68a595296d56bd3", + "payload_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "sequence": 95 + }, + { + "command_id": "command-f4c202be91064d3e8f4c929f1175450e", + "payload_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "sequence": 107 + }, + { + "command_id": "command-e19f5d23dd944e40b806e930f7d44b35", + "payload_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "sequence": 115 + }, + { + "command_id": "command-1936c404000d4b55a094c49a2fd9eeb3", + "payload_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "sequence": 123 + }, + { + "command_id": "command-8c948c9df6074ee5969ef8b19a3eaddb", + "payload_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "sequence": 135 + }, + { + "command_id": "command-bdc662ab7f4645d79c02f84921f3c9f6", + "payload_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "sequence": 143 + }, + { + "command_id": "command-e1af2fbe5a254692bf212769ddfea045", + "payload_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "sequence": 151 + }, + { + "command_id": "command-b17181d5918a492a916e0e3c5a2f3a11", + "payload_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "sequence": 163 + }, + { + "command_id": "command-2a6e74bdbe424b3caa700dc047365a1c", + "payload_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "sequence": 171 + }, + { + "command_id": "command-df199bef22b442168d0a42242c00c5c4", + "payload_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "sequence": 179 + }, + { + "command_id": "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "payload_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "sequence": 191 + }, + { + "command_id": "command-f008b852a29348218bc57e931887b963", + "payload_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "sequence": 199 + }, + { + "command_id": "command-2d0b436b61124502a9d8558816bda68d", + "payload_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "sequence": 207 + }, + { + "command_id": "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "payload_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "sequence": 219 + }, + { + "command_id": "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "payload_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "sequence": 227 + }, + { + "command_id": "command-b182534626d34766a52d5e11d48239e7", + "payload_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "sequence": 235 + }, + { + "command_id": "command-84c016d3c2564ecea6025edaea7d23e7", + "payload_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "sequence": 247 + }, + { + "command_id": "command-ede04073e3654f4fa17f0a0056db9004", + "payload_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "sequence": 255 + }, + { + "command_id": "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "payload_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "sequence": 263 + }, + { + "command_id": "command-5ced22700795486fa68a50fcda4767b8", + "payload_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "sequence": 275 + }, + { + "command_id": "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "payload_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "sequence": 283 + }, + { + "command_id": "command-9dd2a0c9603f4667956ec976e822b74a", + "payload_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "sequence": 291 + }, + { + "command_id": "command-3083d955014b4e5282809a47046c53db", + "payload_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "sequence": 303 + }, + { + "command_id": "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "payload_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "sequence": 311 + }, + { + "command_id": "command-38a300b989a649fd9ee243704cdc65a9", + "payload_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "sequence": 319 + }, + { + "command_id": "command-553be6e82b274944b5e6ea9e2cd3902e", + "payload_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "sequence": 331 + }, + { + "command_id": "command-d9922eefd392420b96bf0a62d7eea982", + "payload_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "sequence": 339 + }, + { + "command_id": "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "payload_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "sequence": 347 + }, + { + "command_id": "command-7661dbdae1c34bd797ca17b3b5321a5e", + "payload_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "sequence": 359 + }, + { + "command_id": "command-c80c18cc647b45fe8e9faf2fa890283c", + "payload_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "sequence": 367 + }, + { + "command_id": "command-248b9798493e4a43a51259929e135e29", + "payload_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "sequence": 375 + }, + { + "command_id": "command-015d5d03d0624f14ba43d889871c45ee", + "payload_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "sequence": 387 + }, + { + "command_id": "command-210150e3ac9f45adae24e89f22a66286", + "payload_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "sequence": 395 + }, + { + "command_id": "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "payload_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "sequence": 403 + }, + { + "command_id": "command-ab4679dd21e14ce08f70b1ef63bd5868", + "payload_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "sequence": 411 + } + ], + "processes": { + "body": 25787, + "workbench": 39967 + }, + "public": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "activity": [ + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "12", + "seq": 91, + "time": 1791828921.078729 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 92, + "time": 1791828929.681102 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-553be6e82b274944b5e6ea9e2cd3902e", + "seq": 93, + "time": 1791828929.76928 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 94, + "time": 1791828929.815944 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-d9922eefd392420b96bf0a62d7eea982", + "seq": 95, + "time": 1791828930.0683122 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 96, + "time": 1791828930.114275 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-f7b8d8e5cddb47ecbac988938ccee2a0", + "seq": 97, + "time": 1791828930.315923 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "13", + "seq": 98, + "time": 1791828930.318308 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 99, + "time": 1791828937.617022 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-7661dbdae1c34bd797ca17b3b5321a5e", + "seq": 100, + "time": 1791828937.772455 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 101, + "time": 1791828937.839915 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-c80c18cc647b45fe8e9faf2fa890283c", + "seq": 102, + "time": 1791828938.114055 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 103, + "time": 1791828938.266146 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-248b9798493e4a43a51259929e135e29", + "seq": 104, + "time": 1791828938.604191 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "14", + "seq": 105, + "time": 1791828938.608861 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 106, + "time": 1791828950.833822 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-015d5d03d0624f14ba43d889871c45ee", + "seq": 107, + "time": 1791828950.9307368 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 108, + "time": 1791828950.97887 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-210150e3ac9f45adae24e89f22a66286", + "seq": 109, + "time": 1791828951.468124 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 110, + "time": 1791828951.5941691 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "start_job: command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "seq": 111, + "time": 1791828951.745876 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 112, + "time": 1791828951.8522959 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-ab4679dd21e14ce08f70b1ef63bd5868", + "seq": 113, + "time": 1791828952.1878028 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "1", + "seq": 114, + "time": 1791828952.191465 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 1, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "B" + } + ], + "model_request_pending": true, + "pending_operation": null, + "readiness": { + "ready": true, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 15, + "input_tokens": 172372, + "output_tokens": 2008, + "reported_calls": 13, + "token_usage_complete": false + } + }, + "time": 1791828952.71952 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000006-after.json b/jevdual/results/workbench-restarts-20261012/snapshots/000006-after.json new file mode 100644 index 0000000..5ed24d1 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000006-after.json @@ -0,0 +1,887 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "moves": 0, + "phase": "prepare", + "started_generation": 10, + "started_native_time_s": 21.679999999998863, + "target_zone": { + "x_max": 0.115, + "x_min": 0.045, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 10, + "jobs": { + "20eb6586-e73a-4db4-aaac-db3e5d537329": { + "completed_generation": 10, + "completed_native_time_s": 21.679999999998863, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement_operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "measurement_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.46799999999962, + "operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "source_kind": "mujoco_simulation", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9", + "version": 1, + "worker_pid": 51928 + }, + "moves": 10, + "phase": "completed", + "reset_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "reset_started_generation": 2, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 21.679999999998863, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": null, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-015d5d03d0624f14ba43d889871c45ee": { + "operation": "propose", + "request_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "send_count": 1, + "state": "completed" + }, + "command-097e6bec1ce24ef2bc8f37c40f80d98f": { + "operation": "dispatch", + "request_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "send_count": 1, + "state": "completed" + }, + "command-0e35d66b16a64168b11d255a1d871e71": { + "operation": "observe", + "request_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "send_count": 1, + "state": "completed" + }, + "command-1936c404000d4b55a094c49a2fd9eeb3": { + "operation": "observe", + "request_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "send_count": 1, + "state": "completed" + }, + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-210150e3ac9f45adae24e89f22a66286": { + "operation": "dispatch", + "request_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "completed" + }, + "command-248b9798493e4a43a51259929e135e29": { + "operation": "observe", + "request_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "send_count": 1, + "state": "completed" + }, + "command-2a6e74bdbe424b3caa700dc047365a1c": { + "operation": "dispatch", + "request_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "send_count": 1, + "state": "completed" + }, + "command-2d0b436b61124502a9d8558816bda68d": { + "operation": "observe", + "request_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "send_count": 1, + "state": "completed" + }, + "command-3083d955014b4e5282809a47046c53db": { + "operation": "propose", + "request_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "send_count": 1, + "state": "completed" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-38a300b989a649fd9ee243704cdc65a9": { + "operation": "observe", + "request_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "send_count": 1, + "state": "completed" + }, + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e": { + "operation": "propose", + "request_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "send_count": 1, + "state": "completed" + }, + "command-553be6e82b274944b5e6ea9e2cd3902e": { + "operation": "propose", + "request_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "send_count": 1, + "state": "completed" + }, + "command-5ca0b6a7e99f4263b2239a95b02ca7e7": { + "operation": "propose", + "request_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "send_count": 1, + "state": "completed" + }, + "command-5ced22700795486fa68a50fcda4767b8": { + "operation": "propose", + "request_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "send_count": 1, + "state": "completed" + }, + "command-5e27abcb6c914c5ca0ae77ddee974bbc": { + "operation": "dispatch", + "request_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "send_count": 1, + "state": "completed" + }, + "command-7661dbdae1c34bd797ca17b3b5321a5e": { + "operation": "propose", + "request_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "send_count": 1, + "state": "completed" + }, + "command-7ce7b5eb9b5b4b1d953e624d2312327b": { + "operation": "dispatch", + "request_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "send_count": 1, + "state": "completed" + }, + "command-84c016d3c2564ecea6025edaea7d23e7": { + "operation": "propose", + "request_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "send_count": 1, + "state": "completed" + }, + "command-8c948c9df6074ee5969ef8b19a3eaddb": { + "operation": "propose", + "request_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-9dd2a0c9603f4667956ec976e822b74a": { + "operation": "observe", + "request_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "send_count": 1, + "state": "completed" + }, + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee": { + "operation": "dispatch", + "request_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "send_count": 1, + "state": "completed" + }, + "command-ab4679dd21e14ce08f70b1ef63bd5868": { + "operation": "observe", + "request_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "send_count": 1, + "state": "completed" + }, + "command-b17181d5918a492a916e0e3c5a2f3a11": { + "operation": "propose", + "request_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "send_count": 1, + "state": "completed" + }, + "command-b182534626d34766a52d5e11d48239e7": { + "operation": "observe", + "request_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "send_count": 1, + "state": "completed" + }, + "command-bdc662ab7f4645d79c02f84921f3c9f6": { + "operation": "dispatch", + "request_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "send_count": 1, + "state": "completed" + }, + "command-c80c18cc647b45fe8e9faf2fa890283c": { + "operation": "dispatch", + "request_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "send_count": 1, + "state": "completed" + }, + "command-c8be0caf76504410a68a595296d56bd3": { + "operation": "observe", + "request_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "send_count": 1, + "state": "completed" + }, + "command-d9922eefd392420b96bf0a62d7eea982": { + "operation": "dispatch", + "request_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "send_count": 1, + "state": "completed" + }, + "command-df199bef22b442168d0a42242c00c5c4": { + "operation": "observe", + "request_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "send_count": 1, + "state": "completed" + }, + "command-e19f5d23dd944e40b806e930f7d44b35": { + "operation": "dispatch", + "request_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "send_count": 1, + "state": "completed" + }, + "command-e1af2fbe5a254692bf212769ddfea045": { + "operation": "observe", + "request_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + }, + "command-ede04073e3654f4fa17f0a0056db9004": { + "operation": "dispatch", + "request_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "send_count": 1, + "state": "completed" + }, + "command-ef48ba673c2b4ae08b09fbbc4c56fca8": { + "operation": "start_job", + "request_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "send_count": 1, + "state": "completed" + }, + "command-f008b852a29348218bc57e931887b963": { + "operation": "dispatch", + "request_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "send_count": 1, + "state": "completed" + }, + "command-f0d26ac5d6914f1bb038a57ee515b7f0": { + "operation": "observe", + "request_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "send_count": 1, + "state": "completed" + }, + "command-f4c202be91064d3e8f4c929f1175450e": { + "operation": "propose", + "request_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "send_count": 1, + "state": "completed" + }, + "command-f7b8d8e5cddb47ecbac988938ccee2a0": { + "operation": "observe", + "request_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "send_count": 1, + "state": "completed" + }, + "command-fda8dcacccc940c493511804050cc0ec": { + "operation": "propose", + "request_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "f816c68447d947988c70c3362c619003" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 10, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-71f5f95d2a1f4b91ab54de93a921a68c-118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa.png", + "sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "runner_status": "paused", + "sequence": 118, + "stage": null, + "usage": { + "calls": 15, + "input_tokens": 172372, + "output_tokens": 2008, + "reported_calls": 13 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + }, + { + "command_id": "command-0e35d66b16a64168b11d255a1d871e71", + "payload_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "sequence": 67 + }, + { + "command_id": "command-fda8dcacccc940c493511804050cc0ec", + "payload_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "sequence": 79 + }, + { + "command_id": "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "payload_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "sequence": 87 + }, + { + "command_id": "command-c8be0caf76504410a68a595296d56bd3", + "payload_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "sequence": 95 + }, + { + "command_id": "command-f4c202be91064d3e8f4c929f1175450e", + "payload_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "sequence": 107 + }, + { + "command_id": "command-e19f5d23dd944e40b806e930f7d44b35", + "payload_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "sequence": 115 + }, + { + "command_id": "command-1936c404000d4b55a094c49a2fd9eeb3", + "payload_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "sequence": 123 + }, + { + "command_id": "command-8c948c9df6074ee5969ef8b19a3eaddb", + "payload_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "sequence": 135 + }, + { + "command_id": "command-bdc662ab7f4645d79c02f84921f3c9f6", + "payload_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "sequence": 143 + }, + { + "command_id": "command-e1af2fbe5a254692bf212769ddfea045", + "payload_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "sequence": 151 + }, + { + "command_id": "command-b17181d5918a492a916e0e3c5a2f3a11", + "payload_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "sequence": 163 + }, + { + "command_id": "command-2a6e74bdbe424b3caa700dc047365a1c", + "payload_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "sequence": 171 + }, + { + "command_id": "command-df199bef22b442168d0a42242c00c5c4", + "payload_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "sequence": 179 + }, + { + "command_id": "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "payload_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "sequence": 191 + }, + { + "command_id": "command-f008b852a29348218bc57e931887b963", + "payload_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "sequence": 199 + }, + { + "command_id": "command-2d0b436b61124502a9d8558816bda68d", + "payload_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "sequence": 207 + }, + { + "command_id": "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "payload_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "sequence": 219 + }, + { + "command_id": "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "payload_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "sequence": 227 + }, + { + "command_id": "command-b182534626d34766a52d5e11d48239e7", + "payload_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "sequence": 235 + }, + { + "command_id": "command-84c016d3c2564ecea6025edaea7d23e7", + "payload_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "sequence": 247 + }, + { + "command_id": "command-ede04073e3654f4fa17f0a0056db9004", + "payload_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "sequence": 255 + }, + { + "command_id": "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "payload_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "sequence": 263 + }, + { + "command_id": "command-5ced22700795486fa68a50fcda4767b8", + "payload_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "sequence": 275 + }, + { + "command_id": "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "payload_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "sequence": 283 + }, + { + "command_id": "command-9dd2a0c9603f4667956ec976e822b74a", + "payload_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "sequence": 291 + }, + { + "command_id": "command-3083d955014b4e5282809a47046c53db", + "payload_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "sequence": 303 + }, + { + "command_id": "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "payload_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "sequence": 311 + }, + { + "command_id": "command-38a300b989a649fd9ee243704cdc65a9", + "payload_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "sequence": 319 + }, + { + "command_id": "command-553be6e82b274944b5e6ea9e2cd3902e", + "payload_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "sequence": 331 + }, + { + "command_id": "command-d9922eefd392420b96bf0a62d7eea982", + "payload_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "sequence": 339 + }, + { + "command_id": "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "payload_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "sequence": 347 + }, + { + "command_id": "command-7661dbdae1c34bd797ca17b3b5321a5e", + "payload_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "sequence": 359 + }, + { + "command_id": "command-c80c18cc647b45fe8e9faf2fa890283c", + "payload_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "sequence": 367 + }, + { + "command_id": "command-248b9798493e4a43a51259929e135e29", + "payload_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "sequence": 375 + }, + { + "command_id": "command-015d5d03d0624f14ba43d889871c45ee", + "payload_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "sequence": 387 + }, + { + "command_id": "command-210150e3ac9f45adae24e89f22a66286", + "payload_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "sequence": 395 + }, + { + "command_id": "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "payload_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "sequence": 403 + }, + { + "command_id": "command-ab4679dd21e14ce08f70b1ef63bd5868", + "payload_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "sequence": 411 + } + ], + "processes": { + "body": 71324, + "workbench": 39967 + }, + "public": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "activity": [ + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-d9922eefd392420b96bf0a62d7eea982", + "seq": 95, + "time": 1791828930.0683122 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 96, + "time": 1791828930.114275 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-f7b8d8e5cddb47ecbac988938ccee2a0", + "seq": 97, + "time": 1791828930.315923 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "13", + "seq": 98, + "time": 1791828930.318308 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 99, + "time": 1791828937.617022 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-7661dbdae1c34bd797ca17b3b5321a5e", + "seq": 100, + "time": 1791828937.772455 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 101, + "time": 1791828937.839915 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-c80c18cc647b45fe8e9faf2fa890283c", + "seq": 102, + "time": 1791828938.114055 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 103, + "time": 1791828938.266146 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-248b9798493e4a43a51259929e135e29", + "seq": 104, + "time": 1791828938.604191 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "14", + "seq": 105, + "time": 1791828938.608861 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 106, + "time": 1791828950.833822 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-015d5d03d0624f14ba43d889871c45ee", + "seq": 107, + "time": 1791828950.9307368 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 108, + "time": 1791828950.97887 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-210150e3ac9f45adae24e89f22a66286", + "seq": 109, + "time": 1791828951.468124 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 110, + "time": 1791828951.5941691 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "start_job: command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "seq": 111, + "time": 1791828951.745876 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 112, + "time": 1791828951.8522959 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-ab4679dd21e14ce08f70b1ef63bd5868", + "seq": 113, + "time": 1791828952.1878028 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "1", + "seq": 114, + "time": 1791828952.191465 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 115, + "time": 1791828953.570592 + }, + { + "intent": "", + "kind": "halt", + "outcome": "operator_pause_requested", + "seq": 116, + "time": 1791828953.590626 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "seq": 117, + "time": 1791829010.0203352 + }, + { + "intent": "", + "kind": "body_epoch_changed", + "outcome": "1 -> 2", + "seq": 118, + "time": 1791829010.021775 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 2, + "fault": null, + "reason": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 1, + "disturbance_test": false, + "error": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": false, + "reason": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "paused", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 15, + "input_tokens": 172372, + "output_tokens": 2008, + "reported_calls": 13, + "token_usage_complete": false + } + }, + "time": 1791829010.038528 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000006-before.json b/jevdual/results/workbench-restarts-20261012/snapshots/000006-before.json new file mode 100644 index 0000000..6371a56 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000006-before.json @@ -0,0 +1,784 @@ +{ + "body_identity": null, + "body_identity_error": "[Errno 61] Connection refused", + "body_state": null, + "body_state_error": "[Errno 61] Connection refused", + "client": { + "commands": { + "command-015d5d03d0624f14ba43d889871c45ee": { + "operation": "propose", + "request_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "send_count": 1, + "state": "completed" + }, + "command-097e6bec1ce24ef2bc8f37c40f80d98f": { + "operation": "dispatch", + "request_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "send_count": 1, + "state": "completed" + }, + "command-0e35d66b16a64168b11d255a1d871e71": { + "operation": "observe", + "request_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "send_count": 1, + "state": "completed" + }, + "command-1936c404000d4b55a094c49a2fd9eeb3": { + "operation": "observe", + "request_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "send_count": 1, + "state": "completed" + }, + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-210150e3ac9f45adae24e89f22a66286": { + "operation": "dispatch", + "request_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "completed" + }, + "command-248b9798493e4a43a51259929e135e29": { + "operation": "observe", + "request_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "send_count": 1, + "state": "completed" + }, + "command-2a6e74bdbe424b3caa700dc047365a1c": { + "operation": "dispatch", + "request_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "send_count": 1, + "state": "completed" + }, + "command-2d0b436b61124502a9d8558816bda68d": { + "operation": "observe", + "request_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "send_count": 1, + "state": "completed" + }, + "command-3083d955014b4e5282809a47046c53db": { + "operation": "propose", + "request_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "send_count": 1, + "state": "completed" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-38a300b989a649fd9ee243704cdc65a9": { + "operation": "observe", + "request_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "send_count": 1, + "state": "completed" + }, + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e": { + "operation": "propose", + "request_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "send_count": 1, + "state": "completed" + }, + "command-553be6e82b274944b5e6ea9e2cd3902e": { + "operation": "propose", + "request_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "send_count": 1, + "state": "completed" + }, + "command-5ca0b6a7e99f4263b2239a95b02ca7e7": { + "operation": "propose", + "request_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "send_count": 1, + "state": "completed" + }, + "command-5ced22700795486fa68a50fcda4767b8": { + "operation": "propose", + "request_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "send_count": 1, + "state": "completed" + }, + "command-5e27abcb6c914c5ca0ae77ddee974bbc": { + "operation": "dispatch", + "request_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "send_count": 1, + "state": "completed" + }, + "command-7661dbdae1c34bd797ca17b3b5321a5e": { + "operation": "propose", + "request_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "send_count": 1, + "state": "completed" + }, + "command-7ce7b5eb9b5b4b1d953e624d2312327b": { + "operation": "dispatch", + "request_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "send_count": 1, + "state": "completed" + }, + "command-84c016d3c2564ecea6025edaea7d23e7": { + "operation": "propose", + "request_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "send_count": 1, + "state": "completed" + }, + "command-8c948c9df6074ee5969ef8b19a3eaddb": { + "operation": "propose", + "request_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-9dd2a0c9603f4667956ec976e822b74a": { + "operation": "observe", + "request_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "send_count": 1, + "state": "completed" + }, + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee": { + "operation": "dispatch", + "request_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "send_count": 1, + "state": "completed" + }, + "command-ab4679dd21e14ce08f70b1ef63bd5868": { + "operation": "observe", + "request_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "send_count": 1, + "state": "completed" + }, + "command-b17181d5918a492a916e0e3c5a2f3a11": { + "operation": "propose", + "request_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "send_count": 1, + "state": "completed" + }, + "command-b182534626d34766a52d5e11d48239e7": { + "operation": "observe", + "request_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "send_count": 1, + "state": "completed" + }, + "command-bdc662ab7f4645d79c02f84921f3c9f6": { + "operation": "dispatch", + "request_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "send_count": 1, + "state": "completed" + }, + "command-c80c18cc647b45fe8e9faf2fa890283c": { + "operation": "dispatch", + "request_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "send_count": 1, + "state": "completed" + }, + "command-c8be0caf76504410a68a595296d56bd3": { + "operation": "observe", + "request_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "send_count": 1, + "state": "completed" + }, + "command-d9922eefd392420b96bf0a62d7eea982": { + "operation": "dispatch", + "request_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "send_count": 1, + "state": "completed" + }, + "command-df199bef22b442168d0a42242c00c5c4": { + "operation": "observe", + "request_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "send_count": 1, + "state": "completed" + }, + "command-e19f5d23dd944e40b806e930f7d44b35": { + "operation": "dispatch", + "request_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "send_count": 1, + "state": "completed" + }, + "command-e1af2fbe5a254692bf212769ddfea045": { + "operation": "observe", + "request_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + }, + "command-ede04073e3654f4fa17f0a0056db9004": { + "operation": "dispatch", + "request_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "send_count": 1, + "state": "completed" + }, + "command-ef48ba673c2b4ae08b09fbbc4c56fca8": { + "operation": "start_job", + "request_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "send_count": 1, + "state": "completed" + }, + "command-f008b852a29348218bc57e931887b963": { + "operation": "dispatch", + "request_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "send_count": 1, + "state": "completed" + }, + "command-f0d26ac5d6914f1bb038a57ee515b7f0": { + "operation": "observe", + "request_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "send_count": 1, + "state": "completed" + }, + "command-f4c202be91064d3e8f4c929f1175450e": { + "operation": "propose", + "request_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "send_count": 1, + "state": "completed" + }, + "command-f7b8d8e5cddb47ecbac988938ccee2a0": { + "operation": "observe", + "request_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "send_count": 1, + "state": "completed" + }, + "command-fda8dcacccc940c493511804050cc0ec": { + "operation": "propose", + "request_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 1, + "lease_id": "f816c68447d947988c70c3362c619003" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 10, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-71f5f95d2a1f4b91ab54de93a921a68c-118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa.png", + "sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "runner_status": "paused", + "sequence": 116, + "stage": null, + "usage": { + "calls": 15, + "input_tokens": 172372, + "output_tokens": 2008, + "reported_calls": 13 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + }, + { + "command_id": "command-0e35d66b16a64168b11d255a1d871e71", + "payload_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "sequence": 67 + }, + { + "command_id": "command-fda8dcacccc940c493511804050cc0ec", + "payload_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "sequence": 79 + }, + { + "command_id": "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "payload_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "sequence": 87 + }, + { + "command_id": "command-c8be0caf76504410a68a595296d56bd3", + "payload_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "sequence": 95 + }, + { + "command_id": "command-f4c202be91064d3e8f4c929f1175450e", + "payload_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "sequence": 107 + }, + { + "command_id": "command-e19f5d23dd944e40b806e930f7d44b35", + "payload_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "sequence": 115 + }, + { + "command_id": "command-1936c404000d4b55a094c49a2fd9eeb3", + "payload_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "sequence": 123 + }, + { + "command_id": "command-8c948c9df6074ee5969ef8b19a3eaddb", + "payload_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "sequence": 135 + }, + { + "command_id": "command-bdc662ab7f4645d79c02f84921f3c9f6", + "payload_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "sequence": 143 + }, + { + "command_id": "command-e1af2fbe5a254692bf212769ddfea045", + "payload_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "sequence": 151 + }, + { + "command_id": "command-b17181d5918a492a916e0e3c5a2f3a11", + "payload_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "sequence": 163 + }, + { + "command_id": "command-2a6e74bdbe424b3caa700dc047365a1c", + "payload_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "sequence": 171 + }, + { + "command_id": "command-df199bef22b442168d0a42242c00c5c4", + "payload_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "sequence": 179 + }, + { + "command_id": "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "payload_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "sequence": 191 + }, + { + "command_id": "command-f008b852a29348218bc57e931887b963", + "payload_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "sequence": 199 + }, + { + "command_id": "command-2d0b436b61124502a9d8558816bda68d", + "payload_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "sequence": 207 + }, + { + "command_id": "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "payload_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "sequence": 219 + }, + { + "command_id": "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "payload_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "sequence": 227 + }, + { + "command_id": "command-b182534626d34766a52d5e11d48239e7", + "payload_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "sequence": 235 + }, + { + "command_id": "command-84c016d3c2564ecea6025edaea7d23e7", + "payload_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "sequence": 247 + }, + { + "command_id": "command-ede04073e3654f4fa17f0a0056db9004", + "payload_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "sequence": 255 + }, + { + "command_id": "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "payload_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "sequence": 263 + }, + { + "command_id": "command-5ced22700795486fa68a50fcda4767b8", + "payload_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "sequence": 275 + }, + { + "command_id": "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "payload_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "sequence": 283 + }, + { + "command_id": "command-9dd2a0c9603f4667956ec976e822b74a", + "payload_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "sequence": 291 + }, + { + "command_id": "command-3083d955014b4e5282809a47046c53db", + "payload_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "sequence": 303 + }, + { + "command_id": "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "payload_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "sequence": 311 + }, + { + "command_id": "command-38a300b989a649fd9ee243704cdc65a9", + "payload_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "sequence": 319 + }, + { + "command_id": "command-553be6e82b274944b5e6ea9e2cd3902e", + "payload_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "sequence": 331 + }, + { + "command_id": "command-d9922eefd392420b96bf0a62d7eea982", + "payload_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "sequence": 339 + }, + { + "command_id": "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "payload_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "sequence": 347 + }, + { + "command_id": "command-7661dbdae1c34bd797ca17b3b5321a5e", + "payload_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "sequence": 359 + }, + { + "command_id": "command-c80c18cc647b45fe8e9faf2fa890283c", + "payload_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "sequence": 367 + }, + { + "command_id": "command-248b9798493e4a43a51259929e135e29", + "payload_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "sequence": 375 + }, + { + "command_id": "command-015d5d03d0624f14ba43d889871c45ee", + "payload_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "sequence": 387 + }, + { + "command_id": "command-210150e3ac9f45adae24e89f22a66286", + "payload_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "sequence": 395 + }, + { + "command_id": "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "payload_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "sequence": 403 + }, + { + "command_id": "command-ab4679dd21e14ce08f70b1ef63bd5868", + "payload_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "sequence": 411 + } + ], + "processes": { + "body": null, + "workbench": 39967 + }, + "public": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "activity": [ + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-553be6e82b274944b5e6ea9e2cd3902e", + "seq": 93, + "time": 1791828929.76928 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 94, + "time": 1791828929.815944 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-d9922eefd392420b96bf0a62d7eea982", + "seq": 95, + "time": 1791828930.0683122 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 96, + "time": 1791828930.114275 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-f7b8d8e5cddb47ecbac988938ccee2a0", + "seq": 97, + "time": 1791828930.315923 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "13", + "seq": 98, + "time": 1791828930.318308 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 99, + "time": 1791828937.617022 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-7661dbdae1c34bd797ca17b3b5321a5e", + "seq": 100, + "time": 1791828937.772455 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 101, + "time": 1791828937.839915 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-c80c18cc647b45fe8e9faf2fa890283c", + "seq": 102, + "time": 1791828938.114055 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 103, + "time": 1791828938.266146 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-248b9798493e4a43a51259929e135e29", + "seq": 104, + "time": 1791828938.604191 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "14", + "seq": 105, + "time": 1791828938.608861 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 106, + "time": 1791828950.833822 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-015d5d03d0624f14ba43d889871c45ee", + "seq": 107, + "time": 1791828950.9307368 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 108, + "time": 1791828950.97887 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-210150e3ac9f45adae24e89f22a66286", + "seq": 109, + "time": 1791828951.468124 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 110, + "time": 1791828951.5941691 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "start_job: command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "seq": 111, + "time": 1791828951.745876 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 112, + "time": 1791828951.8522959 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-ab4679dd21e14ce08f70b1ef63bd5868", + "seq": 113, + "time": 1791828952.1878028 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "1", + "seq": 114, + "time": 1791828952.191465 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 115, + "time": 1791828953.570592 + }, + { + "intent": "", + "kind": "halt", + "outcome": "operator_pause_requested", + "seq": 116, + "time": 1791828953.590626 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Remote end closed connection without response", + "state": "unavailable" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 1, + "disturbance_test": false, + "error": "operator_pause_requested", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": false, + "reason": "operator_pause_requested" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "paused", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 15, + "input_tokens": 172372, + "output_tokens": 2008, + "reported_calls": 13, + "token_usage_complete": false + } + }, + "time": 1791829008.8750951 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000007-after.json b/jevdual/results/workbench-restarts-20261012/snapshots/000007-after.json new file mode 100644 index 0000000..af93ddf --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000007-after.json @@ -0,0 +1,884 @@ +{ + "body_identity": null, + "body_identity_error": "[Errno 61] Connection refused", + "body_state": null, + "body_state_error": "[Errno 61] Connection refused", + "client": { + "commands": { + "command-015d5d03d0624f14ba43d889871c45ee": { + "operation": "propose", + "request_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "send_count": 1, + "state": "completed" + }, + "command-097e6bec1ce24ef2bc8f37c40f80d98f": { + "operation": "dispatch", + "request_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "send_count": 1, + "state": "completed" + }, + "command-0e35d66b16a64168b11d255a1d871e71": { + "operation": "observe", + "request_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "send_count": 1, + "state": "completed" + }, + "command-1936c404000d4b55a094c49a2fd9eeb3": { + "operation": "observe", + "request_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "send_count": 1, + "state": "completed" + }, + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-210150e3ac9f45adae24e89f22a66286": { + "operation": "dispatch", + "request_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "completed" + }, + "command-248b9798493e4a43a51259929e135e29": { + "operation": "observe", + "request_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "send_count": 1, + "state": "completed" + }, + "command-2a6e74bdbe424b3caa700dc047365a1c": { + "operation": "dispatch", + "request_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "send_count": 1, + "state": "completed" + }, + "command-2d0b436b61124502a9d8558816bda68d": { + "operation": "observe", + "request_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "send_count": 1, + "state": "completed" + }, + "command-3083d955014b4e5282809a47046c53db": { + "operation": "propose", + "request_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "send_count": 1, + "state": "completed" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-38a300b989a649fd9ee243704cdc65a9": { + "operation": "observe", + "request_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "send_count": 1, + "state": "completed" + }, + "command-40c6a8a1f1a04ea7bec902767b57387a": { + "operation": "observe", + "request_sha256": "7bb39849a52b98bf039deebb6371d28ece55c04faed9809c9866c6babae4ef26", + "send_count": 1, + "state": "completed" + }, + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e": { + "operation": "propose", + "request_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "send_count": 1, + "state": "completed" + }, + "command-553be6e82b274944b5e6ea9e2cd3902e": { + "operation": "propose", + "request_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "send_count": 1, + "state": "completed" + }, + "command-5ca0b6a7e99f4263b2239a95b02ca7e7": { + "operation": "propose", + "request_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "send_count": 1, + "state": "completed" + }, + "command-5ced22700795486fa68a50fcda4767b8": { + "operation": "propose", + "request_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "send_count": 1, + "state": "completed" + }, + "command-5e27abcb6c914c5ca0ae77ddee974bbc": { + "operation": "dispatch", + "request_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "send_count": 1, + "state": "completed" + }, + "command-7661dbdae1c34bd797ca17b3b5321a5e": { + "operation": "propose", + "request_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "send_count": 1, + "state": "completed" + }, + "command-7ce7b5eb9b5b4b1d953e624d2312327b": { + "operation": "dispatch", + "request_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "send_count": 1, + "state": "completed" + }, + "command-84c016d3c2564ecea6025edaea7d23e7": { + "operation": "propose", + "request_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "send_count": 1, + "state": "completed" + }, + "command-8c948c9df6074ee5969ef8b19a3eaddb": { + "operation": "propose", + "request_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-9dd2a0c9603f4667956ec976e822b74a": { + "operation": "observe", + "request_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "send_count": 1, + "state": "completed" + }, + "command-9f8d9402f1d74504b579f39b9c793576": { + "operation": "propose", + "request_sha256": "1c6f1885adbe13c8b3ab749812e10139310fcbadf478b3debaa8bc025bda45b0", + "send_count": 1, + "state": "completed" + }, + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee": { + "operation": "dispatch", + "request_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "send_count": 1, + "state": "completed" + }, + "command-ab4679dd21e14ce08f70b1ef63bd5868": { + "operation": "observe", + "request_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "send_count": 1, + "state": "completed" + }, + "command-b17181d5918a492a916e0e3c5a2f3a11": { + "operation": "propose", + "request_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "send_count": 1, + "state": "completed" + }, + "command-b182534626d34766a52d5e11d48239e7": { + "operation": "observe", + "request_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "send_count": 1, + "state": "completed" + }, + "command-bdc662ab7f4645d79c02f84921f3c9f6": { + "operation": "dispatch", + "request_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "send_count": 1, + "state": "completed" + }, + "command-c80c18cc647b45fe8e9faf2fa890283c": { + "operation": "dispatch", + "request_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "send_count": 1, + "state": "completed" + }, + "command-c8be0caf76504410a68a595296d56bd3": { + "operation": "observe", + "request_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "send_count": 1, + "state": "completed" + }, + "command-cf898dd6e56a4d8bb0a5894371f9ba81": { + "operation": "dispatch", + "request_sha256": "bd591cd9646fb80860bc791d8b9d02fd3695a496ead456ceb7f903185883edc9", + "send_count": 1, + "state": "unknown" + }, + "command-d9922eefd392420b96bf0a62d7eea982": { + "operation": "dispatch", + "request_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "send_count": 1, + "state": "completed" + }, + "command-df199bef22b442168d0a42242c00c5c4": { + "operation": "observe", + "request_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "send_count": 1, + "state": "completed" + }, + "command-e19f5d23dd944e40b806e930f7d44b35": { + "operation": "dispatch", + "request_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "send_count": 1, + "state": "completed" + }, + "command-e1af2fbe5a254692bf212769ddfea045": { + "operation": "observe", + "request_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + }, + "command-ede04073e3654f4fa17f0a0056db9004": { + "operation": "dispatch", + "request_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "send_count": 1, + "state": "completed" + }, + "command-ef48ba673c2b4ae08b09fbbc4c56fca8": { + "operation": "start_job", + "request_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "send_count": 1, + "state": "completed" + }, + "command-f008b852a29348218bc57e931887b963": { + "operation": "dispatch", + "request_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "send_count": 1, + "state": "completed" + }, + "command-f0d26ac5d6914f1bb038a57ee515b7f0": { + "operation": "observe", + "request_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "send_count": 1, + "state": "completed" + }, + "command-f4c202be91064d3e8f4c929f1175450e": { + "operation": "propose", + "request_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "send_count": 1, + "state": "completed" + }, + "command-f7b8d8e5cddb47ecbac988938ccee2a0": { + "operation": "observe", + "request_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "send_count": 1, + "state": "completed" + }, + "command-fda8dcacccc940c493511804050cc0ec": { + "operation": "propose", + "request_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 2, + "lease_id": "8acd97f5a9dc48b583d6d3097f60b1ba" + }, + "pending_command_ids": [ + "command-cf898dd6e56a4d8bb0a5894371f9ba81" + ] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 10, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-28dc527b5ab349cab70e9bea9dbea92a-118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa.png", + "sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "runner_status": "blocked", + "sequence": 127, + "stage": { + "arguments": { + "proposal_id": "proposal-2fc20beaf42745488b7a16af3afeaf6d" + }, + "before_commands": [ + "command-015d5d03d0624f14ba43d889871c45ee", + "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "command-0e35d66b16a64168b11d255a1d871e71", + "command-1936c404000d4b55a094c49a2fd9eeb3", + "command-1e2386315d6c4c7692405dac7dfa7bd1", + "command-210150e3ac9f45adae24e89f22a66286", + "command-24230a0f96bc4e84a84f720b1c974054", + "command-248b9798493e4a43a51259929e135e29", + "command-2a6e74bdbe424b3caa700dc047365a1c", + "command-2d0b436b61124502a9d8558816bda68d", + "command-3083d955014b4e5282809a47046c53db", + "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "command-38a300b989a649fd9ee243704cdc65a9", + "command-40c6a8a1f1a04ea7bec902767b57387a", + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "command-553be6e82b274944b5e6ea9e2cd3902e", + "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "command-5ced22700795486fa68a50fcda4767b8", + "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "command-7661dbdae1c34bd797ca17b3b5321a5e", + "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "command-84c016d3c2564ecea6025edaea7d23e7", + "command-8c948c9df6074ee5969ef8b19a3eaddb", + "command-933c11248d6a48079ead24676a2df7fb", + "command-9dd2a0c9603f4667956ec976e822b74a", + "command-9f8d9402f1d74504b579f39b9c793576", + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "command-ab4679dd21e14ce08f70b1ef63bd5868", + "command-b17181d5918a492a916e0e3c5a2f3a11", + "command-b182534626d34766a52d5e11d48239e7", + "command-bdc662ab7f4645d79c02f84921f3c9f6", + "command-c80c18cc647b45fe8e9faf2fa890283c", + "command-c8be0caf76504410a68a595296d56bd3", + "command-d9922eefd392420b96bf0a62d7eea982", + "command-df199bef22b442168d0a42242c00c5c4", + "command-e19f5d23dd944e40b806e930f7d44b35", + "command-e1af2fbe5a254692bf212769ddfea045", + "command-e897a3cba83b4d83b460a556be5e459b", + "command-ede04073e3654f4fa17f0a0056db9004", + "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "command-f008b852a29348218bc57e931887b963", + "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "command-f4c202be91064d3e8f4c929f1175450e", + "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "command-fda8dcacccc940c493511804050cc0ec" + ], + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "context": { + "action": { + "action": "move", + "intent": "Push the block right so its center reaches the measurement region.", + "target_x": 0.025, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/calls/c922a26d-9121-4d7e-82e1-7b61b0e42161-02", + "decision_epoch": 7, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161" + }, + "control_epoch": 7, + "operation": "dispatch" + }, + "usage": { + "calls": 16, + "input_tokens": 185456, + "output_tokens": 2222, + "reported_calls": 14 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + }, + { + "command_id": "command-0e35d66b16a64168b11d255a1d871e71", + "payload_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "sequence": 67 + }, + { + "command_id": "command-fda8dcacccc940c493511804050cc0ec", + "payload_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "sequence": 79 + }, + { + "command_id": "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "payload_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "sequence": 87 + }, + { + "command_id": "command-c8be0caf76504410a68a595296d56bd3", + "payload_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "sequence": 95 + }, + { + "command_id": "command-f4c202be91064d3e8f4c929f1175450e", + "payload_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "sequence": 107 + }, + { + "command_id": "command-e19f5d23dd944e40b806e930f7d44b35", + "payload_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "sequence": 115 + }, + { + "command_id": "command-1936c404000d4b55a094c49a2fd9eeb3", + "payload_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "sequence": 123 + }, + { + "command_id": "command-8c948c9df6074ee5969ef8b19a3eaddb", + "payload_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "sequence": 135 + }, + { + "command_id": "command-bdc662ab7f4645d79c02f84921f3c9f6", + "payload_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "sequence": 143 + }, + { + "command_id": "command-e1af2fbe5a254692bf212769ddfea045", + "payload_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "sequence": 151 + }, + { + "command_id": "command-b17181d5918a492a916e0e3c5a2f3a11", + "payload_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "sequence": 163 + }, + { + "command_id": "command-2a6e74bdbe424b3caa700dc047365a1c", + "payload_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "sequence": 171 + }, + { + "command_id": "command-df199bef22b442168d0a42242c00c5c4", + "payload_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "sequence": 179 + }, + { + "command_id": "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "payload_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "sequence": 191 + }, + { + "command_id": "command-f008b852a29348218bc57e931887b963", + "payload_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "sequence": 199 + }, + { + "command_id": "command-2d0b436b61124502a9d8558816bda68d", + "payload_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "sequence": 207 + }, + { + "command_id": "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "payload_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "sequence": 219 + }, + { + "command_id": "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "payload_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "sequence": 227 + }, + { + "command_id": "command-b182534626d34766a52d5e11d48239e7", + "payload_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "sequence": 235 + }, + { + "command_id": "command-84c016d3c2564ecea6025edaea7d23e7", + "payload_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "sequence": 247 + }, + { + "command_id": "command-ede04073e3654f4fa17f0a0056db9004", + "payload_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "sequence": 255 + }, + { + "command_id": "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "payload_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "sequence": 263 + }, + { + "command_id": "command-5ced22700795486fa68a50fcda4767b8", + "payload_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "sequence": 275 + }, + { + "command_id": "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "payload_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "sequence": 283 + }, + { + "command_id": "command-9dd2a0c9603f4667956ec976e822b74a", + "payload_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "sequence": 291 + }, + { + "command_id": "command-3083d955014b4e5282809a47046c53db", + "payload_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "sequence": 303 + }, + { + "command_id": "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "payload_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "sequence": 311 + }, + { + "command_id": "command-38a300b989a649fd9ee243704cdc65a9", + "payload_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "sequence": 319 + }, + { + "command_id": "command-553be6e82b274944b5e6ea9e2cd3902e", + "payload_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "sequence": 331 + }, + { + "command_id": "command-d9922eefd392420b96bf0a62d7eea982", + "payload_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "sequence": 339 + }, + { + "command_id": "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "payload_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "sequence": 347 + }, + { + "command_id": "command-7661dbdae1c34bd797ca17b3b5321a5e", + "payload_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "sequence": 359 + }, + { + "command_id": "command-c80c18cc647b45fe8e9faf2fa890283c", + "payload_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "sequence": 367 + }, + { + "command_id": "command-248b9798493e4a43a51259929e135e29", + "payload_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "sequence": 375 + }, + { + "command_id": "command-015d5d03d0624f14ba43d889871c45ee", + "payload_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "sequence": 387 + }, + { + "command_id": "command-210150e3ac9f45adae24e89f22a66286", + "payload_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "sequence": 395 + }, + { + "command_id": "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "payload_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "sequence": 403 + }, + { + "command_id": "command-ab4679dd21e14ce08f70b1ef63bd5868", + "payload_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "sequence": 411 + }, + { + "command_id": "command-40c6a8a1f1a04ea7bec902767b57387a", + "payload_sha256": "7bb39849a52b98bf039deebb6371d28ece55c04faed9809c9866c6babae4ef26", + "sequence": 480 + }, + { + "command_id": "command-9f8d9402f1d74504b579f39b9c793576", + "payload_sha256": "1c6f1885adbe13c8b3ab749812e10139310fcbadf478b3debaa8bc025bda45b0", + "sequence": 492 + }, + { + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "payload_sha256": "bd591cd9646fb80860bc791d8b9d02fd3695a496ead456ceb7f903185883edc9", + "sequence": 500 + } + ], + "processes": { + "body": null, + "workbench": 39967 + }, + "public": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "activity": [ + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-248b9798493e4a43a51259929e135e29", + "seq": 104, + "time": 1791828938.604191 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "14", + "seq": 105, + "time": 1791828938.608861 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 106, + "time": 1791828950.833822 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-015d5d03d0624f14ba43d889871c45ee", + "seq": 107, + "time": 1791828950.9307368 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 108, + "time": 1791828950.97887 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-210150e3ac9f45adae24e89f22a66286", + "seq": 109, + "time": 1791828951.468124 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 110, + "time": 1791828951.5941691 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "start_job: command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "seq": 111, + "time": 1791828951.745876 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 112, + "time": 1791828951.8522959 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-ab4679dd21e14ce08f70b1ef63bd5868", + "seq": 113, + "time": 1791828952.1878028 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "1", + "seq": 114, + "time": 1791828952.191465 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 115, + "time": 1791828953.570592 + }, + { + "intent": "", + "kind": "halt", + "outcome": "operator_pause_requested", + "seq": 116, + "time": 1791828953.590626 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "seq": 117, + "time": 1791829010.0203352 + }, + { + "intent": "", + "kind": "body_epoch_changed", + "outcome": "1 -> 2", + "seq": 118, + "time": 1791829010.021775 + }, + { + "intent": "", + "kind": "fault_armed", + "outcome": "drop_after", + "seq": 119, + "time": 1791829019.593674 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 120, + "time": 1791829019.920613 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 121, + "time": 1791829019.99926 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-40c6a8a1f1a04ea7bec902767b57387a", + "seq": 122, + "time": 1791829020.389618 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "2", + "seq": 123, + "time": 1791829020.392122 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 124, + "time": 1791829028.703949 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "propose: command-9f8d9402f1d74504b579f39b9c793576", + "seq": 125, + "time": 1791829028.798069 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 126, + "time": 1791829028.864546 + }, + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 127, + "time": 1791829029.1331468 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 2, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 2, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "reply is uncertain; reconcile exact command without resending" + }, + "recovery": { + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 16, + "input_tokens": 185456, + "output_tokens": 2222, + "reported_calls": 14, + "token_usage_complete": false + } + }, + "time": 1791829042.121286 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000007-before.json b/jevdual/results/workbench-restarts-20261012/snapshots/000007-before.json new file mode 100644 index 0000000..f95297e --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000007-before.json @@ -0,0 +1,992 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "moves": 1, + "phase": "prepare", + "started_generation": 10, + "started_native_time_s": 21.679999999998863, + "target_zone": { + "x_max": 0.115, + "x_min": 0.045, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 11, + "jobs": { + "20eb6586-e73a-4db4-aaac-db3e5d537329": { + "completed_generation": 10, + "completed_native_time_s": 21.679999999998863, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement_operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "measurement_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.46799999999962, + "operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "source_kind": "mujoco_simulation", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9", + "version": 1, + "worker_pid": 51928 + }, + "moves": 10, + "phase": "completed", + "reset_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "reset_started_generation": 2, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 24.847999999997107, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "epoch": 2, + "expires_in_s": 16.92054991587065, + "lease_id": "8acd97f5a9dc48b583d6d3097f60b1ba" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-015d5d03d0624f14ba43d889871c45ee": { + "operation": "propose", + "request_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "send_count": 1, + "state": "completed" + }, + "command-097e6bec1ce24ef2bc8f37c40f80d98f": { + "operation": "dispatch", + "request_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "send_count": 1, + "state": "completed" + }, + "command-0e35d66b16a64168b11d255a1d871e71": { + "operation": "observe", + "request_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "send_count": 1, + "state": "completed" + }, + "command-1936c404000d4b55a094c49a2fd9eeb3": { + "operation": "observe", + "request_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "send_count": 1, + "state": "completed" + }, + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-210150e3ac9f45adae24e89f22a66286": { + "operation": "dispatch", + "request_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "completed" + }, + "command-248b9798493e4a43a51259929e135e29": { + "operation": "observe", + "request_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "send_count": 1, + "state": "completed" + }, + "command-2a6e74bdbe424b3caa700dc047365a1c": { + "operation": "dispatch", + "request_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "send_count": 1, + "state": "completed" + }, + "command-2d0b436b61124502a9d8558816bda68d": { + "operation": "observe", + "request_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "send_count": 1, + "state": "completed" + }, + "command-3083d955014b4e5282809a47046c53db": { + "operation": "propose", + "request_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "send_count": 1, + "state": "completed" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-38a300b989a649fd9ee243704cdc65a9": { + "operation": "observe", + "request_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "send_count": 1, + "state": "completed" + }, + "command-40c6a8a1f1a04ea7bec902767b57387a": { + "operation": "observe", + "request_sha256": "7bb39849a52b98bf039deebb6371d28ece55c04faed9809c9866c6babae4ef26", + "send_count": 1, + "state": "completed" + }, + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e": { + "operation": "propose", + "request_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "send_count": 1, + "state": "completed" + }, + "command-553be6e82b274944b5e6ea9e2cd3902e": { + "operation": "propose", + "request_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "send_count": 1, + "state": "completed" + }, + "command-5ca0b6a7e99f4263b2239a95b02ca7e7": { + "operation": "propose", + "request_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "send_count": 1, + "state": "completed" + }, + "command-5ced22700795486fa68a50fcda4767b8": { + "operation": "propose", + "request_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "send_count": 1, + "state": "completed" + }, + "command-5e27abcb6c914c5ca0ae77ddee974bbc": { + "operation": "dispatch", + "request_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "send_count": 1, + "state": "completed" + }, + "command-7661dbdae1c34bd797ca17b3b5321a5e": { + "operation": "propose", + "request_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "send_count": 1, + "state": "completed" + }, + "command-7ce7b5eb9b5b4b1d953e624d2312327b": { + "operation": "dispatch", + "request_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "send_count": 1, + "state": "completed" + }, + "command-84c016d3c2564ecea6025edaea7d23e7": { + "operation": "propose", + "request_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "send_count": 1, + "state": "completed" + }, + "command-8c948c9df6074ee5969ef8b19a3eaddb": { + "operation": "propose", + "request_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-9dd2a0c9603f4667956ec976e822b74a": { + "operation": "observe", + "request_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "send_count": 1, + "state": "completed" + }, + "command-9f8d9402f1d74504b579f39b9c793576": { + "operation": "propose", + "request_sha256": "1c6f1885adbe13c8b3ab749812e10139310fcbadf478b3debaa8bc025bda45b0", + "send_count": 1, + "state": "completed" + }, + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee": { + "operation": "dispatch", + "request_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "send_count": 1, + "state": "completed" + }, + "command-ab4679dd21e14ce08f70b1ef63bd5868": { + "operation": "observe", + "request_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "send_count": 1, + "state": "completed" + }, + "command-b17181d5918a492a916e0e3c5a2f3a11": { + "operation": "propose", + "request_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "send_count": 1, + "state": "completed" + }, + "command-b182534626d34766a52d5e11d48239e7": { + "operation": "observe", + "request_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "send_count": 1, + "state": "completed" + }, + "command-bdc662ab7f4645d79c02f84921f3c9f6": { + "operation": "dispatch", + "request_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "send_count": 1, + "state": "completed" + }, + "command-c80c18cc647b45fe8e9faf2fa890283c": { + "operation": "dispatch", + "request_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "send_count": 1, + "state": "completed" + }, + "command-c8be0caf76504410a68a595296d56bd3": { + "operation": "observe", + "request_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "send_count": 1, + "state": "completed" + }, + "command-cf898dd6e56a4d8bb0a5894371f9ba81": { + "operation": "dispatch", + "request_sha256": "bd591cd9646fb80860bc791d8b9d02fd3695a496ead456ceb7f903185883edc9", + "send_count": 1, + "state": "unknown" + }, + "command-d9922eefd392420b96bf0a62d7eea982": { + "operation": "dispatch", + "request_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "send_count": 1, + "state": "completed" + }, + "command-df199bef22b442168d0a42242c00c5c4": { + "operation": "observe", + "request_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "send_count": 1, + "state": "completed" + }, + "command-e19f5d23dd944e40b806e930f7d44b35": { + "operation": "dispatch", + "request_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "send_count": 1, + "state": "completed" + }, + "command-e1af2fbe5a254692bf212769ddfea045": { + "operation": "observe", + "request_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + }, + "command-ede04073e3654f4fa17f0a0056db9004": { + "operation": "dispatch", + "request_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "send_count": 1, + "state": "completed" + }, + "command-ef48ba673c2b4ae08b09fbbc4c56fca8": { + "operation": "start_job", + "request_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "send_count": 1, + "state": "completed" + }, + "command-f008b852a29348218bc57e931887b963": { + "operation": "dispatch", + "request_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "send_count": 1, + "state": "completed" + }, + "command-f0d26ac5d6914f1bb038a57ee515b7f0": { + "operation": "observe", + "request_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "send_count": 1, + "state": "completed" + }, + "command-f4c202be91064d3e8f4c929f1175450e": { + "operation": "propose", + "request_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "send_count": 1, + "state": "completed" + }, + "command-f7b8d8e5cddb47ecbac988938ccee2a0": { + "operation": "observe", + "request_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "send_count": 1, + "state": "completed" + }, + "command-fda8dcacccc940c493511804050cc0ec": { + "operation": "propose", + "request_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 2, + "lease_id": "8acd97f5a9dc48b583d6d3097f60b1ba" + }, + "pending_command_ids": [ + "command-cf898dd6e56a4d8bb0a5894371f9ba81" + ] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 10, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-28dc527b5ab349cab70e9bea9dbea92a-118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa.png", + "sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "runner_status": "blocked", + "sequence": 127, + "stage": { + "arguments": { + "proposal_id": "proposal-2fc20beaf42745488b7a16af3afeaf6d" + }, + "before_commands": [ + "command-015d5d03d0624f14ba43d889871c45ee", + "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "command-0e35d66b16a64168b11d255a1d871e71", + "command-1936c404000d4b55a094c49a2fd9eeb3", + "command-1e2386315d6c4c7692405dac7dfa7bd1", + "command-210150e3ac9f45adae24e89f22a66286", + "command-24230a0f96bc4e84a84f720b1c974054", + "command-248b9798493e4a43a51259929e135e29", + "command-2a6e74bdbe424b3caa700dc047365a1c", + "command-2d0b436b61124502a9d8558816bda68d", + "command-3083d955014b4e5282809a47046c53db", + "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "command-38a300b989a649fd9ee243704cdc65a9", + "command-40c6a8a1f1a04ea7bec902767b57387a", + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "command-553be6e82b274944b5e6ea9e2cd3902e", + "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "command-5ced22700795486fa68a50fcda4767b8", + "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "command-7661dbdae1c34bd797ca17b3b5321a5e", + "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "command-84c016d3c2564ecea6025edaea7d23e7", + "command-8c948c9df6074ee5969ef8b19a3eaddb", + "command-933c11248d6a48079ead24676a2df7fb", + "command-9dd2a0c9603f4667956ec976e822b74a", + "command-9f8d9402f1d74504b579f39b9c793576", + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "command-ab4679dd21e14ce08f70b1ef63bd5868", + "command-b17181d5918a492a916e0e3c5a2f3a11", + "command-b182534626d34766a52d5e11d48239e7", + "command-bdc662ab7f4645d79c02f84921f3c9f6", + "command-c80c18cc647b45fe8e9faf2fa890283c", + "command-c8be0caf76504410a68a595296d56bd3", + "command-d9922eefd392420b96bf0a62d7eea982", + "command-df199bef22b442168d0a42242c00c5c4", + "command-e19f5d23dd944e40b806e930f7d44b35", + "command-e1af2fbe5a254692bf212769ddfea045", + "command-e897a3cba83b4d83b460a556be5e459b", + "command-ede04073e3654f4fa17f0a0056db9004", + "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "command-f008b852a29348218bc57e931887b963", + "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "command-f4c202be91064d3e8f4c929f1175450e", + "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "command-fda8dcacccc940c493511804050cc0ec" + ], + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "context": { + "action": { + "action": "move", + "intent": "Push the block right so its center reaches the measurement region.", + "target_x": 0.025, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/calls/c922a26d-9121-4d7e-82e1-7b61b0e42161-02", + "decision_epoch": 7, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161" + }, + "control_epoch": 7, + "operation": "dispatch" + }, + "usage": { + "calls": 16, + "input_tokens": 185456, + "output_tokens": 2222, + "reported_calls": 14 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + }, + { + "command_id": "command-0e35d66b16a64168b11d255a1d871e71", + "payload_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "sequence": 67 + }, + { + "command_id": "command-fda8dcacccc940c493511804050cc0ec", + "payload_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "sequence": 79 + }, + { + "command_id": "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "payload_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "sequence": 87 + }, + { + "command_id": "command-c8be0caf76504410a68a595296d56bd3", + "payload_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "sequence": 95 + }, + { + "command_id": "command-f4c202be91064d3e8f4c929f1175450e", + "payload_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "sequence": 107 + }, + { + "command_id": "command-e19f5d23dd944e40b806e930f7d44b35", + "payload_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "sequence": 115 + }, + { + "command_id": "command-1936c404000d4b55a094c49a2fd9eeb3", + "payload_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "sequence": 123 + }, + { + "command_id": "command-8c948c9df6074ee5969ef8b19a3eaddb", + "payload_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "sequence": 135 + }, + { + "command_id": "command-bdc662ab7f4645d79c02f84921f3c9f6", + "payload_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "sequence": 143 + }, + { + "command_id": "command-e1af2fbe5a254692bf212769ddfea045", + "payload_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "sequence": 151 + }, + { + "command_id": "command-b17181d5918a492a916e0e3c5a2f3a11", + "payload_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "sequence": 163 + }, + { + "command_id": "command-2a6e74bdbe424b3caa700dc047365a1c", + "payload_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "sequence": 171 + }, + { + "command_id": "command-df199bef22b442168d0a42242c00c5c4", + "payload_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "sequence": 179 + }, + { + "command_id": "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "payload_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "sequence": 191 + }, + { + "command_id": "command-f008b852a29348218bc57e931887b963", + "payload_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "sequence": 199 + }, + { + "command_id": "command-2d0b436b61124502a9d8558816bda68d", + "payload_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "sequence": 207 + }, + { + "command_id": "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "payload_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "sequence": 219 + }, + { + "command_id": "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "payload_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "sequence": 227 + }, + { + "command_id": "command-b182534626d34766a52d5e11d48239e7", + "payload_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "sequence": 235 + }, + { + "command_id": "command-84c016d3c2564ecea6025edaea7d23e7", + "payload_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "sequence": 247 + }, + { + "command_id": "command-ede04073e3654f4fa17f0a0056db9004", + "payload_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "sequence": 255 + }, + { + "command_id": "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "payload_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "sequence": 263 + }, + { + "command_id": "command-5ced22700795486fa68a50fcda4767b8", + "payload_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "sequence": 275 + }, + { + "command_id": "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "payload_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "sequence": 283 + }, + { + "command_id": "command-9dd2a0c9603f4667956ec976e822b74a", + "payload_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "sequence": 291 + }, + { + "command_id": "command-3083d955014b4e5282809a47046c53db", + "payload_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "sequence": 303 + }, + { + "command_id": "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "payload_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "sequence": 311 + }, + { + "command_id": "command-38a300b989a649fd9ee243704cdc65a9", + "payload_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "sequence": 319 + }, + { + "command_id": "command-553be6e82b274944b5e6ea9e2cd3902e", + "payload_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "sequence": 331 + }, + { + "command_id": "command-d9922eefd392420b96bf0a62d7eea982", + "payload_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "sequence": 339 + }, + { + "command_id": "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "payload_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "sequence": 347 + }, + { + "command_id": "command-7661dbdae1c34bd797ca17b3b5321a5e", + "payload_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "sequence": 359 + }, + { + "command_id": "command-c80c18cc647b45fe8e9faf2fa890283c", + "payload_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "sequence": 367 + }, + { + "command_id": "command-248b9798493e4a43a51259929e135e29", + "payload_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "sequence": 375 + }, + { + "command_id": "command-015d5d03d0624f14ba43d889871c45ee", + "payload_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "sequence": 387 + }, + { + "command_id": "command-210150e3ac9f45adae24e89f22a66286", + "payload_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "sequence": 395 + }, + { + "command_id": "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "payload_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "sequence": 403 + }, + { + "command_id": "command-ab4679dd21e14ce08f70b1ef63bd5868", + "payload_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "sequence": 411 + }, + { + "command_id": "command-40c6a8a1f1a04ea7bec902767b57387a", + "payload_sha256": "7bb39849a52b98bf039deebb6371d28ece55c04faed9809c9866c6babae4ef26", + "sequence": 480 + }, + { + "command_id": "command-9f8d9402f1d74504b579f39b9c793576", + "payload_sha256": "1c6f1885adbe13c8b3ab749812e10139310fcbadf478b3debaa8bc025bda45b0", + "sequence": 492 + }, + { + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "payload_sha256": "bd591cd9646fb80860bc791d8b9d02fd3695a496ead456ceb7f903185883edc9", + "sequence": 500 + } + ], + "processes": { + "body": 71324, + "workbench": 39967 + }, + "public": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "activity": [ + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "observe: command-248b9798493e4a43a51259929e135e29", + "seq": 104, + "time": 1791828938.604191 + }, + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "14", + "seq": 105, + "time": 1791828938.608861 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 106, + "time": 1791828950.833822 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-015d5d03d0624f14ba43d889871c45ee", + "seq": 107, + "time": 1791828950.9307368 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 108, + "time": 1791828950.97887 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-210150e3ac9f45adae24e89f22a66286", + "seq": 109, + "time": 1791828951.468124 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 110, + "time": 1791828951.5941691 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "start_job: command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "seq": 111, + "time": 1791828951.745876 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 112, + "time": 1791828951.8522959 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-ab4679dd21e14ce08f70b1ef63bd5868", + "seq": 113, + "time": 1791828952.1878028 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "1", + "seq": 114, + "time": 1791828952.191465 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 115, + "time": 1791828953.570592 + }, + { + "intent": "", + "kind": "halt", + "outcome": "operator_pause_requested", + "seq": 116, + "time": 1791828953.590626 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "seq": 117, + "time": 1791829010.0203352 + }, + { + "intent": "", + "kind": "body_epoch_changed", + "outcome": "1 -> 2", + "seq": 118, + "time": 1791829010.021775 + }, + { + "intent": "", + "kind": "fault_armed", + "outcome": "drop_after", + "seq": 119, + "time": 1791829019.593674 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 120, + "time": 1791829019.920613 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 121, + "time": 1791829019.99926 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-40c6a8a1f1a04ea7bec902767b57387a", + "seq": 122, + "time": 1791829020.389618 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "2", + "seq": 123, + "time": 1791829020.392122 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 124, + "time": 1791829028.703949 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "propose: command-9f8d9402f1d74504b579f39b9c793576", + "seq": 125, + "time": 1791829028.798069 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 126, + "time": 1791829028.864546 + }, + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 127, + "time": 1791829029.1331468 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 2, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 2, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "reply is uncertain; reconcile exact command without resending" + }, + "recovery": { + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 16, + "input_tokens": 185456, + "output_tokens": 2222, + "reported_calls": 14, + "token_usage_complete": false + } + }, + "time": 1791829041.853085 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000008-after.json b/jevdual/results/workbench-restarts-20261012/snapshots/000008-after.json new file mode 100644 index 0000000..b5499ec --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000008-after.json @@ -0,0 +1,987 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": { + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "moves": 1, + "phase": "prepare", + "started_generation": 10, + "started_native_time_s": 21.679999999998863, + "target_zone": { + "x_max": 0.115, + "x_min": 0.045, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 11, + "jobs": { + "20eb6586-e73a-4db4-aaac-db3e5d537329": { + "completed_generation": 10, + "completed_native_time_s": 21.679999999998863, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement_operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "measurement_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.46799999999962, + "operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "source_kind": "mujoco_simulation", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9", + "version": 1, + "worker_pid": 51928 + }, + "moves": 10, + "phase": "completed", + "reset_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "reset_started_generation": 2, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 24.847999999997107, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": null, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-015d5d03d0624f14ba43d889871c45ee": { + "operation": "propose", + "request_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "send_count": 1, + "state": "completed" + }, + "command-097e6bec1ce24ef2bc8f37c40f80d98f": { + "operation": "dispatch", + "request_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "send_count": 1, + "state": "completed" + }, + "command-0e35d66b16a64168b11d255a1d871e71": { + "operation": "observe", + "request_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "send_count": 1, + "state": "completed" + }, + "command-1936c404000d4b55a094c49a2fd9eeb3": { + "operation": "observe", + "request_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "send_count": 1, + "state": "completed" + }, + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-210150e3ac9f45adae24e89f22a66286": { + "operation": "dispatch", + "request_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "completed" + }, + "command-248b9798493e4a43a51259929e135e29": { + "operation": "observe", + "request_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "send_count": 1, + "state": "completed" + }, + "command-2a6e74bdbe424b3caa700dc047365a1c": { + "operation": "dispatch", + "request_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "send_count": 1, + "state": "completed" + }, + "command-2d0b436b61124502a9d8558816bda68d": { + "operation": "observe", + "request_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "send_count": 1, + "state": "completed" + }, + "command-3083d955014b4e5282809a47046c53db": { + "operation": "propose", + "request_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "send_count": 1, + "state": "completed" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-38a300b989a649fd9ee243704cdc65a9": { + "operation": "observe", + "request_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "send_count": 1, + "state": "completed" + }, + "command-40c6a8a1f1a04ea7bec902767b57387a": { + "operation": "observe", + "request_sha256": "7bb39849a52b98bf039deebb6371d28ece55c04faed9809c9866c6babae4ef26", + "send_count": 1, + "state": "completed" + }, + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e": { + "operation": "propose", + "request_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "send_count": 1, + "state": "completed" + }, + "command-553be6e82b274944b5e6ea9e2cd3902e": { + "operation": "propose", + "request_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "send_count": 1, + "state": "completed" + }, + "command-5ca0b6a7e99f4263b2239a95b02ca7e7": { + "operation": "propose", + "request_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "send_count": 1, + "state": "completed" + }, + "command-5ced22700795486fa68a50fcda4767b8": { + "operation": "propose", + "request_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "send_count": 1, + "state": "completed" + }, + "command-5e27abcb6c914c5ca0ae77ddee974bbc": { + "operation": "dispatch", + "request_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "send_count": 1, + "state": "completed" + }, + "command-7661dbdae1c34bd797ca17b3b5321a5e": { + "operation": "propose", + "request_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "send_count": 1, + "state": "completed" + }, + "command-7ce7b5eb9b5b4b1d953e624d2312327b": { + "operation": "dispatch", + "request_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "send_count": 1, + "state": "completed" + }, + "command-84c016d3c2564ecea6025edaea7d23e7": { + "operation": "propose", + "request_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "send_count": 1, + "state": "completed" + }, + "command-8c948c9df6074ee5969ef8b19a3eaddb": { + "operation": "propose", + "request_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-9dd2a0c9603f4667956ec976e822b74a": { + "operation": "observe", + "request_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "send_count": 1, + "state": "completed" + }, + "command-9f8d9402f1d74504b579f39b9c793576": { + "operation": "propose", + "request_sha256": "1c6f1885adbe13c8b3ab749812e10139310fcbadf478b3debaa8bc025bda45b0", + "send_count": 1, + "state": "completed" + }, + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee": { + "operation": "dispatch", + "request_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "send_count": 1, + "state": "completed" + }, + "command-ab4679dd21e14ce08f70b1ef63bd5868": { + "operation": "observe", + "request_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "send_count": 1, + "state": "completed" + }, + "command-b17181d5918a492a916e0e3c5a2f3a11": { + "operation": "propose", + "request_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "send_count": 1, + "state": "completed" + }, + "command-b182534626d34766a52d5e11d48239e7": { + "operation": "observe", + "request_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "send_count": 1, + "state": "completed" + }, + "command-bdc662ab7f4645d79c02f84921f3c9f6": { + "operation": "dispatch", + "request_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "send_count": 1, + "state": "completed" + }, + "command-c80c18cc647b45fe8e9faf2fa890283c": { + "operation": "dispatch", + "request_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "send_count": 1, + "state": "completed" + }, + "command-c8be0caf76504410a68a595296d56bd3": { + "operation": "observe", + "request_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "send_count": 1, + "state": "completed" + }, + "command-cf898dd6e56a4d8bb0a5894371f9ba81": { + "operation": "dispatch", + "request_sha256": "bd591cd9646fb80860bc791d8b9d02fd3695a496ead456ceb7f903185883edc9", + "send_count": 1, + "state": "unknown" + }, + "command-d9922eefd392420b96bf0a62d7eea982": { + "operation": "dispatch", + "request_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "send_count": 1, + "state": "completed" + }, + "command-df199bef22b442168d0a42242c00c5c4": { + "operation": "observe", + "request_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "send_count": 1, + "state": "completed" + }, + "command-e19f5d23dd944e40b806e930f7d44b35": { + "operation": "dispatch", + "request_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "send_count": 1, + "state": "completed" + }, + "command-e1af2fbe5a254692bf212769ddfea045": { + "operation": "observe", + "request_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + }, + "command-ede04073e3654f4fa17f0a0056db9004": { + "operation": "dispatch", + "request_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "send_count": 1, + "state": "completed" + }, + "command-ef48ba673c2b4ae08b09fbbc4c56fca8": { + "operation": "start_job", + "request_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "send_count": 1, + "state": "completed" + }, + "command-f008b852a29348218bc57e931887b963": { + "operation": "dispatch", + "request_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "send_count": 1, + "state": "completed" + }, + "command-f0d26ac5d6914f1bb038a57ee515b7f0": { + "operation": "observe", + "request_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "send_count": 1, + "state": "completed" + }, + "command-f4c202be91064d3e8f4c929f1175450e": { + "operation": "propose", + "request_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "send_count": 1, + "state": "completed" + }, + "command-f7b8d8e5cddb47ecbac988938ccee2a0": { + "operation": "observe", + "request_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "send_count": 1, + "state": "completed" + }, + "command-fda8dcacccc940c493511804050cc0ec": { + "operation": "propose", + "request_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 2, + "lease_id": "8acd97f5a9dc48b583d6d3097f60b1ba" + }, + "pending_command_ids": [ + "command-cf898dd6e56a4d8bb0a5894371f9ba81" + ] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 10, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-28dc527b5ab349cab70e9bea9dbea92a-118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa.png", + "sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "runner_status": "blocked", + "sequence": 130, + "stage": { + "arguments": { + "proposal_id": "proposal-2fc20beaf42745488b7a16af3afeaf6d" + }, + "before_commands": [ + "command-015d5d03d0624f14ba43d889871c45ee", + "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "command-0e35d66b16a64168b11d255a1d871e71", + "command-1936c404000d4b55a094c49a2fd9eeb3", + "command-1e2386315d6c4c7692405dac7dfa7bd1", + "command-210150e3ac9f45adae24e89f22a66286", + "command-24230a0f96bc4e84a84f720b1c974054", + "command-248b9798493e4a43a51259929e135e29", + "command-2a6e74bdbe424b3caa700dc047365a1c", + "command-2d0b436b61124502a9d8558816bda68d", + "command-3083d955014b4e5282809a47046c53db", + "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "command-38a300b989a649fd9ee243704cdc65a9", + "command-40c6a8a1f1a04ea7bec902767b57387a", + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "command-553be6e82b274944b5e6ea9e2cd3902e", + "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "command-5ced22700795486fa68a50fcda4767b8", + "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "command-7661dbdae1c34bd797ca17b3b5321a5e", + "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "command-84c016d3c2564ecea6025edaea7d23e7", + "command-8c948c9df6074ee5969ef8b19a3eaddb", + "command-933c11248d6a48079ead24676a2df7fb", + "command-9dd2a0c9603f4667956ec976e822b74a", + "command-9f8d9402f1d74504b579f39b9c793576", + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "command-ab4679dd21e14ce08f70b1ef63bd5868", + "command-b17181d5918a492a916e0e3c5a2f3a11", + "command-b182534626d34766a52d5e11d48239e7", + "command-bdc662ab7f4645d79c02f84921f3c9f6", + "command-c80c18cc647b45fe8e9faf2fa890283c", + "command-c8be0caf76504410a68a595296d56bd3", + "command-d9922eefd392420b96bf0a62d7eea982", + "command-df199bef22b442168d0a42242c00c5c4", + "command-e19f5d23dd944e40b806e930f7d44b35", + "command-e1af2fbe5a254692bf212769ddfea045", + "command-e897a3cba83b4d83b460a556be5e459b", + "command-ede04073e3654f4fa17f0a0056db9004", + "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "command-f008b852a29348218bc57e931887b963", + "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "command-f4c202be91064d3e8f4c929f1175450e", + "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "command-fda8dcacccc940c493511804050cc0ec" + ], + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "context": { + "action": { + "action": "move", + "intent": "Push the block right so its center reaches the measurement region.", + "target_x": 0.025, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/calls/c922a26d-9121-4d7e-82e1-7b61b0e42161-02", + "decision_epoch": 7, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161" + }, + "control_epoch": 7, + "operation": "dispatch" + }, + "usage": { + "calls": 16, + "input_tokens": 185456, + "output_tokens": 2222, + "reported_calls": 14 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + }, + { + "command_id": "command-0e35d66b16a64168b11d255a1d871e71", + "payload_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "sequence": 67 + }, + { + "command_id": "command-fda8dcacccc940c493511804050cc0ec", + "payload_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "sequence": 79 + }, + { + "command_id": "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "payload_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "sequence": 87 + }, + { + "command_id": "command-c8be0caf76504410a68a595296d56bd3", + "payload_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "sequence": 95 + }, + { + "command_id": "command-f4c202be91064d3e8f4c929f1175450e", + "payload_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "sequence": 107 + }, + { + "command_id": "command-e19f5d23dd944e40b806e930f7d44b35", + "payload_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "sequence": 115 + }, + { + "command_id": "command-1936c404000d4b55a094c49a2fd9eeb3", + "payload_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "sequence": 123 + }, + { + "command_id": "command-8c948c9df6074ee5969ef8b19a3eaddb", + "payload_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "sequence": 135 + }, + { + "command_id": "command-bdc662ab7f4645d79c02f84921f3c9f6", + "payload_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "sequence": 143 + }, + { + "command_id": "command-e1af2fbe5a254692bf212769ddfea045", + "payload_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "sequence": 151 + }, + { + "command_id": "command-b17181d5918a492a916e0e3c5a2f3a11", + "payload_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "sequence": 163 + }, + { + "command_id": "command-2a6e74bdbe424b3caa700dc047365a1c", + "payload_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "sequence": 171 + }, + { + "command_id": "command-df199bef22b442168d0a42242c00c5c4", + "payload_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "sequence": 179 + }, + { + "command_id": "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "payload_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "sequence": 191 + }, + { + "command_id": "command-f008b852a29348218bc57e931887b963", + "payload_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "sequence": 199 + }, + { + "command_id": "command-2d0b436b61124502a9d8558816bda68d", + "payload_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "sequence": 207 + }, + { + "command_id": "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "payload_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "sequence": 219 + }, + { + "command_id": "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "payload_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "sequence": 227 + }, + { + "command_id": "command-b182534626d34766a52d5e11d48239e7", + "payload_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "sequence": 235 + }, + { + "command_id": "command-84c016d3c2564ecea6025edaea7d23e7", + "payload_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "sequence": 247 + }, + { + "command_id": "command-ede04073e3654f4fa17f0a0056db9004", + "payload_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "sequence": 255 + }, + { + "command_id": "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "payload_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "sequence": 263 + }, + { + "command_id": "command-5ced22700795486fa68a50fcda4767b8", + "payload_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "sequence": 275 + }, + { + "command_id": "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "payload_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "sequence": 283 + }, + { + "command_id": "command-9dd2a0c9603f4667956ec976e822b74a", + "payload_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "sequence": 291 + }, + { + "command_id": "command-3083d955014b4e5282809a47046c53db", + "payload_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "sequence": 303 + }, + { + "command_id": "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "payload_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "sequence": 311 + }, + { + "command_id": "command-38a300b989a649fd9ee243704cdc65a9", + "payload_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "sequence": 319 + }, + { + "command_id": "command-553be6e82b274944b5e6ea9e2cd3902e", + "payload_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "sequence": 331 + }, + { + "command_id": "command-d9922eefd392420b96bf0a62d7eea982", + "payload_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "sequence": 339 + }, + { + "command_id": "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "payload_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "sequence": 347 + }, + { + "command_id": "command-7661dbdae1c34bd797ca17b3b5321a5e", + "payload_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "sequence": 359 + }, + { + "command_id": "command-c80c18cc647b45fe8e9faf2fa890283c", + "payload_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "sequence": 367 + }, + { + "command_id": "command-248b9798493e4a43a51259929e135e29", + "payload_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "sequence": 375 + }, + { + "command_id": "command-015d5d03d0624f14ba43d889871c45ee", + "payload_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "sequence": 387 + }, + { + "command_id": "command-210150e3ac9f45adae24e89f22a66286", + "payload_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "sequence": 395 + }, + { + "command_id": "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "payload_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "sequence": 403 + }, + { + "command_id": "command-ab4679dd21e14ce08f70b1ef63bd5868", + "payload_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "sequence": 411 + }, + { + "command_id": "command-40c6a8a1f1a04ea7bec902767b57387a", + "payload_sha256": "7bb39849a52b98bf039deebb6371d28ece55c04faed9809c9866c6babae4ef26", + "sequence": 480 + }, + { + "command_id": "command-9f8d9402f1d74504b579f39b9c793576", + "payload_sha256": "1c6f1885adbe13c8b3ab749812e10139310fcbadf478b3debaa8bc025bda45b0", + "sequence": 492 + }, + { + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "payload_sha256": "bd591cd9646fb80860bc791d8b9d02fd3695a496ead456ceb7f903185883edc9", + "sequence": 500 + } + ], + "processes": { + "body": 74921, + "workbench": 39967 + }, + "public": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "activity": [ + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-015d5d03d0624f14ba43d889871c45ee", + "seq": 107, + "time": 1791828950.9307368 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 108, + "time": 1791828950.97887 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-210150e3ac9f45adae24e89f22a66286", + "seq": 109, + "time": 1791828951.468124 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 110, + "time": 1791828951.5941691 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "start_job: command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "seq": 111, + "time": 1791828951.745876 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 112, + "time": 1791828951.8522959 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-ab4679dd21e14ce08f70b1ef63bd5868", + "seq": 113, + "time": 1791828952.1878028 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "1", + "seq": 114, + "time": 1791828952.191465 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 115, + "time": 1791828953.570592 + }, + { + "intent": "", + "kind": "halt", + "outcome": "operator_pause_requested", + "seq": 116, + "time": 1791828953.590626 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "seq": 117, + "time": 1791829010.0203352 + }, + { + "intent": "", + "kind": "body_epoch_changed", + "outcome": "1 -> 2", + "seq": 118, + "time": 1791829010.021775 + }, + { + "intent": "", + "kind": "fault_armed", + "outcome": "drop_after", + "seq": 119, + "time": 1791829019.593674 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 120, + "time": 1791829019.920613 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 121, + "time": 1791829019.99926 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-40c6a8a1f1a04ea7bec902767b57387a", + "seq": 122, + "time": 1791829020.389618 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "2", + "seq": 123, + "time": 1791829020.392122 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 124, + "time": 1791829028.703949 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "propose: command-9f8d9402f1d74504b579f39b9c793576", + "seq": 125, + "time": 1791829028.798069 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 126, + "time": 1791829028.864546 + }, + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 127, + "time": 1791829029.1331468 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 128, + "time": 1791829042.14413 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Body restarted from boot 2 to 3; check pending receipts and explicitly Resume for a fresh image", + "seq": 129, + "time": 1791829080.500874 + }, + { + "intent": "", + "kind": "body_epoch_changed", + "outcome": "2 -> 3", + "seq": 130, + "time": 1791829080.502091 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "fault": null, + "reason": "Body restarted from boot 2 to 3; check pending receipts and explicitly Resume for a fresh image", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 2, + "disturbance_test": false, + "error": "Body restarted from boot 2 to 3; check pending receipts and explicitly Resume for a fresh image", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "Body restarted from boot 2 to 3; check pending receipts and explicitly Resume for a fresh image" + }, + "recovery": { + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "operation": "dispatch", + "reason": "Body restarted from boot 2 to 3; check pending receipts and explicitly Resume for a fresh image", + "state": "uncertain" + }, + "runner_status": "blocked", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 16, + "input_tokens": 185456, + "output_tokens": 2222, + "reported_calls": 14, + "token_usage_complete": false + } + }, + "time": 1791829080.5281138 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000008-before.json b/jevdual/results/workbench-restarts-20261012/snapshots/000008-before.json new file mode 100644 index 0000000..bf6647c --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000008-before.json @@ -0,0 +1,884 @@ +{ + "body_identity": null, + "body_identity_error": "[Errno 61] Connection refused", + "body_state": null, + "body_state_error": "[Errno 61] Connection refused", + "client": { + "commands": { + "command-015d5d03d0624f14ba43d889871c45ee": { + "operation": "propose", + "request_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "send_count": 1, + "state": "completed" + }, + "command-097e6bec1ce24ef2bc8f37c40f80d98f": { + "operation": "dispatch", + "request_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "send_count": 1, + "state": "completed" + }, + "command-0e35d66b16a64168b11d255a1d871e71": { + "operation": "observe", + "request_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "send_count": 1, + "state": "completed" + }, + "command-1936c404000d4b55a094c49a2fd9eeb3": { + "operation": "observe", + "request_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "send_count": 1, + "state": "completed" + }, + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-210150e3ac9f45adae24e89f22a66286": { + "operation": "dispatch", + "request_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "completed" + }, + "command-248b9798493e4a43a51259929e135e29": { + "operation": "observe", + "request_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "send_count": 1, + "state": "completed" + }, + "command-2a6e74bdbe424b3caa700dc047365a1c": { + "operation": "dispatch", + "request_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "send_count": 1, + "state": "completed" + }, + "command-2d0b436b61124502a9d8558816bda68d": { + "operation": "observe", + "request_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "send_count": 1, + "state": "completed" + }, + "command-3083d955014b4e5282809a47046c53db": { + "operation": "propose", + "request_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "send_count": 1, + "state": "completed" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-38a300b989a649fd9ee243704cdc65a9": { + "operation": "observe", + "request_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "send_count": 1, + "state": "completed" + }, + "command-40c6a8a1f1a04ea7bec902767b57387a": { + "operation": "observe", + "request_sha256": "7bb39849a52b98bf039deebb6371d28ece55c04faed9809c9866c6babae4ef26", + "send_count": 1, + "state": "completed" + }, + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e": { + "operation": "propose", + "request_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "send_count": 1, + "state": "completed" + }, + "command-553be6e82b274944b5e6ea9e2cd3902e": { + "operation": "propose", + "request_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "send_count": 1, + "state": "completed" + }, + "command-5ca0b6a7e99f4263b2239a95b02ca7e7": { + "operation": "propose", + "request_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "send_count": 1, + "state": "completed" + }, + "command-5ced22700795486fa68a50fcda4767b8": { + "operation": "propose", + "request_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "send_count": 1, + "state": "completed" + }, + "command-5e27abcb6c914c5ca0ae77ddee974bbc": { + "operation": "dispatch", + "request_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "send_count": 1, + "state": "completed" + }, + "command-7661dbdae1c34bd797ca17b3b5321a5e": { + "operation": "propose", + "request_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "send_count": 1, + "state": "completed" + }, + "command-7ce7b5eb9b5b4b1d953e624d2312327b": { + "operation": "dispatch", + "request_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "send_count": 1, + "state": "completed" + }, + "command-84c016d3c2564ecea6025edaea7d23e7": { + "operation": "propose", + "request_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "send_count": 1, + "state": "completed" + }, + "command-8c948c9df6074ee5969ef8b19a3eaddb": { + "operation": "propose", + "request_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-9dd2a0c9603f4667956ec976e822b74a": { + "operation": "observe", + "request_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "send_count": 1, + "state": "completed" + }, + "command-9f8d9402f1d74504b579f39b9c793576": { + "operation": "propose", + "request_sha256": "1c6f1885adbe13c8b3ab749812e10139310fcbadf478b3debaa8bc025bda45b0", + "send_count": 1, + "state": "completed" + }, + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee": { + "operation": "dispatch", + "request_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "send_count": 1, + "state": "completed" + }, + "command-ab4679dd21e14ce08f70b1ef63bd5868": { + "operation": "observe", + "request_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "send_count": 1, + "state": "completed" + }, + "command-b17181d5918a492a916e0e3c5a2f3a11": { + "operation": "propose", + "request_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "send_count": 1, + "state": "completed" + }, + "command-b182534626d34766a52d5e11d48239e7": { + "operation": "observe", + "request_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "send_count": 1, + "state": "completed" + }, + "command-bdc662ab7f4645d79c02f84921f3c9f6": { + "operation": "dispatch", + "request_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "send_count": 1, + "state": "completed" + }, + "command-c80c18cc647b45fe8e9faf2fa890283c": { + "operation": "dispatch", + "request_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "send_count": 1, + "state": "completed" + }, + "command-c8be0caf76504410a68a595296d56bd3": { + "operation": "observe", + "request_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "send_count": 1, + "state": "completed" + }, + "command-cf898dd6e56a4d8bb0a5894371f9ba81": { + "operation": "dispatch", + "request_sha256": "bd591cd9646fb80860bc791d8b9d02fd3695a496ead456ceb7f903185883edc9", + "send_count": 1, + "state": "unknown" + }, + "command-d9922eefd392420b96bf0a62d7eea982": { + "operation": "dispatch", + "request_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "send_count": 1, + "state": "completed" + }, + "command-df199bef22b442168d0a42242c00c5c4": { + "operation": "observe", + "request_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "send_count": 1, + "state": "completed" + }, + "command-e19f5d23dd944e40b806e930f7d44b35": { + "operation": "dispatch", + "request_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "send_count": 1, + "state": "completed" + }, + "command-e1af2fbe5a254692bf212769ddfea045": { + "operation": "observe", + "request_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + }, + "command-ede04073e3654f4fa17f0a0056db9004": { + "operation": "dispatch", + "request_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "send_count": 1, + "state": "completed" + }, + "command-ef48ba673c2b4ae08b09fbbc4c56fca8": { + "operation": "start_job", + "request_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "send_count": 1, + "state": "completed" + }, + "command-f008b852a29348218bc57e931887b963": { + "operation": "dispatch", + "request_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "send_count": 1, + "state": "completed" + }, + "command-f0d26ac5d6914f1bb038a57ee515b7f0": { + "operation": "observe", + "request_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "send_count": 1, + "state": "completed" + }, + "command-f4c202be91064d3e8f4c929f1175450e": { + "operation": "propose", + "request_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "send_count": 1, + "state": "completed" + }, + "command-f7b8d8e5cddb47ecbac988938ccee2a0": { + "operation": "observe", + "request_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "send_count": 1, + "state": "completed" + }, + "command-fda8dcacccc940c493511804050cc0ec": { + "operation": "propose", + "request_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 2, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 2, + "lease_id": "8acd97f5a9dc48b583d6d3097f60b1ba" + }, + "pending_command_ids": [ + "command-cf898dd6e56a4d8bb0a5894371f9ba81" + ] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 10, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-28dc527b5ab349cab70e9bea9dbea92a-118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa.png", + "sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "runner_status": "paused", + "sequence": 128, + "stage": { + "arguments": { + "proposal_id": "proposal-2fc20beaf42745488b7a16af3afeaf6d" + }, + "before_commands": [ + "command-015d5d03d0624f14ba43d889871c45ee", + "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "command-0e35d66b16a64168b11d255a1d871e71", + "command-1936c404000d4b55a094c49a2fd9eeb3", + "command-1e2386315d6c4c7692405dac7dfa7bd1", + "command-210150e3ac9f45adae24e89f22a66286", + "command-24230a0f96bc4e84a84f720b1c974054", + "command-248b9798493e4a43a51259929e135e29", + "command-2a6e74bdbe424b3caa700dc047365a1c", + "command-2d0b436b61124502a9d8558816bda68d", + "command-3083d955014b4e5282809a47046c53db", + "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "command-38a300b989a649fd9ee243704cdc65a9", + "command-40c6a8a1f1a04ea7bec902767b57387a", + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "command-553be6e82b274944b5e6ea9e2cd3902e", + "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "command-5ced22700795486fa68a50fcda4767b8", + "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "command-7661dbdae1c34bd797ca17b3b5321a5e", + "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "command-84c016d3c2564ecea6025edaea7d23e7", + "command-8c948c9df6074ee5969ef8b19a3eaddb", + "command-933c11248d6a48079ead24676a2df7fb", + "command-9dd2a0c9603f4667956ec976e822b74a", + "command-9f8d9402f1d74504b579f39b9c793576", + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "command-ab4679dd21e14ce08f70b1ef63bd5868", + "command-b17181d5918a492a916e0e3c5a2f3a11", + "command-b182534626d34766a52d5e11d48239e7", + "command-bdc662ab7f4645d79c02f84921f3c9f6", + "command-c80c18cc647b45fe8e9faf2fa890283c", + "command-c8be0caf76504410a68a595296d56bd3", + "command-d9922eefd392420b96bf0a62d7eea982", + "command-df199bef22b442168d0a42242c00c5c4", + "command-e19f5d23dd944e40b806e930f7d44b35", + "command-e1af2fbe5a254692bf212769ddfea045", + "command-e897a3cba83b4d83b460a556be5e459b", + "command-ede04073e3654f4fa17f0a0056db9004", + "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "command-f008b852a29348218bc57e931887b963", + "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "command-f4c202be91064d3e8f4c929f1175450e", + "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "command-fda8dcacccc940c493511804050cc0ec" + ], + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "context": { + "action": { + "action": "move", + "intent": "Push the block right so its center reaches the measurement region.", + "target_x": 0.025, + "target_y": 0.0 + }, + "call_dir": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/calls/c922a26d-9121-4d7e-82e1-7b61b0e42161-02", + "decision_epoch": 7, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161" + }, + "control_epoch": 7, + "operation": "dispatch" + }, + "usage": { + "calls": 16, + "input_tokens": 185456, + "output_tokens": 2222, + "reported_calls": 14 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + }, + { + "command_id": "command-0e35d66b16a64168b11d255a1d871e71", + "payload_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "sequence": 67 + }, + { + "command_id": "command-fda8dcacccc940c493511804050cc0ec", + "payload_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "sequence": 79 + }, + { + "command_id": "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "payload_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "sequence": 87 + }, + { + "command_id": "command-c8be0caf76504410a68a595296d56bd3", + "payload_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "sequence": 95 + }, + { + "command_id": "command-f4c202be91064d3e8f4c929f1175450e", + "payload_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "sequence": 107 + }, + { + "command_id": "command-e19f5d23dd944e40b806e930f7d44b35", + "payload_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "sequence": 115 + }, + { + "command_id": "command-1936c404000d4b55a094c49a2fd9eeb3", + "payload_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "sequence": 123 + }, + { + "command_id": "command-8c948c9df6074ee5969ef8b19a3eaddb", + "payload_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "sequence": 135 + }, + { + "command_id": "command-bdc662ab7f4645d79c02f84921f3c9f6", + "payload_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "sequence": 143 + }, + { + "command_id": "command-e1af2fbe5a254692bf212769ddfea045", + "payload_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "sequence": 151 + }, + { + "command_id": "command-b17181d5918a492a916e0e3c5a2f3a11", + "payload_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "sequence": 163 + }, + { + "command_id": "command-2a6e74bdbe424b3caa700dc047365a1c", + "payload_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "sequence": 171 + }, + { + "command_id": "command-df199bef22b442168d0a42242c00c5c4", + "payload_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "sequence": 179 + }, + { + "command_id": "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "payload_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "sequence": 191 + }, + { + "command_id": "command-f008b852a29348218bc57e931887b963", + "payload_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "sequence": 199 + }, + { + "command_id": "command-2d0b436b61124502a9d8558816bda68d", + "payload_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "sequence": 207 + }, + { + "command_id": "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "payload_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "sequence": 219 + }, + { + "command_id": "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "payload_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "sequence": 227 + }, + { + "command_id": "command-b182534626d34766a52d5e11d48239e7", + "payload_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "sequence": 235 + }, + { + "command_id": "command-84c016d3c2564ecea6025edaea7d23e7", + "payload_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "sequence": 247 + }, + { + "command_id": "command-ede04073e3654f4fa17f0a0056db9004", + "payload_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "sequence": 255 + }, + { + "command_id": "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "payload_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "sequence": 263 + }, + { + "command_id": "command-5ced22700795486fa68a50fcda4767b8", + "payload_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "sequence": 275 + }, + { + "command_id": "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "payload_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "sequence": 283 + }, + { + "command_id": "command-9dd2a0c9603f4667956ec976e822b74a", + "payload_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "sequence": 291 + }, + { + "command_id": "command-3083d955014b4e5282809a47046c53db", + "payload_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "sequence": 303 + }, + { + "command_id": "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "payload_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "sequence": 311 + }, + { + "command_id": "command-38a300b989a649fd9ee243704cdc65a9", + "payload_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "sequence": 319 + }, + { + "command_id": "command-553be6e82b274944b5e6ea9e2cd3902e", + "payload_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "sequence": 331 + }, + { + "command_id": "command-d9922eefd392420b96bf0a62d7eea982", + "payload_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "sequence": 339 + }, + { + "command_id": "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "payload_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "sequence": 347 + }, + { + "command_id": "command-7661dbdae1c34bd797ca17b3b5321a5e", + "payload_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "sequence": 359 + }, + { + "command_id": "command-c80c18cc647b45fe8e9faf2fa890283c", + "payload_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "sequence": 367 + }, + { + "command_id": "command-248b9798493e4a43a51259929e135e29", + "payload_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "sequence": 375 + }, + { + "command_id": "command-015d5d03d0624f14ba43d889871c45ee", + "payload_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "sequence": 387 + }, + { + "command_id": "command-210150e3ac9f45adae24e89f22a66286", + "payload_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "sequence": 395 + }, + { + "command_id": "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "payload_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "sequence": 403 + }, + { + "command_id": "command-ab4679dd21e14ce08f70b1ef63bd5868", + "payload_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "sequence": 411 + }, + { + "command_id": "command-40c6a8a1f1a04ea7bec902767b57387a", + "payload_sha256": "7bb39849a52b98bf039deebb6371d28ece55c04faed9809c9866c6babae4ef26", + "sequence": 480 + }, + { + "command_id": "command-9f8d9402f1d74504b579f39b9c793576", + "payload_sha256": "1c6f1885adbe13c8b3ab749812e10139310fcbadf478b3debaa8bc025bda45b0", + "sequence": 492 + }, + { + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "payload_sha256": "bd591cd9646fb80860bc791d8b9d02fd3695a496ead456ceb7f903185883edc9", + "sequence": 500 + } + ], + "processes": { + "body": null, + "workbench": 39967 + }, + "public": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "activity": [ + { + "intent": "Restart inspection 01", + "kind": "inference", + "outcome": "14", + "seq": 105, + "time": 1791828938.608861 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 106, + "time": 1791828950.833822 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "propose: command-015d5d03d0624f14ba43d889871c45ee", + "seq": 107, + "time": 1791828950.9307368 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 108, + "time": 1791828950.97887 + }, + { + "intent": "Restart inspection 01", + "kind": "rpc_applied", + "outcome": "dispatch: command-210150e3ac9f45adae24e89f22a66286", + "seq": 109, + "time": 1791828951.468124 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "start_job", + "seq": 110, + "time": 1791828951.5941691 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "start_job: command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "seq": 111, + "time": 1791828951.745876 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 112, + "time": 1791828951.8522959 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-ab4679dd21e14ce08f70b1ef63bd5868", + "seq": 113, + "time": 1791828952.1878028 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "1", + "seq": 114, + "time": 1791828952.191465 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 115, + "time": 1791828953.570592 + }, + { + "intent": "", + "kind": "halt", + "outcome": "operator_pause_requested", + "seq": 116, + "time": 1791828953.590626 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image", + "seq": 117, + "time": 1791829010.0203352 + }, + { + "intent": "", + "kind": "body_epoch_changed", + "outcome": "1 -> 2", + "seq": 118, + "time": 1791829010.021775 + }, + { + "intent": "", + "kind": "fault_armed", + "outcome": "drop_after", + "seq": 119, + "time": 1791829019.593674 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 120, + "time": 1791829019.920613 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 121, + "time": 1791829019.99926 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-40c6a8a1f1a04ea7bec902767b57387a", + "seq": 122, + "time": 1791829020.389618 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "2", + "seq": 123, + "time": 1791829020.392122 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 124, + "time": 1791829028.703949 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "propose: command-9f8d9402f1d74504b579f39b9c793576", + "seq": 125, + "time": 1791829028.798069 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 126, + "time": 1791829028.864546 + }, + { + "intent": "", + "kind": "halt", + "outcome": "reply is uncertain; reconcile exact command without resending", + "seq": 127, + "time": 1791829029.1331468 + }, + { + "intent": "", + "kind": "halt", + "outcome": "Remote end closed connection without response", + "seq": 128, + "time": 1791829042.14413 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 2, + "fault": null, + "reason": "Remote end closed connection without response", + "state": "unavailable" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 2, + "disturbance_test": false, + "error": "Remote end closed connection without response", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": "dispatch", + "readiness": { + "ready": false, + "reason": "Remote end closed connection without response" + }, + "recovery": { + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "paused", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 16, + "input_tokens": 185456, + "output_tokens": 2222, + "reported_calls": 14, + "token_usage_complete": false + } + }, + "time": 1791829079.486072 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000009-after.json b/jevdual/results/workbench-restarts-20261012/snapshots/000009-after.json new file mode 100644 index 0000000..b549285 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000009-after.json @@ -0,0 +1,1385 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": null, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 20, + "jobs": { + "20eb6586-e73a-4db4-aaac-db3e5d537329": { + "completed_generation": 10, + "completed_native_time_s": 21.679999999998863, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement_operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "measurement_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.46799999999962, + "operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "source_kind": "mujoco_simulation", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9", + "version": 1, + "worker_pid": 51928 + }, + "moves": 10, + "phase": "completed", + "reset_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "reset_started_generation": 2, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "c922a26d-9121-4d7e-82e1-7b61b0e42161": { + "completed_generation": 20, + "completed_native_time_s": 42.630000000006135, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "measurement_operation_id": "agent-12d303b3be23401d9cdd1b1296f3c87a", + "measurement_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 12, + "image_sha256": "60d8074b8c92a02b009c072c57bc1340098311df51c08f9d9d02d41f596c342e", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 26.349999999996275, + "operation_id": "agent-12d303b3be23401d9cdd1b1296f3c87a", + "source_kind": "mujoco_simulation", + "state_sha256": "7ced546541611d509086f34fc585c77771e272c65f3bbb518c94fb1942dbcd16", + "version": 1, + "worker_pid": 77998 + }, + "moves": 10, + "phase": "completed", + "reset_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 20, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "kind": "finish_reset", + "native_time_s": 42.630000000006135, + "operation_id": "agent-7acee30a3cb442d9a3a2b3a9bfec10ed", + "pusher_home_verified": true, + "reset_contact_samples": 3314, + "reset_native_steps": 8140, + "state_sha256": "02c49f9f03f16f8de552b4b4ce57b6d78c85e291fc3dbf1b6acb0ce4b7cd638b" + }, + "reset_started_generation": 12, + "started_generation": 10, + "started_native_time_s": 21.679999999998863, + "target_zone": { + "x_max": 0.115, + "x_min": 0.045, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 42.630000000006135, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "epoch": 3, + "expires_in_s": 0, + "lease_id": "64fd21cec5f8404abdfe09dbbecfe4f8" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-009165e2aa1d4b6d8c000766c3967914": { + "operation": "propose", + "request_sha256": "1206f95b1d06e80eae8ca4baf0ce26951be111339dc3e2f4d9ebfa1dbe410eab", + "send_count": 1, + "state": "completed" + }, + "command-015d5d03d0624f14ba43d889871c45ee": { + "operation": "propose", + "request_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "send_count": 1, + "state": "completed" + }, + "command-01793f114b2c4d3a8f23b17ebc2b8f1f": { + "operation": "propose", + "request_sha256": "6bf2d4795f61435907607137db5beb6f2607768011e90fb1b8b00082d8c62274", + "send_count": 1, + "state": "completed" + }, + "command-07d3f286ef6846ba90c196ad79a88bff": { + "operation": "dispatch", + "request_sha256": "956cc6d0e3e864da997877f551aa587cb8c18ef23887fa852f68d322858a1934", + "send_count": 1, + "state": "completed" + }, + "command-097e6bec1ce24ef2bc8f37c40f80d98f": { + "operation": "dispatch", + "request_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "send_count": 1, + "state": "completed" + }, + "command-0c04ca9d775443a9b69a78f25f183e7d": { + "operation": "dispatch", + "request_sha256": "e9dce120ea68457626fe8f895b8003547049335bf71398de3a7d45d50475ff93", + "send_count": 1, + "state": "completed" + }, + "command-0d1afb96c11546759d80d26095199c48": { + "operation": "observe", + "request_sha256": "df0a0ddfda89951587e1c2bbae4062b35d48d528b9ff565aad1ae070312bdae2", + "send_count": 1, + "state": "completed" + }, + "command-0d48c82d84d340c6b1d28b76f670dd29": { + "operation": "propose", + "request_sha256": "4335b774435a6b013d28c8327d5fc7e208576eed4d6796b3b671fd41dbf58ba3", + "send_count": 1, + "state": "completed" + }, + "command-0e35d66b16a64168b11d255a1d871e71": { + "operation": "observe", + "request_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "send_count": 1, + "state": "completed" + }, + "command-0ee1728417374a16938ab09aa59e64c0": { + "operation": "propose", + "request_sha256": "32bfef5a823099dab6fc5291582f6faaa0ac10a840ac9e94c38946dab92ae0ab", + "send_count": 1, + "state": "completed" + }, + "command-12c51cb647bc4d01badb3bebf7cb0446": { + "operation": "observe", + "request_sha256": "45dee3c4b5394ccc300f72ba73630f476ddcd992912bb5a1da838e264bf53343", + "send_count": 1, + "state": "completed" + }, + "command-16ac2d834cc949e980dbdc7b3bf60af7": { + "operation": "propose", + "request_sha256": "e750a765511c25e30bf1a54400fae352577c98a13af91dc2bbded25a2b60e0b6", + "send_count": 1, + "state": "completed" + }, + "command-16c006b2256a4000b45fc63cd2d334ff": { + "operation": "dispatch", + "request_sha256": "8f291e7bbe50782c675defef8bc2c5636c22ce4c9e383802274acca154c03a31", + "send_count": 1, + "state": "completed" + }, + "command-1936c404000d4b55a094c49a2fd9eeb3": { + "operation": "observe", + "request_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "send_count": 1, + "state": "completed" + }, + "command-1af4c5bd1341495093201a5821d1dec9": { + "operation": "observe", + "request_sha256": "217b8ec12db8b35fbe968b343144566338207ce3b7d07e9ea8ad8fbf766845a9", + "send_count": 1, + "state": "completed" + }, + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-210150e3ac9f45adae24e89f22a66286": { + "operation": "dispatch", + "request_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "completed" + }, + "command-248b9798493e4a43a51259929e135e29": { + "operation": "observe", + "request_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "send_count": 1, + "state": "completed" + }, + "command-2a6e74bdbe424b3caa700dc047365a1c": { + "operation": "dispatch", + "request_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "send_count": 1, + "state": "completed" + }, + "command-2d0b436b61124502a9d8558816bda68d": { + "operation": "observe", + "request_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "send_count": 1, + "state": "completed" + }, + "command-3083d955014b4e5282809a47046c53db": { + "operation": "propose", + "request_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "send_count": 1, + "state": "completed" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-382a79f756314a4ea88e1836344549d2": { + "operation": "dispatch", + "request_sha256": "3dda2ab314e9003e2f4f2126c449a2e634fdb05cc99050e52a101481650ae9a2", + "send_count": 1, + "state": "completed" + }, + "command-38a300b989a649fd9ee243704cdc65a9": { + "operation": "observe", + "request_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "send_count": 1, + "state": "completed" + }, + "command-38b6786c82b04b3d92ecde7200a19450": { + "operation": "propose", + "request_sha256": "0eedff7111f86a7c96f02b76328e26f9252832fcd0b32fdcfab09fd639cf62ac", + "send_count": 1, + "state": "completed" + }, + "command-40c6a8a1f1a04ea7bec902767b57387a": { + "operation": "observe", + "request_sha256": "7bb39849a52b98bf039deebb6371d28ece55c04faed9809c9866c6babae4ef26", + "send_count": 1, + "state": "completed" + }, + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e": { + "operation": "propose", + "request_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "send_count": 1, + "state": "completed" + }, + "command-4c3705f8d7cc48a4b45029d078357250": { + "operation": "observe", + "request_sha256": "6670065bd1fd29ab4a84a51cb0a31a497929cb54d24b94909462d946768c2d6d", + "send_count": 1, + "state": "completed" + }, + "command-51f66bfa90ab421bb24242caa6d054fd": { + "operation": "observe", + "request_sha256": "327a292c61ede952cf37c3157827e4fa1d919d5e90b244f18010ef464b558f42", + "send_count": 1, + "state": "completed" + }, + "command-553be6e82b274944b5e6ea9e2cd3902e": { + "operation": "propose", + "request_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "send_count": 1, + "state": "completed" + }, + "command-5ca0b6a7e99f4263b2239a95b02ca7e7": { + "operation": "propose", + "request_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "send_count": 1, + "state": "completed" + }, + "command-5ced22700795486fa68a50fcda4767b8": { + "operation": "propose", + "request_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "send_count": 1, + "state": "completed" + }, + "command-5e27abcb6c914c5ca0ae77ddee974bbc": { + "operation": "dispatch", + "request_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "send_count": 1, + "state": "completed" + }, + "command-604436650640419d9ce6c551ac3e3407": { + "operation": "observe", + "request_sha256": "98c01becbe239e62be2fe587554aa3ea29f2273a052c5359e1e5a85fb476362f", + "send_count": 1, + "state": "completed" + }, + "command-69a56ced2e8c49ce9e9f214c57023381": { + "operation": "propose", + "request_sha256": "9cfc65fc7994db66a7ebebdd994fba2dad0a4d54475862539cd87586f2779dfa", + "send_count": 1, + "state": "completed" + }, + "command-6e6b99559d5749eab04cbf8f42b46ae2": { + "operation": "observe", + "request_sha256": "3a2970680dd2675a75e8bbf91affd223084373d5789d6b873b4fe8bdab7dd5aa", + "send_count": 1, + "state": "completed" + }, + "command-74dc57912fda4c3e96f6845cdffbb045": { + "operation": "observe", + "request_sha256": "aed94b91da1d8ac4b41ac1ad21b488bc9c43e6db4066e352dc6bbacceee6b907", + "send_count": 1, + "state": "completed" + }, + "command-7661dbdae1c34bd797ca17b3b5321a5e": { + "operation": "propose", + "request_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "send_count": 1, + "state": "completed" + }, + "command-7b94f03fcd7449ec97ca592813c20693": { + "operation": "observe", + "request_sha256": "e9adecd7c66f60c892d0bf60a14266e115b7948b8cbac2a26aff6e4fb526e615", + "send_count": 1, + "state": "completed" + }, + "command-7ce7b5eb9b5b4b1d953e624d2312327b": { + "operation": "dispatch", + "request_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "send_count": 1, + "state": "completed" + }, + "command-84c016d3c2564ecea6025edaea7d23e7": { + "operation": "propose", + "request_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "send_count": 1, + "state": "completed" + }, + "command-8c948c9df6074ee5969ef8b19a3eaddb": { + "operation": "propose", + "request_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "send_count": 1, + "state": "completed" + }, + "command-90b2509eee524747a3397627f2c51643": { + "operation": "propose", + "request_sha256": "ba39f43b6c1947a04304c95045272d8cff2c0f70c14831e48ef8b77ce03d585b", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-93b6bfdc895349d2a07cfed89231411d": { + "operation": "propose", + "request_sha256": "a1e20276d19935994fb87711dc6ad10ef2a0f5423e274a3c9ca189348e861573", + "send_count": 1, + "state": "completed" + }, + "command-95a7bae7c4f8479b81bae1ea55e7597f": { + "operation": "observe", + "request_sha256": "acc6a031281992af1b58960f9038dc6cab3e67e435b497bfdaaea926b4c47620", + "send_count": 1, + "state": "completed" + }, + "command-96ff5828100a40b9945b5960aeb7e5dd": { + "operation": "dispatch", + "request_sha256": "80aa2ffe12f32c32c279d78eb3775b4311d8b83677f816849f91ae636fec87d0", + "send_count": 1, + "state": "completed" + }, + "command-9841ade4a0ea46a2b43353bf2b951a7c": { + "operation": "propose", + "request_sha256": "7b732e90c18d4e428c43c95403e2afeca93754f75cc963d94ba01cdec8224561", + "send_count": 1, + "state": "completed" + }, + "command-9dd2a0c9603f4667956ec976e822b74a": { + "operation": "observe", + "request_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "send_count": 1, + "state": "completed" + }, + "command-9f8d9402f1d74504b579f39b9c793576": { + "operation": "propose", + "request_sha256": "1c6f1885adbe13c8b3ab749812e10139310fcbadf478b3debaa8bc025bda45b0", + "send_count": 1, + "state": "completed" + }, + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee": { + "operation": "dispatch", + "request_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "send_count": 1, + "state": "completed" + }, + "command-ab4679dd21e14ce08f70b1ef63bd5868": { + "operation": "observe", + "request_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "send_count": 1, + "state": "completed" + }, + "command-acd7ec8de3eb4a548e2e070898a0318f": { + "operation": "dispatch", + "request_sha256": "e963b00f5df17ccc10c5a050cac94cd1515f23b7c9e38ebfd7b97285aa6fb0cf", + "send_count": 1, + "state": "completed" + }, + "command-ad464b677e044b43ae8f9dd725331fce": { + "operation": "observe", + "request_sha256": "2a139a6cd850620070ff777a29a2acbfb326b69d260036bb5847aefd6a5c4c18", + "send_count": 1, + "state": "completed" + }, + "command-b17181d5918a492a916e0e3c5a2f3a11": { + "operation": "propose", + "request_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "send_count": 1, + "state": "completed" + }, + "command-b182534626d34766a52d5e11d48239e7": { + "operation": "observe", + "request_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "send_count": 1, + "state": "completed" + }, + "command-bdc662ab7f4645d79c02f84921f3c9f6": { + "operation": "dispatch", + "request_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "send_count": 1, + "state": "completed" + }, + "command-c5861bc07c0d4b6a992850fe1ab3c769": { + "operation": "dispatch", + "request_sha256": "2747a5c1d597b72581a2ffef5303fd6f137acdae3c6243750c87309aedb74de2", + "send_count": 1, + "state": "completed" + }, + "command-c80c18cc647b45fe8e9faf2fa890283c": { + "operation": "dispatch", + "request_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "send_count": 1, + "state": "completed" + }, + "command-c8be0caf76504410a68a595296d56bd3": { + "operation": "observe", + "request_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "send_count": 1, + "state": "completed" + }, + "command-cd90dd204ff74139880f3846418a41b7": { + "operation": "dispatch", + "request_sha256": "dc58e74652094316cd08bb4ed5a2b9e9ba9a1793326b9918539c6365f1537e3b", + "send_count": 1, + "state": "completed" + }, + "command-cf898dd6e56a4d8bb0a5894371f9ba81": { + "operation": "dispatch", + "request_sha256": "bd591cd9646fb80860bc791d8b9d02fd3695a496ead456ceb7f903185883edc9", + "send_count": 1, + "state": "completed" + }, + "command-cf90ff6221c640f9ac99b5ceb8a91754": { + "operation": "dispatch", + "request_sha256": "9109f545b48718b5291c081e098f1f815b521fae16217993ee9985e898ffc043", + "send_count": 1, + "state": "completed" + }, + "command-cfead433cabe4303b15fd2ca83ee8899": { + "operation": "propose", + "request_sha256": "504d2c9975111bb32e985cc71034239f5d62d10929273eb2ec94d6b5b59534dd", + "send_count": 1, + "state": "completed" + }, + "command-d3e05b3b869542a085afeba4414811ba": { + "operation": "dispatch", + "request_sha256": "cb0758e27127d99fbc0a75c6dc7cfc2d61c2bdf92925e51129e916f3637fee40", + "send_count": 1, + "state": "completed" + }, + "command-d9922eefd392420b96bf0a62d7eea982": { + "operation": "dispatch", + "request_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "send_count": 1, + "state": "completed" + }, + "command-df199bef22b442168d0a42242c00c5c4": { + "operation": "observe", + "request_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "send_count": 1, + "state": "completed" + }, + "command-e19f5d23dd944e40b806e930f7d44b35": { + "operation": "dispatch", + "request_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "send_count": 1, + "state": "completed" + }, + "command-e1af2fbe5a254692bf212769ddfea045": { + "operation": "observe", + "request_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "send_count": 1, + "state": "completed" + }, + "command-e7a47417ff124ebc915329783ad7c5bb": { + "operation": "dispatch", + "request_sha256": "07cb08a35c3378b556b2d9fb19d575ea53e577e4dca56624bf3e5e49e868fe68", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + }, + "command-eb16ab3154eb40f78f021869ebb485b3": { + "operation": "propose", + "request_sha256": "bbd21b24923ad60e0ba472125e2544efe9ab758d7887e2444bd293bd0f5e25b6", + "send_count": 1, + "state": "completed" + }, + "command-ede04073e3654f4fa17f0a0056db9004": { + "operation": "dispatch", + "request_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "send_count": 1, + "state": "completed" + }, + "command-ef48ba673c2b4ae08b09fbbc4c56fca8": { + "operation": "start_job", + "request_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "send_count": 1, + "state": "completed" + }, + "command-f008b852a29348218bc57e931887b963": { + "operation": "dispatch", + "request_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "send_count": 1, + "state": "completed" + }, + "command-f0d26ac5d6914f1bb038a57ee515b7f0": { + "operation": "observe", + "request_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "send_count": 1, + "state": "completed" + }, + "command-f4c202be91064d3e8f4c929f1175450e": { + "operation": "propose", + "request_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "send_count": 1, + "state": "completed" + }, + "command-f7b8d8e5cddb47ecbac988938ccee2a0": { + "operation": "observe", + "request_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "send_count": 1, + "state": "completed" + }, + "command-fb77cbd4d30f4827bdef78ff67cabe2b": { + "operation": "dispatch", + "request_sha256": "286b425e934eb0fa16526805b7ef5fe3431d0d172fdcf02fd4cc9ccb9d89ef5f", + "send_count": 1, + "state": "completed" + }, + "command-fca0d0500040494396aabdb153197133": { + "operation": "observe", + "request_sha256": "36f0a50bf16631b346b7669d99fb49c44bf15ade5d62d3a84e9bfaf016fd1d1f", + "send_count": 1, + "state": "completed" + }, + "command-fda8dcacccc940c493511804050cc0ec": { + "operation": "propose", + "request_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "send_count": 1, + "state": "completed" + }, + "command-fe049e3ca1ce489e9e7cc0e25188a245": { + "operation": "observe", + "request_sha256": "20563be149da9ea5513d1f967925a42fb19aef1a112f38923e15d34a79907c68", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 3, + "lease_id": "64fd21cec5f8404abdfe09dbbecfe4f8" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 20, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-021548ca7f144b57a98d5dfa7df72f15-118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa.png", + "sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "runner_status": "running", + "sequence": 227, + "stage": null, + "usage": { + "calls": 29, + "input_tokens": 343310, + "output_tokens": 3963, + "reported_calls": 26 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + }, + { + "command_id": "command-0e35d66b16a64168b11d255a1d871e71", + "payload_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "sequence": 67 + }, + { + "command_id": "command-fda8dcacccc940c493511804050cc0ec", + "payload_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "sequence": 79 + }, + { + "command_id": "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "payload_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "sequence": 87 + }, + { + "command_id": "command-c8be0caf76504410a68a595296d56bd3", + "payload_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "sequence": 95 + }, + { + "command_id": "command-f4c202be91064d3e8f4c929f1175450e", + "payload_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "sequence": 107 + }, + { + "command_id": "command-e19f5d23dd944e40b806e930f7d44b35", + "payload_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "sequence": 115 + }, + { + "command_id": "command-1936c404000d4b55a094c49a2fd9eeb3", + "payload_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "sequence": 123 + }, + { + "command_id": "command-8c948c9df6074ee5969ef8b19a3eaddb", + "payload_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "sequence": 135 + }, + { + "command_id": "command-bdc662ab7f4645d79c02f84921f3c9f6", + "payload_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "sequence": 143 + }, + { + "command_id": "command-e1af2fbe5a254692bf212769ddfea045", + "payload_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "sequence": 151 + }, + { + "command_id": "command-b17181d5918a492a916e0e3c5a2f3a11", + "payload_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "sequence": 163 + }, + { + "command_id": "command-2a6e74bdbe424b3caa700dc047365a1c", + "payload_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "sequence": 171 + }, + { + "command_id": "command-df199bef22b442168d0a42242c00c5c4", + "payload_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "sequence": 179 + }, + { + "command_id": "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "payload_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "sequence": 191 + }, + { + "command_id": "command-f008b852a29348218bc57e931887b963", + "payload_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "sequence": 199 + }, + { + "command_id": "command-2d0b436b61124502a9d8558816bda68d", + "payload_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "sequence": 207 + }, + { + "command_id": "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "payload_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "sequence": 219 + }, + { + "command_id": "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "payload_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "sequence": 227 + }, + { + "command_id": "command-b182534626d34766a52d5e11d48239e7", + "payload_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "sequence": 235 + }, + { + "command_id": "command-84c016d3c2564ecea6025edaea7d23e7", + "payload_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "sequence": 247 + }, + { + "command_id": "command-ede04073e3654f4fa17f0a0056db9004", + "payload_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "sequence": 255 + }, + { + "command_id": "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "payload_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "sequence": 263 + }, + { + "command_id": "command-5ced22700795486fa68a50fcda4767b8", + "payload_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "sequence": 275 + }, + { + "command_id": "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "payload_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "sequence": 283 + }, + { + "command_id": "command-9dd2a0c9603f4667956ec976e822b74a", + "payload_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "sequence": 291 + }, + { + "command_id": "command-3083d955014b4e5282809a47046c53db", + "payload_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "sequence": 303 + }, + { + "command_id": "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "payload_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "sequence": 311 + }, + { + "command_id": "command-38a300b989a649fd9ee243704cdc65a9", + "payload_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "sequence": 319 + }, + { + "command_id": "command-553be6e82b274944b5e6ea9e2cd3902e", + "payload_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "sequence": 331 + }, + { + "command_id": "command-d9922eefd392420b96bf0a62d7eea982", + "payload_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "sequence": 339 + }, + { + "command_id": "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "payload_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "sequence": 347 + }, + { + "command_id": "command-7661dbdae1c34bd797ca17b3b5321a5e", + "payload_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "sequence": 359 + }, + { + "command_id": "command-c80c18cc647b45fe8e9faf2fa890283c", + "payload_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "sequence": 367 + }, + { + "command_id": "command-248b9798493e4a43a51259929e135e29", + "payload_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "sequence": 375 + }, + { + "command_id": "command-015d5d03d0624f14ba43d889871c45ee", + "payload_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "sequence": 387 + }, + { + "command_id": "command-210150e3ac9f45adae24e89f22a66286", + "payload_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "sequence": 395 + }, + { + "command_id": "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "payload_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "sequence": 403 + }, + { + "command_id": "command-ab4679dd21e14ce08f70b1ef63bd5868", + "payload_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "sequence": 411 + }, + { + "command_id": "command-40c6a8a1f1a04ea7bec902767b57387a", + "payload_sha256": "7bb39849a52b98bf039deebb6371d28ece55c04faed9809c9866c6babae4ef26", + "sequence": 480 + }, + { + "command_id": "command-9f8d9402f1d74504b579f39b9c793576", + "payload_sha256": "1c6f1885adbe13c8b3ab749812e10139310fcbadf478b3debaa8bc025bda45b0", + "sequence": 492 + }, + { + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "payload_sha256": "bd591cd9646fb80860bc791d8b9d02fd3695a496ead456ceb7f903185883edc9", + "sequence": 500 + }, + { + "command_id": "command-604436650640419d9ce6c551ac3e3407", + "payload_sha256": "98c01becbe239e62be2fe587554aa3ea29f2273a052c5359e1e5a85fb476362f", + "sequence": 550 + }, + { + "command_id": "command-01793f114b2c4d3a8f23b17ebc2b8f1f", + "payload_sha256": "6bf2d4795f61435907607137db5beb6f2607768011e90fb1b8b00082d8c62274", + "sequence": 562 + }, + { + "command_id": "command-d3e05b3b869542a085afeba4414811ba", + "payload_sha256": "cb0758e27127d99fbc0a75c6dc7cfc2d61c2bdf92925e51129e916f3637fee40", + "sequence": 570 + }, + { + "command_id": "command-6e6b99559d5749eab04cbf8f42b46ae2", + "payload_sha256": "3a2970680dd2675a75e8bbf91affd223084373d5789d6b873b4fe8bdab7dd5aa", + "sequence": 578 + }, + { + "command_id": "command-90b2509eee524747a3397627f2c51643", + "payload_sha256": "ba39f43b6c1947a04304c95045272d8cff2c0f70c14831e48ef8b77ce03d585b", + "sequence": 590 + }, + { + "command_id": "command-0c04ca9d775443a9b69a78f25f183e7d", + "payload_sha256": "e9dce120ea68457626fe8f895b8003547049335bf71398de3a7d45d50475ff93", + "sequence": 598 + }, + { + "command_id": "command-7b94f03fcd7449ec97ca592813c20693", + "payload_sha256": "e9adecd7c66f60c892d0bf60a14266e115b7948b8cbac2a26aff6e4fb526e615", + "sequence": 606 + }, + { + "command_id": "command-eb16ab3154eb40f78f021869ebb485b3", + "payload_sha256": "bbd21b24923ad60e0ba472125e2544efe9ab758d7887e2444bd293bd0f5e25b6", + "sequence": 618 + }, + { + "command_id": "command-acd7ec8de3eb4a548e2e070898a0318f", + "payload_sha256": "e963b00f5df17ccc10c5a050cac94cd1515f23b7c9e38ebfd7b97285aa6fb0cf", + "sequence": 626 + }, + { + "command_id": "command-4c3705f8d7cc48a4b45029d078357250", + "payload_sha256": "6670065bd1fd29ab4a84a51cb0a31a497929cb54d24b94909462d946768c2d6d", + "sequence": 634 + }, + { + "command_id": "command-0ee1728417374a16938ab09aa59e64c0", + "payload_sha256": "32bfef5a823099dab6fc5291582f6faaa0ac10a840ac9e94c38946dab92ae0ab", + "sequence": 646 + }, + { + "command_id": "command-07d3f286ef6846ba90c196ad79a88bff", + "payload_sha256": "956cc6d0e3e864da997877f551aa587cb8c18ef23887fa852f68d322858a1934", + "sequence": 654 + }, + { + "command_id": "command-74dc57912fda4c3e96f6845cdffbb045", + "payload_sha256": "aed94b91da1d8ac4b41ac1ad21b488bc9c43e6db4066e352dc6bbacceee6b907", + "sequence": 662 + }, + { + "command_id": "command-009165e2aa1d4b6d8c000766c3967914", + "payload_sha256": "1206f95b1d06e80eae8ca4baf0ce26951be111339dc3e2f4d9ebfa1dbe410eab", + "sequence": 674 + }, + { + "command_id": "command-382a79f756314a4ea88e1836344549d2", + "payload_sha256": "3dda2ab314e9003e2f4f2126c449a2e634fdb05cc99050e52a101481650ae9a2", + "sequence": 682 + }, + { + "command_id": "command-0d1afb96c11546759d80d26095199c48", + "payload_sha256": "df0a0ddfda89951587e1c2bbae4062b35d48d528b9ff565aad1ae070312bdae2", + "sequence": 690 + }, + { + "command_id": "command-0d48c82d84d340c6b1d28b76f670dd29", + "payload_sha256": "4335b774435a6b013d28c8327d5fc7e208576eed4d6796b3b671fd41dbf58ba3", + "sequence": 702 + }, + { + "command_id": "command-16c006b2256a4000b45fc63cd2d334ff", + "payload_sha256": "8f291e7bbe50782c675defef8bc2c5636c22ce4c9e383802274acca154c03a31", + "sequence": 710 + }, + { + "command_id": "command-ad464b677e044b43ae8f9dd725331fce", + "payload_sha256": "2a139a6cd850620070ff777a29a2acbfb326b69d260036bb5847aefd6a5c4c18", + "sequence": 718 + }, + { + "command_id": "command-16ac2d834cc949e980dbdc7b3bf60af7", + "payload_sha256": "e750a765511c25e30bf1a54400fae352577c98a13af91dc2bbded25a2b60e0b6", + "sequence": 730 + }, + { + "command_id": "command-fb77cbd4d30f4827bdef78ff67cabe2b", + "payload_sha256": "286b425e934eb0fa16526805b7ef5fe3431d0d172fdcf02fd4cc9ccb9d89ef5f", + "sequence": 738 + }, + { + "command_id": "command-1af4c5bd1341495093201a5821d1dec9", + "payload_sha256": "217b8ec12db8b35fbe968b343144566338207ce3b7d07e9ea8ad8fbf766845a9", + "sequence": 746 + }, + { + "command_id": "command-93b6bfdc895349d2a07cfed89231411d", + "payload_sha256": "a1e20276d19935994fb87711dc6ad10ef2a0f5423e274a3c9ca189348e861573", + "sequence": 758 + }, + { + "command_id": "command-96ff5828100a40b9945b5960aeb7e5dd", + "payload_sha256": "80aa2ffe12f32c32c279d78eb3775b4311d8b83677f816849f91ae636fec87d0", + "sequence": 766 + }, + { + "command_id": "command-fca0d0500040494396aabdb153197133", + "payload_sha256": "36f0a50bf16631b346b7669d99fb49c44bf15ade5d62d3a84e9bfaf016fd1d1f", + "sequence": 774 + }, + { + "command_id": "command-69a56ced2e8c49ce9e9f214c57023381", + "payload_sha256": "9cfc65fc7994db66a7ebebdd994fba2dad0a4d54475862539cd87586f2779dfa", + "sequence": 786 + }, + { + "command_id": "command-cf90ff6221c640f9ac99b5ceb8a91754", + "payload_sha256": "9109f545b48718b5291c081e098f1f815b521fae16217993ee9985e898ffc043", + "sequence": 794 + }, + { + "command_id": "command-51f66bfa90ab421bb24242caa6d054fd", + "payload_sha256": "327a292c61ede952cf37c3157827e4fa1d919d5e90b244f18010ef464b558f42", + "sequence": 802 + }, + { + "command_id": "command-38b6786c82b04b3d92ecde7200a19450", + "payload_sha256": "0eedff7111f86a7c96f02b76328e26f9252832fcd0b32fdcfab09fd639cf62ac", + "sequence": 814 + }, + { + "command_id": "command-c5861bc07c0d4b6a992850fe1ab3c769", + "payload_sha256": "2747a5c1d597b72581a2ffef5303fd6f137acdae3c6243750c87309aedb74de2", + "sequence": 822 + }, + { + "command_id": "command-fe049e3ca1ce489e9e7cc0e25188a245", + "payload_sha256": "20563be149da9ea5513d1f967925a42fb19aef1a112f38923e15d34a79907c68", + "sequence": 830 + }, + { + "command_id": "command-cfead433cabe4303b15fd2ca83ee8899", + "payload_sha256": "504d2c9975111bb32e985cc71034239f5d62d10929273eb2ec94d6b5b59534dd", + "sequence": 842 + }, + { + "command_id": "command-cd90dd204ff74139880f3846418a41b7", + "payload_sha256": "dc58e74652094316cd08bb4ed5a2b9e9ba9a1793326b9918539c6365f1537e3b", + "sequence": 850 + }, + { + "command_id": "command-95a7bae7c4f8479b81bae1ea55e7597f", + "payload_sha256": "acc6a031281992af1b58960f9038dc6cab3e67e435b497bfdaaea926b4c47620", + "sequence": 858 + }, + { + "command_id": "command-12c51cb647bc4d01badb3bebf7cb0446", + "payload_sha256": "45dee3c4b5394ccc300f72ba73630f476ddcd992912bb5a1da838e264bf53343", + "sequence": 871 + }, + { + "command_id": "command-9841ade4a0ea46a2b43353bf2b951a7c", + "payload_sha256": "7b732e90c18d4e428c43c95403e2afeca93754f75cc963d94ba01cdec8224561", + "sequence": 883 + }, + { + "command_id": "command-e7a47417ff124ebc915329783ad7c5bb", + "payload_sha256": "07cb08a35c3378b556b2d9fb19d575ea53e577e4dca56624bf3e5e49e868fe68", + "sequence": 891 + } + ], + "processes": { + "body": 74921, + "workbench": 39967 + }, + "public": { + "active_job_id": null, + "activity": [ + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 204, + "time": 1791829189.127323 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-fe049e3ca1ce489e9e7cc0e25188a245", + "seq": 205, + "time": 1791829189.480195 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "13", + "seq": 206, + "time": 1791829189.4844081 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 207, + "time": 1791829196.4102259 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "propose: command-cfead433cabe4303b15fd2ca83ee8899", + "seq": 208, + "time": 1791829196.602696 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 209, + "time": 1791829196.697315 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "dispatch: command-cd90dd204ff74139880f3846418a41b7", + "seq": 210, + "time": 1791829197.11283 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 211, + "time": 1791829197.23605 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-95a7bae7c4f8479b81bae1ea55e7597f", + "seq": 212, + "time": 1791829197.9852512 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "14", + "seq": 213, + "time": 1791829197.991858 + }, + { + "intent": "", + "kind": "halt", + "outcome": "timed out", + "seq": 214, + "time": 1791829202.567657 + }, + { + "intent": "", + "kind": "halt", + "outcome": "operator_pause_requested", + "seq": 215, + "time": 1791829202.618878 + }, + { + "intent": "", + "kind": "halt", + "outcome": "timed out", + "seq": 216, + "time": 1791829224.8969312 + }, + { + "intent": "", + "kind": "halt", + "outcome": "timed out", + "seq": 217, + "time": 1791829230.2752252 + }, + { + "intent": "", + "kind": "halt", + "outcome": "timed out", + "seq": 218, + "time": 1791829234.4515479 + }, + { + "intent": "", + "kind": "halt", + "outcome": "timed out", + "seq": 219, + "time": 1791829242.3595982 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 220, + "time": 1791829285.4561422 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 221, + "time": 1791829285.550546 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-12c51cb647bc4d01badb3bebf7cb0446", + "seq": 222, + "time": 1791829286.054108 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "15", + "seq": 223, + "time": 1791829286.056795 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 224, + "time": 1791829296.4025989 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "propose: command-9841ade4a0ea46a2b43353bf2b951a7c", + "seq": 225, + "time": 1791829296.548447 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 226, + "time": 1791829296.6237051 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "dispatch: command-e7a47417ff124ebc915329783ad7c5bb", + "seq": 227, + "time": 1791829297.049696 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 20, + "image_url": "/api/frame?generation=20&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 15, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "Finish reset with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 20, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "kind": "finish_reset", + "native_time_s": 42.630000000006135, + "operation_id": "agent-7acee30a3cb442d9a3a2b3a9bfec10ed", + "pusher_home_verified": true, + "reset_contact_samples": 3314, + "reset_native_steps": 8140, + "state_sha256": "02c49f9f03f16f8de552b4b4ce57b6d78c85e291fc3dbf1b6acb0ce4b7cd638b" + }, + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": true, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 29, + "input_tokens": 343310, + "output_tokens": 3963, + "reported_calls": 26, + "token_usage_complete": false + } + }, + "time": 1791829365.090233 +} diff --git a/jevdual/results/workbench-restarts-20261012/snapshots/000009-before.json b/jevdual/results/workbench-restarts-20261012/snapshots/000009-before.json new file mode 100644 index 0000000..6ff4761 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/snapshots/000009-before.json @@ -0,0 +1,1385 @@ +{ + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "body_state": { + "bench": { + "active_job": null, + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "diagnostic_enabled": true, + "generation": 20, + "jobs": { + "20eb6586-e73a-4db4-aaac-db3e5d537329": { + "completed_generation": 10, + "completed_native_time_s": 21.679999999998863, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement_operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "measurement_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 2, + "image_sha256": "bdee3bf2ea5af18d557cb6560b766242a409cd25ac852273ce2724825f12decb", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "native_time_s": 5.46799999999962, + "operation_id": "agent-8831a976a3b7434db04d4fbcbe36a532", + "source_kind": "mujoco_simulation", + "state_sha256": "7ed25a4cf203dc0ebbb58dc4acdaec8b9dd83be0ccd7d6be0d34c6c74ff8cdd9", + "version": 1, + "worker_pid": 51928 + }, + "moves": 10, + "phase": "completed", + "reset_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "reset_started_generation": 2, + "started_generation": 0, + "started_native_time_s": 1.0000000000000007, + "target_zone": { + "x_max": 0.105, + "x_min": 0.035, + "y_max": 0.025, + "y_min": -0.025 + } + }, + "c922a26d-9121-4d7e-82e1-7b61b0e42161": { + "completed_generation": 20, + "completed_native_time_s": 42.630000000006135, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "measurement_operation_id": "agent-12d303b3be23401d9cdd1b1296f3c87a", + "measurement_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "generation": 12, + "image_sha256": "60d8074b8c92a02b009c072c57bc1340098311df51c08f9d9d02d41f596c342e", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "native_time_s": 26.349999999996275, + "operation_id": "agent-12d303b3be23401d9cdd1b1296f3c87a", + "source_kind": "mujoco_simulation", + "state_sha256": "7ced546541611d509086f34fc585c77771e272c65f3bbb518c94fb1942dbcd16", + "version": 1, + "worker_pid": 77998 + }, + "moves": 10, + "phase": "completed", + "reset_receipt": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 20, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "kind": "finish_reset", + "native_time_s": 42.630000000006135, + "operation_id": "agent-7acee30a3cb442d9a3a2b3a9bfec10ed", + "pusher_home_verified": true, + "reset_contact_samples": 3314, + "reset_native_steps": 8140, + "state_sha256": "02c49f9f03f16f8de552b4b4ce57b6d78c85e291fc3dbf1b6acb0ce4b7cd638b" + }, + "reset_started_generation": 12, + "started_generation": 10, + "started_native_time_s": 21.679999999998863, + "target_zone": { + "x_max": 0.115, + "x_min": 0.045, + "y_max": 0.025, + "y_min": -0.025 + } + } + }, + "native_time_s": 42.630000000006135, + "source_fingerprints": { + "bench_sha256": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "capture_utility_sha256": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "case_validator_sha256": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "controller_sha256": "4b0c7f3e5f069111e7282b85f073ddc2bcd3efba8aaf47d228aad9a3213edd7f", + "implementation_sha256": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2", + "measurement_sha256": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "model_sha256": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "mujoco_version": "3.15.0", + "protocol_sha256": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "state_spec": 16383, + "vision_sha256": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961" + }, + "source_kind": "mujoco_simulation", + "state": "ready", + "unresolved_effect": null, + "version": 1 + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "epoch": 3, + "expires_in_s": 0, + "lease_id": "64fd21cec5f8404abdfe09dbbecfe4f8" + }, + "state": "ready", + "unresolved_commands": [] + }, + "client": { + "commands": { + "command-009165e2aa1d4b6d8c000766c3967914": { + "operation": "propose", + "request_sha256": "1206f95b1d06e80eae8ca4baf0ce26951be111339dc3e2f4d9ebfa1dbe410eab", + "send_count": 1, + "state": "completed" + }, + "command-015d5d03d0624f14ba43d889871c45ee": { + "operation": "propose", + "request_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "send_count": 1, + "state": "completed" + }, + "command-01793f114b2c4d3a8f23b17ebc2b8f1f": { + "operation": "propose", + "request_sha256": "6bf2d4795f61435907607137db5beb6f2607768011e90fb1b8b00082d8c62274", + "send_count": 1, + "state": "completed" + }, + "command-07d3f286ef6846ba90c196ad79a88bff": { + "operation": "dispatch", + "request_sha256": "956cc6d0e3e864da997877f551aa587cb8c18ef23887fa852f68d322858a1934", + "send_count": 1, + "state": "completed" + }, + "command-097e6bec1ce24ef2bc8f37c40f80d98f": { + "operation": "dispatch", + "request_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "send_count": 1, + "state": "completed" + }, + "command-0c04ca9d775443a9b69a78f25f183e7d": { + "operation": "dispatch", + "request_sha256": "e9dce120ea68457626fe8f895b8003547049335bf71398de3a7d45d50475ff93", + "send_count": 1, + "state": "completed" + }, + "command-0d1afb96c11546759d80d26095199c48": { + "operation": "observe", + "request_sha256": "df0a0ddfda89951587e1c2bbae4062b35d48d528b9ff565aad1ae070312bdae2", + "send_count": 1, + "state": "completed" + }, + "command-0d48c82d84d340c6b1d28b76f670dd29": { + "operation": "propose", + "request_sha256": "4335b774435a6b013d28c8327d5fc7e208576eed4d6796b3b671fd41dbf58ba3", + "send_count": 1, + "state": "completed" + }, + "command-0e35d66b16a64168b11d255a1d871e71": { + "operation": "observe", + "request_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "send_count": 1, + "state": "completed" + }, + "command-0ee1728417374a16938ab09aa59e64c0": { + "operation": "propose", + "request_sha256": "32bfef5a823099dab6fc5291582f6faaa0ac10a840ac9e94c38946dab92ae0ab", + "send_count": 1, + "state": "completed" + }, + "command-12c51cb647bc4d01badb3bebf7cb0446": { + "operation": "observe", + "request_sha256": "45dee3c4b5394ccc300f72ba73630f476ddcd992912bb5a1da838e264bf53343", + "send_count": 1, + "state": "completed" + }, + "command-16ac2d834cc949e980dbdc7b3bf60af7": { + "operation": "propose", + "request_sha256": "e750a765511c25e30bf1a54400fae352577c98a13af91dc2bbded25a2b60e0b6", + "send_count": 1, + "state": "completed" + }, + "command-16c006b2256a4000b45fc63cd2d334ff": { + "operation": "dispatch", + "request_sha256": "8f291e7bbe50782c675defef8bc2c5636c22ce4c9e383802274acca154c03a31", + "send_count": 1, + "state": "completed" + }, + "command-1936c404000d4b55a094c49a2fd9eeb3": { + "operation": "observe", + "request_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "send_count": 1, + "state": "completed" + }, + "command-1af4c5bd1341495093201a5821d1dec9": { + "operation": "observe", + "request_sha256": "217b8ec12db8b35fbe968b343144566338207ce3b7d07e9ea8ad8fbf766845a9", + "send_count": 1, + "state": "completed" + }, + "command-1e2386315d6c4c7692405dac7dfa7bd1": { + "operation": "propose", + "request_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "send_count": 1, + "state": "completed" + }, + "command-210150e3ac9f45adae24e89f22a66286": { + "operation": "dispatch", + "request_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "send_count": 1, + "state": "completed" + }, + "command-24230a0f96bc4e84a84f720b1c974054": { + "operation": "dispatch", + "request_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "send_count": 1, + "state": "completed" + }, + "command-248b9798493e4a43a51259929e135e29": { + "operation": "observe", + "request_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "send_count": 1, + "state": "completed" + }, + "command-2a6e74bdbe424b3caa700dc047365a1c": { + "operation": "dispatch", + "request_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "send_count": 1, + "state": "completed" + }, + "command-2d0b436b61124502a9d8558816bda68d": { + "operation": "observe", + "request_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "send_count": 1, + "state": "completed" + }, + "command-3083d955014b4e5282809a47046c53db": { + "operation": "propose", + "request_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "send_count": 1, + "state": "completed" + }, + "command-36da4220dfcd4e85b9a4cee555ccd7aa": { + "operation": "start_job", + "request_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "send_count": 1, + "state": "completed" + }, + "command-382a79f756314a4ea88e1836344549d2": { + "operation": "dispatch", + "request_sha256": "3dda2ab314e9003e2f4f2126c449a2e634fdb05cc99050e52a101481650ae9a2", + "send_count": 1, + "state": "completed" + }, + "command-38a300b989a649fd9ee243704cdc65a9": { + "operation": "observe", + "request_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "send_count": 1, + "state": "completed" + }, + "command-38b6786c82b04b3d92ecde7200a19450": { + "operation": "propose", + "request_sha256": "0eedff7111f86a7c96f02b76328e26f9252832fcd0b32fdcfab09fd639cf62ac", + "send_count": 1, + "state": "completed" + }, + "command-40c6a8a1f1a04ea7bec902767b57387a": { + "operation": "observe", + "request_sha256": "7bb39849a52b98bf039deebb6371d28ece55c04faed9809c9866c6babae4ef26", + "send_count": 1, + "state": "completed" + }, + "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e": { + "operation": "propose", + "request_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "send_count": 1, + "state": "completed" + }, + "command-4c3705f8d7cc48a4b45029d078357250": { + "operation": "observe", + "request_sha256": "6670065bd1fd29ab4a84a51cb0a31a497929cb54d24b94909462d946768c2d6d", + "send_count": 1, + "state": "completed" + }, + "command-51f66bfa90ab421bb24242caa6d054fd": { + "operation": "observe", + "request_sha256": "327a292c61ede952cf37c3157827e4fa1d919d5e90b244f18010ef464b558f42", + "send_count": 1, + "state": "completed" + }, + "command-553be6e82b274944b5e6ea9e2cd3902e": { + "operation": "propose", + "request_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "send_count": 1, + "state": "completed" + }, + "command-5ca0b6a7e99f4263b2239a95b02ca7e7": { + "operation": "propose", + "request_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "send_count": 1, + "state": "completed" + }, + "command-5ced22700795486fa68a50fcda4767b8": { + "operation": "propose", + "request_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "send_count": 1, + "state": "completed" + }, + "command-5e27abcb6c914c5ca0ae77ddee974bbc": { + "operation": "dispatch", + "request_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "send_count": 1, + "state": "completed" + }, + "command-604436650640419d9ce6c551ac3e3407": { + "operation": "observe", + "request_sha256": "98c01becbe239e62be2fe587554aa3ea29f2273a052c5359e1e5a85fb476362f", + "send_count": 1, + "state": "completed" + }, + "command-69a56ced2e8c49ce9e9f214c57023381": { + "operation": "propose", + "request_sha256": "9cfc65fc7994db66a7ebebdd994fba2dad0a4d54475862539cd87586f2779dfa", + "send_count": 1, + "state": "completed" + }, + "command-6e6b99559d5749eab04cbf8f42b46ae2": { + "operation": "observe", + "request_sha256": "3a2970680dd2675a75e8bbf91affd223084373d5789d6b873b4fe8bdab7dd5aa", + "send_count": 1, + "state": "completed" + }, + "command-74dc57912fda4c3e96f6845cdffbb045": { + "operation": "observe", + "request_sha256": "aed94b91da1d8ac4b41ac1ad21b488bc9c43e6db4066e352dc6bbacceee6b907", + "send_count": 1, + "state": "completed" + }, + "command-7661dbdae1c34bd797ca17b3b5321a5e": { + "operation": "propose", + "request_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "send_count": 1, + "state": "completed" + }, + "command-7b94f03fcd7449ec97ca592813c20693": { + "operation": "observe", + "request_sha256": "e9adecd7c66f60c892d0bf60a14266e115b7948b8cbac2a26aff6e4fb526e615", + "send_count": 1, + "state": "completed" + }, + "command-7ce7b5eb9b5b4b1d953e624d2312327b": { + "operation": "dispatch", + "request_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "send_count": 1, + "state": "completed" + }, + "command-84c016d3c2564ecea6025edaea7d23e7": { + "operation": "propose", + "request_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "send_count": 1, + "state": "completed" + }, + "command-8c948c9df6074ee5969ef8b19a3eaddb": { + "operation": "propose", + "request_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "send_count": 1, + "state": "completed" + }, + "command-90b2509eee524747a3397627f2c51643": { + "operation": "propose", + "request_sha256": "ba39f43b6c1947a04304c95045272d8cff2c0f70c14831e48ef8b77ce03d585b", + "send_count": 1, + "state": "completed" + }, + "command-933c11248d6a48079ead24676a2df7fb": { + "operation": "observe", + "request_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "send_count": 1, + "state": "completed" + }, + "command-93b6bfdc895349d2a07cfed89231411d": { + "operation": "propose", + "request_sha256": "a1e20276d19935994fb87711dc6ad10ef2a0f5423e274a3c9ca189348e861573", + "send_count": 1, + "state": "completed" + }, + "command-95a7bae7c4f8479b81bae1ea55e7597f": { + "operation": "observe", + "request_sha256": "acc6a031281992af1b58960f9038dc6cab3e67e435b497bfdaaea926b4c47620", + "send_count": 1, + "state": "completed" + }, + "command-96ff5828100a40b9945b5960aeb7e5dd": { + "operation": "dispatch", + "request_sha256": "80aa2ffe12f32c32c279d78eb3775b4311d8b83677f816849f91ae636fec87d0", + "send_count": 1, + "state": "completed" + }, + "command-9841ade4a0ea46a2b43353bf2b951a7c": { + "operation": "propose", + "request_sha256": "7b732e90c18d4e428c43c95403e2afeca93754f75cc963d94ba01cdec8224561", + "send_count": 1, + "state": "completed" + }, + "command-9dd2a0c9603f4667956ec976e822b74a": { + "operation": "observe", + "request_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "send_count": 1, + "state": "completed" + }, + "command-9f8d9402f1d74504b579f39b9c793576": { + "operation": "propose", + "request_sha256": "1c6f1885adbe13c8b3ab749812e10139310fcbadf478b3debaa8bc025bda45b0", + "send_count": 1, + "state": "completed" + }, + "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee": { + "operation": "dispatch", + "request_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "send_count": 1, + "state": "completed" + }, + "command-ab4679dd21e14ce08f70b1ef63bd5868": { + "operation": "observe", + "request_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "send_count": 1, + "state": "completed" + }, + "command-acd7ec8de3eb4a548e2e070898a0318f": { + "operation": "dispatch", + "request_sha256": "e963b00f5df17ccc10c5a050cac94cd1515f23b7c9e38ebfd7b97285aa6fb0cf", + "send_count": 1, + "state": "completed" + }, + "command-ad464b677e044b43ae8f9dd725331fce": { + "operation": "observe", + "request_sha256": "2a139a6cd850620070ff777a29a2acbfb326b69d260036bb5847aefd6a5c4c18", + "send_count": 1, + "state": "completed" + }, + "command-b17181d5918a492a916e0e3c5a2f3a11": { + "operation": "propose", + "request_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "send_count": 1, + "state": "completed" + }, + "command-b182534626d34766a52d5e11d48239e7": { + "operation": "observe", + "request_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "send_count": 1, + "state": "completed" + }, + "command-bdc662ab7f4645d79c02f84921f3c9f6": { + "operation": "dispatch", + "request_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "send_count": 1, + "state": "completed" + }, + "command-c5861bc07c0d4b6a992850fe1ab3c769": { + "operation": "dispatch", + "request_sha256": "2747a5c1d597b72581a2ffef5303fd6f137acdae3c6243750c87309aedb74de2", + "send_count": 1, + "state": "completed" + }, + "command-c80c18cc647b45fe8e9faf2fa890283c": { + "operation": "dispatch", + "request_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "send_count": 1, + "state": "completed" + }, + "command-c8be0caf76504410a68a595296d56bd3": { + "operation": "observe", + "request_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "send_count": 1, + "state": "completed" + }, + "command-cd90dd204ff74139880f3846418a41b7": { + "operation": "dispatch", + "request_sha256": "dc58e74652094316cd08bb4ed5a2b9e9ba9a1793326b9918539c6365f1537e3b", + "send_count": 1, + "state": "completed" + }, + "command-cf898dd6e56a4d8bb0a5894371f9ba81": { + "operation": "dispatch", + "request_sha256": "bd591cd9646fb80860bc791d8b9d02fd3695a496ead456ceb7f903185883edc9", + "send_count": 1, + "state": "completed" + }, + "command-cf90ff6221c640f9ac99b5ceb8a91754": { + "operation": "dispatch", + "request_sha256": "9109f545b48718b5291c081e098f1f815b521fae16217993ee9985e898ffc043", + "send_count": 1, + "state": "completed" + }, + "command-cfead433cabe4303b15fd2ca83ee8899": { + "operation": "propose", + "request_sha256": "504d2c9975111bb32e985cc71034239f5d62d10929273eb2ec94d6b5b59534dd", + "send_count": 1, + "state": "completed" + }, + "command-d3e05b3b869542a085afeba4414811ba": { + "operation": "dispatch", + "request_sha256": "cb0758e27127d99fbc0a75c6dc7cfc2d61c2bdf92925e51129e916f3637fee40", + "send_count": 1, + "state": "completed" + }, + "command-d9922eefd392420b96bf0a62d7eea982": { + "operation": "dispatch", + "request_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "send_count": 1, + "state": "completed" + }, + "command-df199bef22b442168d0a42242c00c5c4": { + "operation": "observe", + "request_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "send_count": 1, + "state": "completed" + }, + "command-e19f5d23dd944e40b806e930f7d44b35": { + "operation": "dispatch", + "request_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "send_count": 1, + "state": "completed" + }, + "command-e1af2fbe5a254692bf212769ddfea045": { + "operation": "observe", + "request_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "send_count": 1, + "state": "completed" + }, + "command-e7a47417ff124ebc915329783ad7c5bb": { + "operation": "dispatch", + "request_sha256": "07cb08a35c3378b556b2d9fb19d575ea53e577e4dca56624bf3e5e49e868fe68", + "send_count": 1, + "state": "completed" + }, + "command-e897a3cba83b4d83b460a556be5e459b": { + "operation": "observe", + "request_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "send_count": 1, + "state": "completed" + }, + "command-eb16ab3154eb40f78f021869ebb485b3": { + "operation": "propose", + "request_sha256": "bbd21b24923ad60e0ba472125e2544efe9ab758d7887e2444bd293bd0f5e25b6", + "send_count": 1, + "state": "completed" + }, + "command-ede04073e3654f4fa17f0a0056db9004": { + "operation": "dispatch", + "request_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "send_count": 1, + "state": "completed" + }, + "command-ef48ba673c2b4ae08b09fbbc4c56fca8": { + "operation": "start_job", + "request_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "send_count": 1, + "state": "completed" + }, + "command-f008b852a29348218bc57e931887b963": { + "operation": "dispatch", + "request_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "send_count": 1, + "state": "completed" + }, + "command-f0d26ac5d6914f1bb038a57ee515b7f0": { + "operation": "observe", + "request_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "send_count": 1, + "state": "completed" + }, + "command-f4c202be91064d3e8f4c929f1175450e": { + "operation": "propose", + "request_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "send_count": 1, + "state": "completed" + }, + "command-f7b8d8e5cddb47ecbac988938ccee2a0": { + "operation": "observe", + "request_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "send_count": 1, + "state": "completed" + }, + "command-fb77cbd4d30f4827bdef78ff67cabe2b": { + "operation": "dispatch", + "request_sha256": "286b425e934eb0fa16526805b7ef5fe3431d0d172fdcf02fd4cc9ccb9d89ef5f", + "send_count": 1, + "state": "completed" + }, + "command-fca0d0500040494396aabdb153197133": { + "operation": "observe", + "request_sha256": "36f0a50bf16631b346b7669d99fb49c44bf15ade5d62d3a84e9bfaf016fd1d1f", + "send_count": 1, + "state": "completed" + }, + "command-fda8dcacccc940c493511804050cc0ec": { + "operation": "propose", + "request_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "send_count": 1, + "state": "completed" + }, + "command-fe049e3ca1ce489e9e7cc0e25188a245": { + "operation": "observe", + "request_sha256": "20563be149da9ea5513d1f967925a42fb19aef1a112f38923e15d34a79907c68", + "send_count": 1, + "state": "completed" + } + }, + "identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "lease": { + "client_id": "client-6c734bbe900342329bb34c0d4dcc485b", + "duration_s": 30.0, + "epoch": 3, + "lease_id": "64fd21cec5f8404abdfe09dbbecfe4f8" + }, + "pending_command_ids": [] + }, + "host": { + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "body_identity": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "protocol": "footwork.body/1", + "simulation": true, + "source_sha256": "10833c9f931a9d762c93d13b6061795fa5c3b4be9ac6151eaf38427b47f04787" + }, + "frame": { + "generation": 20, + "path": "/Users/thomasmain/projects/footwork/tools/physical-workcell/output/sim11-browser-restarts-v2/operator/client/images/challenge-021548ca7f144b57a98d5dfa7df72f15-118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa.png", + "sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "runner_status": "running", + "sequence": 227, + "stage": null, + "usage": { + "calls": 29, + "input_tokens": 343310, + "output_tokens": 3963, + "reported_calls": 26 + } + }, + "posts": [ + { + "command_id": "command-36da4220dfcd4e85b9a4cee555ccd7aa", + "payload_sha256": "7ea11721570fb77e340b4f2e0d66e64ab5b136d5478b26c44a967be1d86256eb", + "sequence": 8 + }, + { + "command_id": "command-e897a3cba83b4d83b460a556be5e459b", + "payload_sha256": "0b35d9142df3fc25df922ec8c0812e7ad7460346f7389fb80bace9904ec147bb", + "sequence": 16 + }, + { + "command_id": "command-933c11248d6a48079ead24676a2df7fb", + "payload_sha256": "8e249e904c29f91de3ef73aa9fa15d6517e2d6983edfdc0a097e8eda8fc67f59", + "sequence": 31 + }, + { + "command_id": "command-1e2386315d6c4c7692405dac7dfa7bd1", + "payload_sha256": "29daaf4dd4f38db1720d0a28f833caa5084b170118c8cd781a1aba9ce7d1746b", + "sequence": 43 + }, + { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "payload_sha256": "4725aa167fe57d46a1c6674fa63a1f97413421451a44826ee954952e11c63158", + "sequence": 51 + }, + { + "command_id": "command-0e35d66b16a64168b11d255a1d871e71", + "payload_sha256": "ac2992d20493bd10b8fbebea63151c6d075a4704088b321b909ce4a44388b331", + "sequence": 67 + }, + { + "command_id": "command-fda8dcacccc940c493511804050cc0ec", + "payload_sha256": "db632f120ae418bddfaca319ffc50e98d0d206def22a9d0c3655c39423aa1717", + "sequence": 79 + }, + { + "command_id": "command-5e27abcb6c914c5ca0ae77ddee974bbc", + "payload_sha256": "b150863371975909061c5c284297745de818973ef9bb4483fe276d83230c4974", + "sequence": 87 + }, + { + "command_id": "command-c8be0caf76504410a68a595296d56bd3", + "payload_sha256": "d5867b3b7dab8738fe2bcc2e878c120a9651fe09dc13d102dacf63d5e6e3b564", + "sequence": 95 + }, + { + "command_id": "command-f4c202be91064d3e8f4c929f1175450e", + "payload_sha256": "425fe924fe46c38097155ffde315f426490bb4d8ee43f16cd7454fe52af7bf3d", + "sequence": 107 + }, + { + "command_id": "command-e19f5d23dd944e40b806e930f7d44b35", + "payload_sha256": "788dac06d5863a4a6363c4e9b6c16f38bea0eb4bdea7658fd4f83f732a59583a", + "sequence": 115 + }, + { + "command_id": "command-1936c404000d4b55a094c49a2fd9eeb3", + "payload_sha256": "7eda001e48f71d8279c5770cf2633237f092c1b0bb35aa15e19f3667c6116eb2", + "sequence": 123 + }, + { + "command_id": "command-8c948c9df6074ee5969ef8b19a3eaddb", + "payload_sha256": "658c7671a0d062cc75c20b2112e39826075affea6d5d60a183135b399050218d", + "sequence": 135 + }, + { + "command_id": "command-bdc662ab7f4645d79c02f84921f3c9f6", + "payload_sha256": "0ccb54ee47fa858a87eb10bcfb069c345b29db8fad61669c1e58751549e43dc0", + "sequence": 143 + }, + { + "command_id": "command-e1af2fbe5a254692bf212769ddfea045", + "payload_sha256": "9efbed5e6627632a03212e000b633a1ab1e8692aaa81c4340c7b76536a2af8a3", + "sequence": 151 + }, + { + "command_id": "command-b17181d5918a492a916e0e3c5a2f3a11", + "payload_sha256": "56ce449d1a0416dc5e7f1830c50887e493606e6f935dd72cfb0fff054a2b7042", + "sequence": 163 + }, + { + "command_id": "command-2a6e74bdbe424b3caa700dc047365a1c", + "payload_sha256": "c8c822c9fba17ecf981cb86e65485f2678e917c7266fda736ef3d0b0635b5f9c", + "sequence": 171 + }, + { + "command_id": "command-df199bef22b442168d0a42242c00c5c4", + "payload_sha256": "14d1cb1ec52d8e8d520981b293408d72ff6fb592324b6a3da695f0254f04c636", + "sequence": 179 + }, + { + "command_id": "command-5ca0b6a7e99f4263b2239a95b02ca7e7", + "payload_sha256": "8110ae8d9f624a89419f3229a1f13a47f9f17249df601237cfd4cae5224a07ee", + "sequence": 191 + }, + { + "command_id": "command-f008b852a29348218bc57e931887b963", + "payload_sha256": "46b71d8afc3b366718a29d00919e73d3d7bc8199558f2c6345c38d602084c454", + "sequence": 199 + }, + { + "command_id": "command-2d0b436b61124502a9d8558816bda68d", + "payload_sha256": "d565247b59ff983b0e8056739a7312553ff11a86e0c2ade2e57ae03e2794e389", + "sequence": 207 + }, + { + "command_id": "command-40dfcee9c7d24d5b88eacf3ad4ef9a4e", + "payload_sha256": "22fd8eae4d69f51771a01dd11df73ca52f7ab75a37cf657e5ce13d4231112a6c", + "sequence": 219 + }, + { + "command_id": "command-aa19aa0c97ee4d8bb8ab6ab1c175e8ee", + "payload_sha256": "21d9f088823463acf90956ee026ac32de41b84cd1f3bfc9f7473836ec6d08a97", + "sequence": 227 + }, + { + "command_id": "command-b182534626d34766a52d5e11d48239e7", + "payload_sha256": "5ad3e108bbf0076da2cd4442bc2cb513b7a3b03a3eb9e6ebcb9365da6617d48a", + "sequence": 235 + }, + { + "command_id": "command-84c016d3c2564ecea6025edaea7d23e7", + "payload_sha256": "e7af49a829ed12157954efd8c9a4d13b89e2cd991bd0254fa3377a8ea20b2aff", + "sequence": 247 + }, + { + "command_id": "command-ede04073e3654f4fa17f0a0056db9004", + "payload_sha256": "8d4f2e531917f1b2ebb0b3ac9e9c7bc8640874a5709a1dbf0cf755db56cd6a28", + "sequence": 255 + }, + { + "command_id": "command-f0d26ac5d6914f1bb038a57ee515b7f0", + "payload_sha256": "6f52c77b4617240c34fa8b76ec854925153b81d241bdf354519ee685ea333e05", + "sequence": 263 + }, + { + "command_id": "command-5ced22700795486fa68a50fcda4767b8", + "payload_sha256": "9ee7328d2661197fbca4b2fb342317a403245d5db32515b19b398e1b10ba27e9", + "sequence": 275 + }, + { + "command_id": "command-7ce7b5eb9b5b4b1d953e624d2312327b", + "payload_sha256": "b6b3bf3339bf5881e47f71f7eb5f514f3e287587f62fe505bbd2d9950828acc4", + "sequence": 283 + }, + { + "command_id": "command-9dd2a0c9603f4667956ec976e822b74a", + "payload_sha256": "3652535d33974b64aba2aba0d5df03b66c1c9075c9069c7b673874f332e0bbcb", + "sequence": 291 + }, + { + "command_id": "command-3083d955014b4e5282809a47046c53db", + "payload_sha256": "f4ffd63b2254e4e91e23fdfb9eb6e1ac0e5f0701d7ba7617f575083cbc2fffec", + "sequence": 303 + }, + { + "command_id": "command-097e6bec1ce24ef2bc8f37c40f80d98f", + "payload_sha256": "3df0298afc1f4e44d60b2b3f7d3cf13c0277600682f0f0d6359cf0ae1cfb9488", + "sequence": 311 + }, + { + "command_id": "command-38a300b989a649fd9ee243704cdc65a9", + "payload_sha256": "97c7881b3b3eac3f20e327de6d1e0a23e94b8b06415bc43b0b2d00a298a1778f", + "sequence": 319 + }, + { + "command_id": "command-553be6e82b274944b5e6ea9e2cd3902e", + "payload_sha256": "1e1cc0fede8a9f595d8146da49dce6138da32f9261916b1613f46392318f3a1e", + "sequence": 331 + }, + { + "command_id": "command-d9922eefd392420b96bf0a62d7eea982", + "payload_sha256": "7440f248c466bc7ce206c2354a5852ad1cdf7f9372457b367879d24984766480", + "sequence": 339 + }, + { + "command_id": "command-f7b8d8e5cddb47ecbac988938ccee2a0", + "payload_sha256": "bf279c09f19252bbda950361945a6b143fb96c87ce278b567860295e4a263f7c", + "sequence": 347 + }, + { + "command_id": "command-7661dbdae1c34bd797ca17b3b5321a5e", + "payload_sha256": "c1470e90804d516b6bd51b0dc825b1d06f699223e0b0d117e66da0ce8d97f463", + "sequence": 359 + }, + { + "command_id": "command-c80c18cc647b45fe8e9faf2fa890283c", + "payload_sha256": "0bf907eb26ef2015935816cf5887a6e465a729aecc183a4fa10cca5ad47334cd", + "sequence": 367 + }, + { + "command_id": "command-248b9798493e4a43a51259929e135e29", + "payload_sha256": "bed21822b73961a780adf48cc0912e8038574b180a5f0ad976f2a01ebe37703a", + "sequence": 375 + }, + { + "command_id": "command-015d5d03d0624f14ba43d889871c45ee", + "payload_sha256": "072a32a019c848228f49953e695f6a220d0db7743fdda906011df8ed8f71c3a0", + "sequence": 387 + }, + { + "command_id": "command-210150e3ac9f45adae24e89f22a66286", + "payload_sha256": "674d3fa1ea7146ddb31fb4e1dccd69d009a30437e56cafc56ae48019202f991d", + "sequence": 395 + }, + { + "command_id": "command-ef48ba673c2b4ae08b09fbbc4c56fca8", + "payload_sha256": "6a500fe4198f0aadde08c1859e8020d7833ca1fa0529fbdbac4c8f17aa957a60", + "sequence": 403 + }, + { + "command_id": "command-ab4679dd21e14ce08f70b1ef63bd5868", + "payload_sha256": "c7cfb9cf1ffa949ed60ad3b3aef61884cfd959107a56129d23c7758714a971d8", + "sequence": 411 + }, + { + "command_id": "command-40c6a8a1f1a04ea7bec902767b57387a", + "payload_sha256": "7bb39849a52b98bf039deebb6371d28ece55c04faed9809c9866c6babae4ef26", + "sequence": 480 + }, + { + "command_id": "command-9f8d9402f1d74504b579f39b9c793576", + "payload_sha256": "1c6f1885adbe13c8b3ab749812e10139310fcbadf478b3debaa8bc025bda45b0", + "sequence": 492 + }, + { + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "payload_sha256": "bd591cd9646fb80860bc791d8b9d02fd3695a496ead456ceb7f903185883edc9", + "sequence": 500 + }, + { + "command_id": "command-604436650640419d9ce6c551ac3e3407", + "payload_sha256": "98c01becbe239e62be2fe587554aa3ea29f2273a052c5359e1e5a85fb476362f", + "sequence": 550 + }, + { + "command_id": "command-01793f114b2c4d3a8f23b17ebc2b8f1f", + "payload_sha256": "6bf2d4795f61435907607137db5beb6f2607768011e90fb1b8b00082d8c62274", + "sequence": 562 + }, + { + "command_id": "command-d3e05b3b869542a085afeba4414811ba", + "payload_sha256": "cb0758e27127d99fbc0a75c6dc7cfc2d61c2bdf92925e51129e916f3637fee40", + "sequence": 570 + }, + { + "command_id": "command-6e6b99559d5749eab04cbf8f42b46ae2", + "payload_sha256": "3a2970680dd2675a75e8bbf91affd223084373d5789d6b873b4fe8bdab7dd5aa", + "sequence": 578 + }, + { + "command_id": "command-90b2509eee524747a3397627f2c51643", + "payload_sha256": "ba39f43b6c1947a04304c95045272d8cff2c0f70c14831e48ef8b77ce03d585b", + "sequence": 590 + }, + { + "command_id": "command-0c04ca9d775443a9b69a78f25f183e7d", + "payload_sha256": "e9dce120ea68457626fe8f895b8003547049335bf71398de3a7d45d50475ff93", + "sequence": 598 + }, + { + "command_id": "command-7b94f03fcd7449ec97ca592813c20693", + "payload_sha256": "e9adecd7c66f60c892d0bf60a14266e115b7948b8cbac2a26aff6e4fb526e615", + "sequence": 606 + }, + { + "command_id": "command-eb16ab3154eb40f78f021869ebb485b3", + "payload_sha256": "bbd21b24923ad60e0ba472125e2544efe9ab758d7887e2444bd293bd0f5e25b6", + "sequence": 618 + }, + { + "command_id": "command-acd7ec8de3eb4a548e2e070898a0318f", + "payload_sha256": "e963b00f5df17ccc10c5a050cac94cd1515f23b7c9e38ebfd7b97285aa6fb0cf", + "sequence": 626 + }, + { + "command_id": "command-4c3705f8d7cc48a4b45029d078357250", + "payload_sha256": "6670065bd1fd29ab4a84a51cb0a31a497929cb54d24b94909462d946768c2d6d", + "sequence": 634 + }, + { + "command_id": "command-0ee1728417374a16938ab09aa59e64c0", + "payload_sha256": "32bfef5a823099dab6fc5291582f6faaa0ac10a840ac9e94c38946dab92ae0ab", + "sequence": 646 + }, + { + "command_id": "command-07d3f286ef6846ba90c196ad79a88bff", + "payload_sha256": "956cc6d0e3e864da997877f551aa587cb8c18ef23887fa852f68d322858a1934", + "sequence": 654 + }, + { + "command_id": "command-74dc57912fda4c3e96f6845cdffbb045", + "payload_sha256": "aed94b91da1d8ac4b41ac1ad21b488bc9c43e6db4066e352dc6bbacceee6b907", + "sequence": 662 + }, + { + "command_id": "command-009165e2aa1d4b6d8c000766c3967914", + "payload_sha256": "1206f95b1d06e80eae8ca4baf0ce26951be111339dc3e2f4d9ebfa1dbe410eab", + "sequence": 674 + }, + { + "command_id": "command-382a79f756314a4ea88e1836344549d2", + "payload_sha256": "3dda2ab314e9003e2f4f2126c449a2e634fdb05cc99050e52a101481650ae9a2", + "sequence": 682 + }, + { + "command_id": "command-0d1afb96c11546759d80d26095199c48", + "payload_sha256": "df0a0ddfda89951587e1c2bbae4062b35d48d528b9ff565aad1ae070312bdae2", + "sequence": 690 + }, + { + "command_id": "command-0d48c82d84d340c6b1d28b76f670dd29", + "payload_sha256": "4335b774435a6b013d28c8327d5fc7e208576eed4d6796b3b671fd41dbf58ba3", + "sequence": 702 + }, + { + "command_id": "command-16c006b2256a4000b45fc63cd2d334ff", + "payload_sha256": "8f291e7bbe50782c675defef8bc2c5636c22ce4c9e383802274acca154c03a31", + "sequence": 710 + }, + { + "command_id": "command-ad464b677e044b43ae8f9dd725331fce", + "payload_sha256": "2a139a6cd850620070ff777a29a2acbfb326b69d260036bb5847aefd6a5c4c18", + "sequence": 718 + }, + { + "command_id": "command-16ac2d834cc949e980dbdc7b3bf60af7", + "payload_sha256": "e750a765511c25e30bf1a54400fae352577c98a13af91dc2bbded25a2b60e0b6", + "sequence": 730 + }, + { + "command_id": "command-fb77cbd4d30f4827bdef78ff67cabe2b", + "payload_sha256": "286b425e934eb0fa16526805b7ef5fe3431d0d172fdcf02fd4cc9ccb9d89ef5f", + "sequence": 738 + }, + { + "command_id": "command-1af4c5bd1341495093201a5821d1dec9", + "payload_sha256": "217b8ec12db8b35fbe968b343144566338207ce3b7d07e9ea8ad8fbf766845a9", + "sequence": 746 + }, + { + "command_id": "command-93b6bfdc895349d2a07cfed89231411d", + "payload_sha256": "a1e20276d19935994fb87711dc6ad10ef2a0f5423e274a3c9ca189348e861573", + "sequence": 758 + }, + { + "command_id": "command-96ff5828100a40b9945b5960aeb7e5dd", + "payload_sha256": "80aa2ffe12f32c32c279d78eb3775b4311d8b83677f816849f91ae636fec87d0", + "sequence": 766 + }, + { + "command_id": "command-fca0d0500040494396aabdb153197133", + "payload_sha256": "36f0a50bf16631b346b7669d99fb49c44bf15ade5d62d3a84e9bfaf016fd1d1f", + "sequence": 774 + }, + { + "command_id": "command-69a56ced2e8c49ce9e9f214c57023381", + "payload_sha256": "9cfc65fc7994db66a7ebebdd994fba2dad0a4d54475862539cd87586f2779dfa", + "sequence": 786 + }, + { + "command_id": "command-cf90ff6221c640f9ac99b5ceb8a91754", + "payload_sha256": "9109f545b48718b5291c081e098f1f815b521fae16217993ee9985e898ffc043", + "sequence": 794 + }, + { + "command_id": "command-51f66bfa90ab421bb24242caa6d054fd", + "payload_sha256": "327a292c61ede952cf37c3157827e4fa1d919d5e90b244f18010ef464b558f42", + "sequence": 802 + }, + { + "command_id": "command-38b6786c82b04b3d92ecde7200a19450", + "payload_sha256": "0eedff7111f86a7c96f02b76328e26f9252832fcd0b32fdcfab09fd639cf62ac", + "sequence": 814 + }, + { + "command_id": "command-c5861bc07c0d4b6a992850fe1ab3c769", + "payload_sha256": "2747a5c1d597b72581a2ffef5303fd6f137acdae3c6243750c87309aedb74de2", + "sequence": 822 + }, + { + "command_id": "command-fe049e3ca1ce489e9e7cc0e25188a245", + "payload_sha256": "20563be149da9ea5513d1f967925a42fb19aef1a112f38923e15d34a79907c68", + "sequence": 830 + }, + { + "command_id": "command-cfead433cabe4303b15fd2ca83ee8899", + "payload_sha256": "504d2c9975111bb32e985cc71034239f5d62d10929273eb2ec94d6b5b59534dd", + "sequence": 842 + }, + { + "command_id": "command-cd90dd204ff74139880f3846418a41b7", + "payload_sha256": "dc58e74652094316cd08bb4ed5a2b9e9ba9a1793326b9918539c6365f1537e3b", + "sequence": 850 + }, + { + "command_id": "command-95a7bae7c4f8479b81bae1ea55e7597f", + "payload_sha256": "acc6a031281992af1b58960f9038dc6cab3e67e435b497bfdaaea926b4c47620", + "sequence": 858 + }, + { + "command_id": "command-12c51cb647bc4d01badb3bebf7cb0446", + "payload_sha256": "45dee3c4b5394ccc300f72ba73630f476ddcd992912bb5a1da838e264bf53343", + "sequence": 871 + }, + { + "command_id": "command-9841ade4a0ea46a2b43353bf2b951a7c", + "payload_sha256": "7b732e90c18d4e428c43c95403e2afeca93754f75cc963d94ba01cdec8224561", + "sequence": 883 + }, + { + "command_id": "command-e7a47417ff124ebc915329783ad7c5bb", + "payload_sha256": "07cb08a35c3378b556b2d9fb19d575ea53e577e4dca56624bf3e5e49e868fe68", + "sequence": 891 + } + ], + "processes": { + "body": 74921, + "workbench": 39967 + }, + "public": { + "active_job_id": null, + "activity": [ + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 204, + "time": 1791829189.127323 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-fe049e3ca1ce489e9e7cc0e25188a245", + "seq": 205, + "time": 1791829189.480195 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "13", + "seq": 206, + "time": 1791829189.4844081 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 207, + "time": 1791829196.4102259 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "propose: command-cfead433cabe4303b15fd2ca83ee8899", + "seq": 208, + "time": 1791829196.602696 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 209, + "time": 1791829196.697315 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "dispatch: command-cd90dd204ff74139880f3846418a41b7", + "seq": 210, + "time": 1791829197.11283 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 211, + "time": 1791829197.23605 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-95a7bae7c4f8479b81bae1ea55e7597f", + "seq": 212, + "time": 1791829197.9852512 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "14", + "seq": 213, + "time": 1791829197.991858 + }, + { + "intent": "", + "kind": "halt", + "outcome": "timed out", + "seq": 214, + "time": 1791829202.567657 + }, + { + "intent": "", + "kind": "halt", + "outcome": "operator_pause_requested", + "seq": 215, + "time": 1791829202.618878 + }, + { + "intent": "", + "kind": "halt", + "outcome": "timed out", + "seq": 216, + "time": 1791829224.8969312 + }, + { + "intent": "", + "kind": "halt", + "outcome": "timed out", + "seq": 217, + "time": 1791829230.2752252 + }, + { + "intent": "", + "kind": "halt", + "outcome": "timed out", + "seq": 218, + "time": 1791829234.4515479 + }, + { + "intent": "", + "kind": "halt", + "outcome": "timed out", + "seq": 219, + "time": 1791829242.3595982 + }, + { + "intent": "", + "kind": "resume", + "outcome": "", + "seq": 220, + "time": 1791829285.4561422 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "observe", + "seq": 221, + "time": 1791829285.550546 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "observe: command-12c51cb647bc4d01badb3bebf7cb0446", + "seq": 222, + "time": 1791829286.054108 + }, + { + "intent": "Restart inspection 02", + "kind": "inference", + "outcome": "15", + "seq": 223, + "time": 1791829286.056795 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "propose", + "seq": 224, + "time": 1791829296.4025989 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "propose: command-9841ade4a0ea46a2b43353bf2b951a7c", + "seq": 225, + "time": 1791829296.548447 + }, + { + "intent": "", + "kind": "rpc_intent", + "outcome": "dispatch", + "seq": 226, + "time": 1791829296.6237051 + }, + { + "intent": "Restart inspection 02", + "kind": "rpc_applied", + "outcome": "dispatch: command-e7a47417ff124ebc915329783ad7c5bb", + "seq": 227, + "time": 1791829297.049696 + } + ], + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 20, + "image_url": "/api/frame?generation=20&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 15, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "Finish reset with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 20, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "kind": "finish_reset", + "native_time_s": 42.630000000006135, + "operation_id": "agent-7acee30a3cb442d9a3a2b3a9bfec10ed", + "pusher_home_verified": true, + "reset_contact_samples": 3314, + "reset_native_steps": 8140, + "state_sha256": "02c49f9f03f16f8de552b4b4ce57b6d78c85e291fc3dbf1b6acb0ce4b7cd638b" + }, + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "pending_operation": null, + "readiness": { + "ready": true, + "reason": "Resumed; next decision receives a fresh TCP image" + }, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running", + "simulation": true, + "transport_pending": false, + "usage": { + "calls": 29, + "input_tokens": 343310, + "output_tokens": 3963, + "reported_calls": 26, + "token_usage_complete": false + } + }, + "time": 1791829364.5815492 +} diff --git a/jevdual/results/workbench-restarts-20261012/source-freeze.json b/jevdual/results/workbench-restarts-20261012/source-freeze.json new file mode 100644 index 0000000..1c1115e --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/source-freeze.json @@ -0,0 +1,33 @@ +{ + "commit": "d7072107d576d7a09643229ae584a3864d2fe0f6", + "files": { + "tools/camera-capture-experiment/capture.py": "3623fcad2a6ec0135af2452d50403bad7c508f80795c0f98417ccc9446a56f7b", + "tools/physical-workcell/agent_bench.py": "e120dbfc9a96ca07fedb15226f27282265203755866210b86d5f856f6e3f5e07", + "tools/physical-workcell/bench.py": "f35b9fdca2c093d4a93bcdb7797ff252f40e7165494e4ee6a957c6d2db5bd4b2", + "tools/physical-workcell/body_client.py": "40388c238192d923888c9852b363314801dcf65c7266aadc8c377d3810aeb33d", + "tools/physical-workcell/body_network.py": "95fd21597bcdd9e982a698859cd1219ae4522062e332c50fea233629dbfb343c", + "tools/physical-workcell/body_protocol.py": "1d24066768105c28b5cf6f4d46458e4e1b9d86cbde45ed6ce466b30da7ff974f", + "tools/physical-workcell/codex-action.schema.json": "e57197e4e73aa4c78145814a460ca77b9c6dc5e49aaf4c91adc0b521b7c3d833", + "tools/physical-workcell/codex-policy.md": "ce947200b2a652e0766b422f3b404bd5095ceb398569341466b2c5f4dd9647bc", + "tools/physical-workcell/codex_policy.py": "40fe73e01d97ee1e5cfdb813650b11edadbea29534784ab10cc1ef4e7f4d5418", + "tools/physical-workcell/episode_cases.py": "c746a6df8f1f9554633266fcd5363b8001f21eb3d334a5e90ffed4b6ad219029", + "tools/physical-workcell/image_guided.py": "572206e18f900776d951b40802d57112a86b22766eb9f7f83bf7a2410328e961", + "tools/physical-workcell/measurement.py": "21900e9297ac15499642cf106b303836b40bb1b19f128f8008ef400a881f7f9e", + "tools/physical-workcell/network_bench_operator.html": "a4164134b418929476b7e69946b9e42b6804e2836bb57e606630763704d9becb", + "tools/physical-workcell/network_bench_operator.py": "9079729ea4e8b56ccdf2a6d187b0c5d79a23a33688748fe55d92d10922500b40", + "tools/physical-workcell/network_fault_proxy.py": "44b0b52f7c213c0f5f93b8997ba72be4e750bc9f1a4f7f6d7b2a8c4f1233b595", + "tools/physical-workcell/network_operator_runtime.py": "706e0165da8173e501f8d689bcb161f480ae052a283e5e72f01bd26dca1be73b", + "tools/physical-workcell/order_protocol.py": "d5d9824149e3ed68d15ec4b62e36c406cdc64b8df0cc945ece13f34f13449d5e", + "tools/physical-workcell/qualify_bench.py": "524c537c1b310a0f0ae493acee99a069966a5e7c4b0a783daadf347094db58ef", + "tools/physical-workcell/qualify_network_body.py": "2800cf38afd21b86f8b035c46e994b6f96d966db4ccba1bba703e9e1a22f7c2b", + "tools/physical-workcell/qualify_network_workbench.py": "e5c72acda2ea8fae3658faba907448765df2f1e34e05749fbe85faed28ef003b", + "tools/physical-workcell/qualify_workbench_restarts.py": "2bc3edc5ea39b69f3372d4274b757f06472409407a22cd3149dfa52fbf314dcb", + "tools/physical-workcell/record_network_workbench.py": "7604865caa309c7002c01a2384da84eb62d8b22dcd63ab256618c536bc5fedc4", + "tools/physical-workcell/scene.xml": "f411f7632d5df2035c780d26d43e8a066991afe501438daefeedaa68455f251f", + "tools/physical-workcell/score_network_body.py": "d1f7150a5ff23775664b5656fd21379502ba71175487fd7aadb2b51bffcdaf8c", + "tools/physical-workcell/score_network_workbench.py": "67a53586081fe425bd14b0a0fbbf3d7f0fc1e66daccda06765bb88f3d9d4d84c", + "tools/physical-workcell/score_workbench_restarts.py": "181cbf4ee1bbd5483f38edc4a84244fc3efa98dfd21b5c9751d43edfe8f9f5a5", + "tools/physical-workcell/workbench_link.py": "578d94684463bcb3d7f2d45d9ba68abcd27d8a27f7f39538323a269e251c330e", + "tools/physical-workcell/workcell.py": "d7034fc6d528c4476aecb3094e7a0b8fd22d3e09b407e056a4a18a8cf1e5b1e2" + } +} diff --git a/jevdual/results/workbench-restarts-20261012/ui-ledger.json b/jevdual/results/workbench-restarts-20261012/ui-ledger.json new file mode 100644 index 0000000..ca4659d --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui-ledger.json @@ -0,0 +1,734 @@ +{ + "steps": [ + { + "browser_action": "Observed the first lost dispatch reply as Outcome uncertain before any process restart or receipt lookup", + "client_commands": 5, + "name": "uncertain", + "pending_command_ids": [ + "command-24230a0f96bc4e84a84f720b1c974054" + ], + "screenshot": "tools/physical-workcell/output/sim11-browser-restarts-v2/ui/uncertain.jpg", + "state": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 2, + "disturbance_test": false, + "error": "reply is uncertain; reconcile exact command without resending", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Push the block right so its center enters the measurement zone.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "operation": "dispatch", + "reason": "reply is uncertain; reconcile exact command without resending", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791828766.590297, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t75 checkbox Restart inspection 01 Needs attention, Value: 1\n\t\t\t76 checkbox Restart inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t77 text Restart inspection 01\n\t\t78 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t79 text reply is uncertain; reconcile exact command without resending\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t80 text Body connected\n\t\t\t81 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button Check receipt, ID: reconcile\n\t\t\t68 container\n\t\t\t\t69 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t70 text Test the simulated connection\n\t\t\t\t71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t72 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t73 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t74 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t82 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t83 text Last observation \u00b7 Restart inspection 01\n\t\t\t60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t84 text Captured \u00b7 generation 0\n\t\t85 text OUTCOME UNCERTAIN\n\t\t86 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t87 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t88 text 19 events\n\t\t\t43 content list activity\n\t\t\t\t89 container\n\t\t\t\t\t90 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending\n\t\t\t\t91 container\n\t\t\t\t\t92 text 02:12 PM Sending a guarded action\n\t\t\t\t93 container\n\t\t\t\t\t94 text 02:12 PM Action proposal accepted Restart inspection 01\n\t\t\t\t95 container\n\t\t\t\t\t96 text 02:12 PM Checking the planned action\n\t\t\t\t97 container\n\t\t\t\t\t98 text 02:12 PM AI planning requested Restart inspection 01\n\t\t\t\t99 container\n\t\t\t\t\t100 text 02:12 PM Camera observation received Restart inspection 01\n\t\t\t\t101 container\n\t\t\t\t\t102 text 02:12 PM Requesting camera observation\n\t\t\t\t103 container\n\t\t\t\t\t104 text 02:12 PM Agent resumed\n\t\t\t\t105 container\n\t\t\t\t\t106 text 02:12 PM Reply fault armed The next command reply will be lost.\n\t\t\t\t107 container\n\t\t\t\t\t108 text 02:10 PM Agent stopped for review operator_pause_requested\n\t\t\t\t109 container\n\t\t\t\t\t110 text 02:10 PM AI planning requested Restart inspection 01\n\t\t\t\t111 container\n\t\t\t\t\t112 text 02:10 PM Camera observation received Restart inspection 01\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t113 text 2 model requests \u00b7 Reported tokens: 13,063 input / 199 output \u00b7 Reporting incomplete (1 of 2 requests)\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Clicked Disconnect after workbench restart while preserving the same uncertain command", + "client_commands": 5, + "name": "disconnected", + "pending_command_ids": [ + "command-24230a0f96bc4e84a84f720b1c974054" + ], + "screenshot": null, + "state": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Diagnostic link disconnected; cached observations are retained", + "state": "disconnected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 2, + "disturbance_test": false, + "error": "Restarted with a retained command stage; check its exact receipt", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Push the block right so its center enters the measurement zone.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "operation": "dispatch", + "reason": "Restarted with a retained command stage; check its exact receipt", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791828795.696644, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t75 checkbox Restart inspection 01 Needs attention, Value: 1\n\t\t\t76 checkbox Restart inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t77 text Restart inspection 01\n\t\t78 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t79 text Restarted with a retained command stage; check its exact receipt\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t80 text Body disconnected\n\t\t\t81 text Diagnostic link disconnected; cached observations are retained\n\t\t\t20 button Reconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t68 container\n\t\t\t\t69 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t70 text Test the simulated connection\n\t\t\t\t71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t72 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t73 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t74 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t82 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t83 text Retained observation \u00b7 body offline\n\t\t\t60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t84 text Retained \u00b7 offline \u00b7 generation 0\n\t\t85 text DISCONNECTED\n\t\t86 text The body connection is unavailable. The last camera observation is retained.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t87 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t88 text 21 events\n\t\t\t43 content list activity\n\t\t\t\t89 container\n\t\t\t\t\t90 text 02:13 PM Body disconnected Transport only; no body reset or replay\n\t\t\t\t91 container\n\t\t\t\t\t92 text 02:12 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt\n\t\t\t\t93 container\n\t\t\t\t\t94 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending\n\t\t\t\t95 container\n\t\t\t\t\t96 text 02:12 PM Sending a guarded action\n\t\t\t\t97 container\n\t\t\t\t\t98 text 02:12 PM Action proposal accepted Restart inspection 01\n\t\t\t\t99 container\n\t\t\t\t\t100 text 02:12 PM Checking the planned action\n\t\t\t\t101 container\n\t\t\t\t\t102 text 02:12 PM AI planning requested Restart inspection 01\n\t\t\t\t103 container\n\t\t\t\t\t104 text 02:12 PM Camera observation received Restart inspection 01\n\t\t\t\t105 container\n\t\t\t\t\t106 text 02:12 PM Requesting camera observation\n\t\t\t\t107 container\n\t\t\t\t\t108 text 02:12 PM Agent resumed\n\t\t\t\t109 container\n\t\t\t\t\t110 text 02:12 PM Reply fault armed The next command reply will be lost.\n\t\t\t\t111 container\n\t\t\t\t\t112 text 02:10 PM Agent stopped for review operator_pause_requested\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t113 text 2 model requests \u00b7 Reported tokens: 13,063 input / 199 output \u00b7 Reporting incomplete (1 of 2 requests)\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Observed Check receipt disabled while the retained command remained uncertain and the link was disconnected", + "client_commands": 5, + "name": "offline_receipt", + "pending_command_ids": [ + "command-24230a0f96bc4e84a84f720b1c974054" + ], + "screenshot": null, + "state": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Diagnostic link disconnected; cached observations are retained", + "state": "disconnected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 2, + "disturbance_test": false, + "error": "Restarted with a retained command stage; check its exact receipt", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Push the block right so its center enters the measurement zone.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "operation": "dispatch", + "reason": "Restarted with a retained command stage; check its exact receipt", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791828795.808469, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t75 checkbox Restart inspection 01 Needs attention, Value: 1\n\t\t\t76 checkbox Restart inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t77 text Restart inspection 01\n\t\t78 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t79 text Restarted with a retained command stage; check its exact receipt\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t80 text Body disconnected\n\t\t\t81 text Diagnostic link disconnected; cached observations are retained\n\t\t\t20 button Reconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t68 container\n\t\t\t\t69 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t70 text Test the simulated connection\n\t\t\t\t71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t72 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t73 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t74 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t82 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t83 text Retained observation \u00b7 body offline\n\t\t\t60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t84 text Retained \u00b7 offline \u00b7 generation 0\n\t\t85 text DISCONNECTED\n\t\t86 text The body connection is unavailable. The last camera observation is retained.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t87 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t88 text 21 events\n\t\t\t43 content list activity\n\t\t\t\t89 container\n\t\t\t\t\t90 text 02:13 PM Body disconnected Transport only; no body reset or replay\n\t\t\t\t91 container\n\t\t\t\t\t92 text 02:12 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt\n\t\t\t\t93 container\n\t\t\t\t\t94 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending\n\t\t\t\t95 container\n\t\t\t\t\t96 text 02:12 PM Sending a guarded action\n\t\t\t\t97 container\n\t\t\t\t\t98 text 02:12 PM Action proposal accepted Restart inspection 01\n\t\t\t\t99 container\n\t\t\t\t\t100 text 02:12 PM Checking the planned action\n\t\t\t\t101 container\n\t\t\t\t\t102 text 02:12 PM AI planning requested Restart inspection 01\n\t\t\t\t103 container\n\t\t\t\t\t104 text 02:12 PM Camera observation received Restart inspection 01\n\t\t\t\t105 container\n\t\t\t\t\t106 text 02:12 PM Requesting camera observation\n\t\t\t\t107 container\n\t\t\t\t\t108 text 02:12 PM Agent resumed\n\t\t\t\t109 container\n\t\t\t\t\t110 text 02:12 PM Reply fault armed The next command reply will be lost.\n\t\t\t\t111 container\n\t\t\t\t\t112 text 02:10 PM Agent stopped for review operator_pause_requested\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t113 text 2 model requests \u00b7 Reported tokens: 13,063 input / 199 output \u00b7 Reporting incomplete (1 of 2 requests)\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Clicked Reconnect; the same unknown command remained blocked and no command was sent", + "client_commands": 5, + "name": "reconnected", + "pending_command_ids": [ + "command-24230a0f96bc4e84a84f720b1c974054" + ], + "screenshot": null, + "state": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 2, + "disturbance_test": false, + "error": "Restarted with a retained command stage; check its exact receipt", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Push the block right so its center enters the measurement zone.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "unknown", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "operation": "dispatch", + "reason": "Restarted with a retained command stage; check its exact receipt", + "state": "uncertain" + }, + "runner_status": "blocked" + }, + "time": 1791828811.400429, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button (disabled) \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t75 checkbox Restart inspection 01 Needs attention, Value: 1\n\t\t\t76 checkbox Restart inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t77 text Restart inspection 01\n\t\t78 text Needs attention\n\t\t16 button (disabled) Pause, ID: pause\n\t\t79 text Restarted with a retained command stage; check its exact receipt\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t80 text Body connected\n\t\t\t81 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button Check receipt, ID: reconcile\n\t\t\t68 container\n\t\t\t\t69 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t70 text Test the simulated connection\n\t\t\t\t71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t72 button (disabled) Lose next reply, ID: fault-drop\n\t\t\t\t73 button (disabled) Delay next reply, ID: fault-delay\n\t\t\t\t74 button (disabled) Duplicate next command, ID: fault-duplicate\n\t\t\t\t82 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t83 text Last observation \u00b7 Restart inspection 01\n\t\t\t60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t84 text Captured \u00b7 generation 0\n\t\t85 text OUTCOME UNCERTAIN\n\t\t86 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t87 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t88 text 22 events\n\t\t\t43 content list activity\n\t\t\t\t89 container\n\t\t\t\t\t90 text 02:13 PM Body reconnecting No command sent or replayed\n\t\t\t\t91 container\n\t\t\t\t\t92 text 02:13 PM Body disconnected Transport only; no body reset or replay\n\t\t\t\t93 container\n\t\t\t\t\t94 text 02:12 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt\n\t\t\t\t95 container\n\t\t\t\t\t96 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending\n\t\t\t\t97 container\n\t\t\t\t\t98 text 02:12 PM Sending a guarded action\n\t\t\t\t99 container\n\t\t\t\t\t100 text 02:12 PM Action proposal accepted Restart inspection 01\n\t\t\t\t101 container\n\t\t\t\t\t102 text 02:12 PM Checking the planned action\n\t\t\t\t103 container\n\t\t\t\t\t104 text 02:12 PM AI planning requested Restart inspection 01\n\t\t\t\t105 container\n\t\t\t\t\t106 text 02:12 PM Camera observation received Restart inspection 01\n\t\t\t\t107 container\n\t\t\t\t\t108 text 02:12 PM Requesting camera observation\n\t\t\t\t109 container\n\t\t\t\t\t110 text 02:12 PM Agent resumed\n\t\t\t\t111 container\n\t\t\t\t\t112 text 02:12 PM Reply fault armed The next command reply will be lost.\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t113 text 2 model requests \u00b7 Reported tokens: 13,063 input / 199 output \u00b7 Reporting incomplete (1 of 2 requests)\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Clicked Check receipt after workbench restart; the exact historical command settled and remained paused without a new command", + "client_commands": 5, + "name": "recovered", + "pending_command_ids": [], + "screenshot": "tools/physical-workcell/output/sim11-browser-restarts-v2/ui/recovered.jpg", + "state": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 0, + "image_url": "/api/frame?generation=0&sha256=4fe3b2c26529a4b05ed8a8f0d72bfe586ceea6567dab4713011a550f6f12cbe7" + }, + "jobs": [ + { + "call_count": 2, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Push the block right so its center enters the measurement zone.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-24230a0f96bc4e84a84f720b1c974054", + "operation": "dispatch", + "reason": "Exact historical stage recovered; explicit Resume observes fresh evidence", + "state": "recovered" + }, + "runner_status": "paused" + }, + "time": 1791828827.093657, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t75 checkbox Restart inspection 01 Paused, Value: 1\n\t\t\t76 checkbox Restart inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t77 text Restart inspection 01\n\t\t78 text Paused\n\t\t16 button Resume, ID: pause\n\t\t79 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue.\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t80 text Body connected\n\t\t\t81 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t68 container\n\t\t\t\t69 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t70 text Test the simulated connection\n\t\t\t\t71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t72 button Lose next reply, ID: fault-drop\n\t\t\t\t73 button Delay next reply, ID: fault-delay\n\t\t\t\t74 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t82 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t83 text Last observation \u00b7 Restart inspection 01\n\t\t\t60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t84 text Captured \u00b7 generation 0\n\t\t85 text RECEIPT RECOVERED\n\t\t86 text The exact command receipt was recovered. The agent is paused until you resume.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t87 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t88 text 24 events\n\t\t\t90 content list activity\n\t\t\t\t91 container\n\t\t\t\t\t92 text 02:13 PM Command receipt recovered The agent remains paused until you resume.\n\t\t\t\t93 container\n\t\t\t\t\t94 text 02:13 PM Action receipt checked Restart inspection 01\n\t\t\t\t95 container\n\t\t\t\t\t96 text 02:13 PM Body reconnecting No command sent or replayed\n\t\t\t\t97 container\n\t\t\t\t\t98 text 02:13 PM Body disconnected Transport only; no body reset or replay\n\t\t\t\t99 container\n\t\t\t\t\t100 text 02:12 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt\n\t\t\t\t101 container\n\t\t\t\t\t102 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending\n\t\t\t\t103 container\n\t\t\t\t\t104 text 02:12 PM Sending a guarded action\n\t\t\t\t105 container\n\t\t\t\t\t106 text 02:12 PM Action proposal accepted Restart inspection 01\n\t\t\t\t107 container\n\t\t\t\t\t108 text 02:12 PM Checking the planned action\n\t\t\t\t109 container\n\t\t\t\t\t110 text 02:12 PM AI planning requested Restart inspection 01\n\t\t\t\t111 container\n\t\t\t\t\t112 text 02:12 PM Camera observation received Restart inspection 01\n\t\t\t\t113 container\n\t\t\t\t\t114 text 02:12 PM Requesting camera observation\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t89 text 2 model requests \u00b7 Reported tokens: 13,063 input / 199 output \u00b7 Reporting incomplete (1 of 2 requests)\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Clicked Resume explicitly after receipt recovery; next step takes a fresh TCP camera observation", + "client_commands": 6, + "name": "resumed", + "pending_command_ids": [], + "screenshot": null, + "state": { + "active_job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 1, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 1, + "image_url": "/api/frame?generation=1&sha256=cb4221e600bbb24aec549f49924fc9224876a1ad8d04159fe0cc3798658d8357" + }, + "jobs": [ + { + "call_count": 3, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Push the block right so its center enters the measurement zone.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "A" + }, + { + "call_count": 0, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "", + "measurement": null, + "phase": null, + "reset": null, + "status": "queued", + "target": "B" + } + ], + "model_request_pending": true, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791828841.224544, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t91 checkbox Restart inspection 01 Paused, Value: 1\n\t\t\t92 checkbox Restart inspection 02 Queued, Value: 0\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t93 text Restart inspection 01\n\t\t94 text Running\n\t\t16 button Pause, ID: pause\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t95 text Body connected\n\t\t\t96 text Current body boot verified for explicit Resume\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t68 container\n\t\t\t\t69 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t70 text Test the simulated connection\n\t\t\t\t71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t72 button Lose next reply, ID: fault-drop\n\t\t\t\t73 button Delay next reply, ID: fault-delay\n\t\t\t\t74 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t97 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t98 text Last observation \u00b7 Restart inspection 01\n\t\t\t60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t99 text Captured \u00b7 generation 0\n\t\t100 text AGENT\n\t\t101 text Push the block right so its center enters the measurement zone.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t102 text Pending\n\t\t\t38 text The part\u2019s visible footprint will be measured after its placement is verified.\n\t\t39 container Activity\n\t\t\t40 heading Activity, Value: 2, ID: activity-heading\n\t\t\t\t41 text Activity\n\t\t\t103 text 24 events\n\t\t\t90 content list activity\n\t\t\t\t104 container\n\t\t\t\t\t105 text 02:13 PM Requesting camera observation\n\t\t\t\t106 container\n\t\t\t\t\t107 text 02:13 PM Agent resumed\n\t\t\t\t108 container\n\t\t\t\t\t109 text 02:13 PM Command receipt recovered The agent remains paused until you resume.\n\t\t\t\t110 container\n\t\t\t\t\t111 text 02:13 PM Action receipt checked Restart inspection 01\n\t\t\t\t112 container\n\t\t\t\t\t113 text 02:13 PM Body reconnecting No command sent or replayed\n\t\t\t\t114 container\n\t\t\t\t\t115 text 02:13 PM Body disconnected Transport only; no body reset or replay\n\t\t\t\t116 container\n\t\t\t\t\t117 text 02:12 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt\n\t\t\t\t118 container\n\t\t\t\t\t119 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending\n\t\t\t\t120 container\n\t\t\t\t\t121 text 02:12 PM Sending a guarded action\n\t\t\t\t122 container\n\t\t\t\t\t123 text 02:12 PM Action proposal accepted Restart inspection 01\n\t\t\t\t124 container\n\t\t\t\t\t125 text 02:12 PM Checking the planned action\n\t\t\t\t126 container\n\t\t\t\t\t127 text 02:12 PM AI planning requested Restart inspection 01\n\t\t50 button (collapsed) Session details, Secondary Actions: Expand\n\t\t\t51 text Session details\n\t\t128 text 2 model requests \u00b7 Reported tokens: 13,063 input / 199 output \u00b7 Reporting incomplete (1 of 2 requests)\n\nThe focused UI element is 1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/" + }, + { + "browser_action": "Clicked Check receipt after body reboot; historical boot-two command recovered while boot three remained paused; full UI capture retained separately", + "client_commands": 46, + "name": "body_uncertain_recovered", + "pending_command_ids": [], + "screenshot": "tools/physical-workcell/output/sim11-browser-restarts-v2/ui/body-uncertain-recovered.jpg", + "state": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 10, + "image_url": "/api/frame?generation=10&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 2, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "paused", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": "command-cf898dd6e56a4d8bb0a5894371f9ba81", + "operation": "dispatch", + "reason": "Exact historical stage recovered; explicit Resume observes fresh evidence", + "state": "recovered" + }, + "runner_status": "paused" + }, + "time": 1791829096.6301959, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t105 checkbox Restart inspection 01 Completed, Value: 0\n\t\t\t106 checkbox Restart inspection 02 Paused, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t107 text Restart inspection 02\n\t\t108 text Paused\n\t\t16 button Resume, ID: pause\n\t\t109 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue.\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t110 text Body connected\n\t\t\t111 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t68 container\n\t\t\t\t69 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t70 text Test the simulated connection\n\t\t\t\t71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t72 button Lose next reply, ID: fault-drop\n\t\t\t\t73 button Delay next reply, ID: fault-delay\n\t\t\t\t74 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t112 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t113 text Last observation \u00b7 Restart inspection 02\n\t\t\t60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t114 text Captured \u00b7 generation 10\n\t\t115 text RECEIPT RECOVERED\n\t\t116 text The exact command receipt was recovered. The agent is paused until you resume.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t117 text Pending\n\t\t\t104 text The part\u2019s visible footprint will be measured after its placement is verified.\n[Main-panel excerpt; full browser capture is body-uncertain-recovered.txt]\n" + }, + { + "browser_action": "Clicked Resume explicitly after boot-two receipt recovery; the next decision receives a fresh boot-three TCP image; full UI capture retained separately", + "client_commands": 47, + "name": "body_uncertain_resumed", + "pending_command_ids": [], + "screenshot": null, + "state": { + "active_job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 11, + "image_url": "/api/frame?generation=11&sha256=0d42c93fef97b6bf1ded19965b65518e43b873916c9e8bc1d8300debf130f9fa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 3, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "Push the block right so its center reaches the measurement region.", + "measurement": null, + "phase": "prepare", + "reset": null, + "status": "running", + "target": "B" + } + ], + "model_request_pending": true, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791829107.8461351, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t105 checkbox Restart inspection 01 Completed, Value: 0\n\t\t\t106 checkbox Restart inspection 02 Paused, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t107 text Restart inspection 02\n\t\t108 text Running\n\t\t16 button Pause, ID: pause\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t109 text Body connected\n\t\t\t110 text Current body boot verified for explicit Resume\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t68 container\n\t\t\t\t69 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t70 text Test the simulated connection\n\t\t\t\t71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t72 button Lose next reply, ID: fault-drop\n\t\t\t\t73 button Delay next reply, ID: fault-delay\n\t\t\t\t74 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t111 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t112 text Last observation \u00b7 Restart inspection 02\n\t\t\t60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t113 text Captured \u00b7 generation 10\n\t\t114 text AGENT\n\t\t115 text Push the block right so its center reaches the measurement region.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t116 text Pending\n\t\t\t104 text The part\u2019s visible footprint will be measured after its placement is verified.\n[Main-panel excerpt; full browser capture is body-uncertain-resumed.txt]\n" + }, + { + "browser_action": "Observed both jobs completed after all four restart recoveries and one explicit health-timeout resume; full browser capture retained separately", + "client_commands": 83, + "name": "completed", + "pending_command_ids": [], + "screenshot": "tools/physical-workcell/output/sim11-browser-restarts-v2/ui/completed.jpg", + "state": { + "active_job_id": null, + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 20, + "image_url": "/api/frame?generation=20&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 15, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "Finish reset with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 20, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "kind": "finish_reset", + "native_time_s": 42.630000000006135, + "operation_id": "agent-7acee30a3cb442d9a3a2b3a9bfec10ed", + "pusher_home_verified": true, + "reset_contact_samples": 3314, + "reset_native_steps": 8140, + "state_sha256": "02c49f9f03f16f8de552b4b4ce57b6d78c85e291fc3dbf1b6acb0ce4b7cd638b" + }, + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791829354.9904199, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t140 checkbox Restart inspection 01 Completed, Value: 0\n\t\t\t141 checkbox Restart inspection 02 Completed, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t142 text Restart inspection 02\n\t\t143 text Running\n\t\t16 button Pause, ID: pause\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t144 text Body connected\n\t\t\t145 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t68 container\n\t\t\t\t69 button (expanded) Test the simulated connection, Secondary Actions: Collapse\n\t\t\t\t\t70 text Test the simulated connection\n\t\t\t\t71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.\n\t\t\t\t72 button Lose next reply, ID: fault-drop\n\t\t\t\t73 button Delay next reply, ID: fault-delay\n\t\t\t\t74 button Duplicate next command, ID: fault-duplicate\n\t\t\t\t146 text No reply fault armed.\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t147 text Last camera observation\n\t\t\t60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t148 text Captured \u00b7 generation 20\n\t\t149 text COMPLETED\n\t\t150 text Measurement recorded and reset verified. The bench is ready for another job.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t151 text Historical result\n\t\t\t132 container measurement-result\n\t\t\t\t152 text 1,729\n\t\t\t\t134 text Orange pixels\n\t\t\t\t135 container\n\t\t\t\t\t153 text 4,802.8\n\t\t\t\t\t137 text mm\u00b2\n\t\t\t\t138 text Apparent footprint\n\t\t\t\t139 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment.\n\t\t\t154 button Repeat job, ID: repeat\n[Main-panel excerpt; full browser capture is completed.txt]\n" + }, + { + "browser_action": "Reloaded the browser; completed job records and the 83-command journal remained identical with no replay; full capture retained separately", + "client_commands": 83, + "name": "reloaded", + "pending_command_ids": [], + "screenshot": null, + "state": { + "active_job_id": null, + "app_id": "878fad93039a4e86b4dcc5529f118f20", + "connection": { + "adapter_id": "96943574039142dda13cb620decfddf0", + "body_id": "a26eb04332df4e1e9f3f0f307db47644", + "epoch": 3, + "fault": null, + "reason": "Body identity verified by read-only TCP health", + "state": "connected" + }, + "current_frame": { + "available": true, + "generation": 20, + "image_url": "/api/frame?generation=20&sha256=118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa" + }, + "jobs": [ + { + "call_count": 14, + "disturbance_test": false, + "error": null, + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "label": "Restart inspection 01", + "last_intent": "Request verification with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004794444444444444, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1726 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 10, + "image_sha256": "b404f8a09f6ccd21c78edfec024e7e3e940c21146aeeaa3f58975e6aee8a7e87", + "job_id": "20eb6586-e73a-4db4-aaac-db3e5d537329", + "kind": "finish_reset", + "native_time_s": 21.679999999998863, + "operation_id": "agent-e8227a121d794919a11b4e108dff63f6", + "pusher_home_verified": true, + "reset_contact_samples": 3174, + "reset_native_steps": 8106, + "state_sha256": "f403fd1d61afe7bb83d4a4ff3dd9aa56936c5dbe9d0a709af793549130097d0e" + }, + "status": "completed", + "target": "A" + }, + { + "call_count": 15, + "disturbance_test": false, + "error": null, + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "label": "Restart inspection 02", + "last_intent": "Finish reset with the block centered in the home zone and the pusher at its home position.", + "measurement": { + "footprint_area_m2": 0.004802777777777777, + "method": "simulated_orange_RGB_footprint", + "orange_pixel_count": 1729 + }, + "phase": "completed", + "reset": { + "bench_id": "a26eb04332df4e1e9f3f0f307db47644", + "block_home_verified": true, + "generation": 20, + "image_sha256": "118bf53bca3344b8d0318c995ebe8b171a2a8bbc09c0ae163d70cc0f1d4aaefa", + "job_id": "c922a26d-9121-4d7e-82e1-7b61b0e42161", + "kind": "finish_reset", + "native_time_s": 42.630000000006135, + "operation_id": "agent-7acee30a3cb442d9a3a2b3a9bfec10ed", + "pusher_home_verified": true, + "reset_contact_samples": 3314, + "reset_native_steps": 8140, + "state_sha256": "02c49f9f03f16f8de552b4b4ce57b6d78c85e291fc3dbf1b6acb0ce4b7cd638b" + }, + "status": "completed", + "target": "B" + } + ], + "model_request_pending": false, + "recovery": { + "command_id": null, + "operation": null, + "reason": "No pending command", + "state": "none" + }, + "runner_status": "running" + }, + "time": 1791829364.1247208, + "visible_text": "Browser tab: 4, Title: \"Footwork \u00b7 Network bench\", URL: \"http://127.0.0.1:51377/\".\n1 AXWebArea Footwork \u00b7 Network bench, URL: 127.0.0.1:51377/\n\t2 container\n\t\t3 text Footwork Bench\n\t\t4 text SIMULATION\n\t5 container Jobs\n\t\t6 text WORK QUEUE\n\t\t7 button \uff0b New job, ID: new-job\n\t\t8 container queue\n\t\t\t9 checkbox Restart inspection 01 Completed, Value: 0\n\t\t\t10 checkbox Restart inspection 02 Completed, Value: 1\n\t11 container\n\t\t12 heading Bench, Value: 1\n\t\t\t13 text Bench\n\t\t14 text Restart inspection 02\n\t\t15 text Running\n\t\t16 button Pause, ID: pause\n\t\t17 container Description: Body connection, ID: network-card\n\t\t\t18 text Body connected\n\t\t\t19 text Body identity verified by read-only TCP health\n\t\t\t20 button Disconnect, ID: disconnect\n\t\t\t21 button (disabled) Check receipt, ID: reconcile\n\t\t\t22 button (collapsed) Test the simulated connection, Secondary Actions: Expand\n\t\t\t\t23 text Test the simulated connection\n\t\t24 container Camera observation\n\t\t\t25 text OVERHEAD CAMERA\n\t\t\t26 text Last camera observation\n\t\t\t27 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame\n\t\t\t28 text Orange part Blue pusher Green task region\n\t\t\t29 text Captured \u00b7 generation 20\n\t\t30 text COMPLETED\n\t\t31 text Measurement recorded and reset verified. The bench is ready for another job.\n\t\t32 container Job progress\n\t\t\t33 text 1 Prepare 2 Measure 3 Reset 4 Ready\n\t\t34 container Measurement\n\t\t\t35 heading Measurement, Value: 2, ID: measurement-heading\n\t\t\t\t36 text Measurement\n\t\t\t37 text Historical result\n\t\t\t38 container measurement-result\n\t\t\t\t39 text 1,729\n\t\t\t\t40 text Orange pixels\n\t\t\t\t41 container\n\t\t\t\t\t42 text 4,802.8\n\t\t\t\t\t43 text mm\u00b2\n\t\t\t\t44 text Apparent footprint\n\t\t\t\t45 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI\u2019s judgment.\n\t\t\t46 button Repeat job, ID: repeat\n[Main-panel excerpt; full browser capture is reloaded.txt]\n" + } + ] +} diff --git a/jevdual/results/workbench-restarts-20261012/ui/body-planning-offline.jpg b/jevdual/results/workbench-restarts-20261012/ui/body-planning-offline.jpg new file mode 100644 index 0000000..a415cba Binary files /dev/null and b/jevdual/results/workbench-restarts-20261012/ui/body-planning-offline.jpg differ diff --git a/jevdual/results/workbench-restarts-20261012/ui/body-planning-offline.txt b/jevdual/results/workbench-restarts-20261012/ui/body-planning-offline.txt new file mode 100644 index 0000000..0681d30 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/body-planning-offline.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 container queue + 105 checkbox Restart inspection 01 Completed, Value: 0 + 106 checkbox Restart inspection 02 Paused, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 107 text Restart inspection 02 + 108 text Paused + 16 button (disabled) Resume, ID: pause + 109 text Remote end closed connection without response + 17 container Description: Body connection, ID: network-card + 110 text Body unavailable + 111 text Remote end closed connection without response + 20 button Reconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button (disabled) Lose next reply, ID: fault-drop + 73 button (disabled) Delay next reply, ID: fault-delay + 74 button (disabled) Duplicate next command, ID: fault-duplicate + 112 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 113 text Retained observation · body offline + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 114 text Retained · offline · generation 10 + 115 text DISCONNECTED + 116 text The body connection is unavailable. The last camera observation is retained. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 117 text Pending + 104 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 118 text 24 events + 90 content list activity + 119 container + 120 text 02:15 PM Agent stopped for review operator_pause_requested + 121 container + 122 text 02:15 PM Agent stopped for review Remote end closed connection without response + 123 container + 124 text 02:15 PM AI planning requested Restart inspection 02 + 125 container + 126 text 02:15 PM Camera observation received Restart inspection 02 + 127 container + 128 text 02:15 PM Requesting camera observation + 129 container + 130 text 02:15 PM Inspection started Restart inspection 02 + 131 container + 132 text 02:15 PM Preparing inspection + 133 container + 134 text 02:15 PM Action receipt checked Restart inspection 01 + 135 container + 136 text 02:15 PM Sending a guarded action + 137 container + 138 text 02:15 PM Action proposal accepted Restart inspection 01 + 139 container + 140 text 02:15 PM Checking the planned action + 141 container + 142 text 02:15 PM AI planning requested Restart inspection 01 + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 143 text 15 model requests · Reported tokens: 172,372 input / 2,008 output · Reporting incomplete (13 of 15 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/body-planning-restored.txt b/jevdual/results/workbench-restarts-20261012/ui/body-planning-restored.txt new file mode 100644 index 0000000..1eb041e --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/body-planning-restored.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 105 checkbox Restart inspection 01 Completed, Value: 0 + 106 checkbox Restart inspection 02 Paused, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 107 text Restart inspection 02 + 108 text Paused + 16 button Resume, ID: pause + 109 text Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 17 container Description: Body connection, ID: network-card + 110 text Body connected + 111 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button Lose next reply, ID: fault-drop + 73 button Delay next reply, ID: fault-delay + 74 button Duplicate next command, ID: fault-duplicate + 112 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 113 text Last observation · Restart inspection 02 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 114 text Captured · generation 10 + 115 text PAUSED + 116 text Paused. Resume when you are ready for the next action. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 117 text Pending + 104 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 118 text 24 events + 90 content list activity + 119 container + 120 text 02:16 PM Body restarted 1 -> 2 + 121 container + 122 text 02:16 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 123 container + 124 text 02:15 PM Agent stopped for review operator_pause_requested + 125 container + 126 text 02:15 PM Agent stopped for review Remote end closed connection without response + 127 container + 128 text 02:15 PM AI planning requested Restart inspection 02 + 129 container + 130 text 02:15 PM Camera observation received Restart inspection 02 + 131 container + 132 text 02:15 PM Requesting camera observation + 133 container + 134 text 02:15 PM Inspection started Restart inspection 02 + 135 container + 136 text 02:15 PM Preparing inspection + 137 container + 138 text 02:15 PM Action receipt checked Restart inspection 01 + 139 container + 140 text 02:15 PM Sending a guarded action + 141 container + 142 text 02:15 PM Action proposal accepted Restart inspection 01 + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 143 text 15 model requests · Reported tokens: 172,372 input / 2,008 output · Reporting incomplete (13 of 15 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/body-planning-resumed.txt b/jevdual/results/workbench-restarts-20261012/ui/body-planning-resumed.txt new file mode 100644 index 0000000..b8706cb --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/body-planning-resumed.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 105 checkbox Restart inspection 01 Completed, Value: 0 + 106 checkbox Restart inspection 02 Paused, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 107 text Restart inspection 02 + 108 text Running + 16 button Pause, ID: pause + 109 text Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 17 container Description: Body connection, ID: network-card + 110 text Body connected + 111 text Current body boot verified for explicit Resume + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button (disabled) Lose next reply, ID: fault-drop + 73 button (disabled) Delay next reply, ID: fault-delay + 74 button (disabled) Duplicate next command, ID: fault-duplicate + 112 text The next command reply will be lost. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 113 text Last observation · Restart inspection 02 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 114 text Captured · generation 10 + 115 text AGENT + 116 text Waiting for the agent’s next observation. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 117 text Pending + 104 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 118 text 24 events + 90 content list activity + 119 container + 120 text 02:16 PM Agent resumed + 121 container + 122 text 02:16 PM Reply fault armed The next command reply will be lost. + 123 container + 124 text 02:16 PM Body restarted 1 -> 2 + 125 container + 126 text 02:16 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 127 container + 128 text 02:15 PM Agent stopped for review operator_pause_requested + 129 container + 130 text 02:15 PM Agent stopped for review Remote end closed connection without response + 131 container + 132 text 02:15 PM AI planning requested Restart inspection 02 + 133 container + 134 text 02:15 PM Camera observation received Restart inspection 02 + 135 container + 136 text 02:15 PM Requesting camera observation + 137 container + 138 text 02:15 PM Inspection started Restart inspection 02 + 139 container + 140 text 02:15 PM Preparing inspection + 141 container + 142 text 02:15 PM Action receipt checked Restart inspection 01 + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 143 text 15 model requests · Reported tokens: 172,372 input / 2,008 output · Reporting incomplete (13 of 15 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-before.txt b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-before.txt new file mode 100644 index 0000000..c54b336 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-before.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 container queue + 105 checkbox Restart inspection 01 Completed, Value: 0 + 106 checkbox Restart inspection 02 Needs attention, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 107 text Restart inspection 02 + 108 text Needs attention + 16 button (disabled) Pause, ID: pause + 109 text reply is uncertain; reconcile exact command without resending + 17 container Description: Body connection, ID: network-card + 110 text Body connected + 111 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button (disabled) Lose next reply, ID: fault-drop + 73 button (disabled) Delay next reply, ID: fault-delay + 74 button (disabled) Duplicate next command, ID: fault-duplicate + 112 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 113 text Last observation · Restart inspection 02 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 114 text Captured · generation 10 + 115 text OUTCOME UNCERTAIN + 116 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 117 text Pending + 104 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 118 text 24 events + 90 content list activity + 119 container + 120 text 02:17 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 121 container + 122 text 02:17 PM Sending a guarded action + 123 container + 124 text 02:17 PM Action proposal accepted Restart inspection 02 + 125 container + 126 text 02:17 PM Checking the planned action + 127 container + 128 text 02:17 PM AI planning requested Restart inspection 02 + 129 container + 130 text 02:17 PM Camera observation received Restart inspection 02 + 131 container + 132 text 02:16 PM Requesting camera observation + 133 container + 134 text 02:16 PM Agent resumed + 135 container + 136 text 02:16 PM Reply fault armed The next command reply will be lost. + 137 container + 138 text 02:16 PM Body restarted 1 -> 2 + 139 container + 140 text 02:16 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 141 container + 142 text 02:15 PM Agent stopped for review operator_pause_requested + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 143 text 16 model requests · Reported tokens: 185,456 input / 2,222 output · Reporting incomplete (14 of 16 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-offline.txt b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-offline.txt new file mode 100644 index 0000000..c5250da --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-offline.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 container queue + 105 checkbox Restart inspection 01 Completed, Value: 0 + 106 checkbox Restart inspection 02 Paused, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 107 text Restart inspection 02 + 108 text Paused + 16 button (disabled) Resume, ID: pause + 109 text reply is uncertain; reconcile exact command without resending + 17 container Description: Body connection, ID: network-card + 110 text Body unavailable + 111 text Remote end closed connection without response + 20 button Reconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button (disabled) Lose next reply, ID: fault-drop + 73 button (disabled) Delay next reply, ID: fault-delay + 74 button (disabled) Duplicate next command, ID: fault-duplicate + 112 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 113 text Retained observation · body offline + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 114 text Retained · offline · generation 10 + 115 text DISCONNECTED + 116 text The body connection is unavailable. The last camera observation is retained. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 117 text Pending + 104 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 118 text 24 events + 90 content list activity + 119 container + 120 text 02:17 PM Agent stopped for review Remote end closed connection without response + 121 container + 122 text 02:17 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 123 container + 124 text 02:17 PM Sending a guarded action + 125 container + 126 text 02:17 PM Action proposal accepted Restart inspection 02 + 127 container + 128 text 02:17 PM Checking the planned action + 129 container + 130 text 02:17 PM AI planning requested Restart inspection 02 + 131 container + 132 text 02:17 PM Camera observation received Restart inspection 02 + 133 container + 134 text 02:16 PM Requesting camera observation + 135 container + 136 text 02:16 PM Agent resumed + 137 container + 138 text 02:16 PM Reply fault armed The next command reply will be lost. + 139 container + 140 text 02:16 PM Body restarted 1 -> 2 + 141 container + 142 text 02:16 PM Agent stopped for review Body restarted from boot 1 to 2; check pending receipts and explicitly Resume for a fresh image + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 143 text 16 model requests · Reported tokens: 185,456 input / 2,222 output · Reporting incomplete (14 of 16 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-recovered.jpg b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-recovered.jpg new file mode 100644 index 0000000..73a441c Binary files /dev/null and b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-recovered.jpg differ diff --git a/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-recovered.ledger.txt b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-recovered.ledger.txt new file mode 100644 index 0000000..99ccef1 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-recovered.ledger.txt @@ -0,0 +1,47 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 105 checkbox Restart inspection 01 Completed, Value: 0 + 106 checkbox Restart inspection 02 Paused, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 107 text Restart inspection 02 + 108 text Paused + 16 button Resume, ID: pause + 109 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue. + 17 container Description: Body connection, ID: network-card + 110 text Body connected + 111 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button Lose next reply, ID: fault-drop + 73 button Delay next reply, ID: fault-delay + 74 button Duplicate next command, ID: fault-duplicate + 112 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 113 text Last observation · Restart inspection 02 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 114 text Captured · generation 10 + 115 text RECEIPT RECOVERED + 116 text The exact command receipt was recovered. The agent is paused until you resume. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 117 text Pending + 104 text The part’s visible footprint will be measured after its placement is verified. +[Main-panel excerpt; full browser capture is body-uncertain-recovered.txt] diff --git a/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-recovered.txt b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-recovered.txt new file mode 100644 index 0000000..1bfa87f --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-recovered.txt @@ -0,0 +1,55 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 105 checkbox Restart inspection 01 Completed, Value: 0 + 106 checkbox Restart inspection 02 Paused, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 107 text Restart inspection 02 + 108 text Paused + 16 button Resume, ID: pause + 109 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue. + 17 container Description: Body connection, ID: network-card + 110 text Body connected + 111 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button Lose next reply, ID: fault-drop + 73 button Delay next reply, ID: fault-delay + 74 button Duplicate next command, ID: fault-duplicate + 112 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 113 text Last observation · Restart inspection 02 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 114 text Captured · generation 10 + 115 text RECEIPT RECOVERED + 116 text The exact command receipt was recovered. The agent is paused until you resume. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 117 text Pending + 104 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 118 text 24 events + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 119 text 16 model requests · Reported tokens: 185,456 input / 2,222 output · Reporting incomplete (14 of 16 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-restored.txt b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-restored.txt new file mode 100644 index 0000000..6ddc687 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-restored.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 container queue + 105 checkbox Restart inspection 01 Completed, Value: 0 + 106 checkbox Restart inspection 02 Needs attention, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 107 text Restart inspection 02 + 108 text Needs attention + 16 button (disabled) Pause, ID: pause + 109 text Body restarted from boot 2 to 3; check pending receipts and explicitly Resume for a fresh image + 17 container Description: Body connection, ID: network-card + 110 text Body connected + 111 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button (disabled) Lose next reply, ID: fault-drop + 73 button (disabled) Delay next reply, ID: fault-delay + 74 button (disabled) Duplicate next command, ID: fault-duplicate + 112 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 113 text Last observation · Restart inspection 02 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 114 text Captured · generation 10 + 115 text OUTCOME UNCERTAIN + 116 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 117 text Pending + 104 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 118 text 24 events + 90 content list activity + 119 container + 120 text 02:18 PM Body restarted 2 -> 3 + 121 container + 122 text 02:18 PM Agent stopped for review Body restarted from boot 2 to 3; check pending receipts and explicitly Resume for a fresh image + 123 container + 124 text 02:17 PM Agent stopped for review Remote end closed connection without response + 125 container + 126 text 02:17 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 127 container + 128 text 02:17 PM Sending a guarded action + 129 container + 130 text 02:17 PM Action proposal accepted Restart inspection 02 + 131 container + 132 text 02:17 PM Checking the planned action + 133 container + 134 text 02:17 PM AI planning requested Restart inspection 02 + 135 container + 136 text 02:17 PM Camera observation received Restart inspection 02 + 137 container + 138 text 02:16 PM Requesting camera observation + 139 container + 140 text 02:16 PM Agent resumed + 141 container + 142 text 02:16 PM Reply fault armed The next command reply will be lost. + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 143 text 16 model requests · Reported tokens: 185,456 input / 2,222 output · Reporting incomplete (14 of 16 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-resumed.ledger.txt b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-resumed.ledger.txt new file mode 100644 index 0000000..6555e7e --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-resumed.ledger.txt @@ -0,0 +1,46 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 105 checkbox Restart inspection 01 Completed, Value: 0 + 106 checkbox Restart inspection 02 Paused, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 107 text Restart inspection 02 + 108 text Running + 16 button Pause, ID: pause + 17 container Description: Body connection, ID: network-card + 109 text Body connected + 110 text Current body boot verified for explicit Resume + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button Lose next reply, ID: fault-drop + 73 button Delay next reply, ID: fault-delay + 74 button Duplicate next command, ID: fault-duplicate + 111 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 112 text Last observation · Restart inspection 02 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 113 text Captured · generation 10 + 114 text AGENT + 115 text Push the block right so its center reaches the measurement region. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 116 text Pending + 104 text The part’s visible footprint will be measured after its placement is verified. +[Main-panel excerpt; full browser capture is body-uncertain-resumed.txt] diff --git a/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-resumed.txt b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-resumed.txt new file mode 100644 index 0000000..efcbbe6 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/body-uncertain-resumed.txt @@ -0,0 +1,79 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 105 checkbox Restart inspection 01 Completed, Value: 0 + 106 checkbox Restart inspection 02 Paused, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 107 text Restart inspection 02 + 108 text Running + 16 button Pause, ID: pause + 17 container Description: Body connection, ID: network-card + 109 text Body connected + 110 text Current body boot verified for explicit Resume + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button Lose next reply, ID: fault-drop + 73 button Delay next reply, ID: fault-delay + 74 button Duplicate next command, ID: fault-duplicate + 111 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 112 text Last observation · Restart inspection 02 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 113 text Captured · generation 10 + 114 text AGENT + 115 text Push the block right so its center reaches the measurement region. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 116 text Pending + 104 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 117 text 24 events + 118 content list activity + 119 container + 120 text 02:18 PM Agent resumed + 121 container + 122 text 02:18 PM Command receipt recovered The agent remains paused until you resume. + 123 container + 124 text 02:18 PM Action receipt checked Restart inspection 02 + 125 container + 126 text 02:18 PM Body restarted 2 -> 3 + 127 container + 128 text 02:18 PM Agent stopped for review Body restarted from boot 2 to 3; check pending receipts and explicitly Resume for a fresh image + 129 container + 130 text 02:17 PM Agent stopped for review Remote end closed connection without response + 131 container + 132 text 02:17 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 133 container + 134 text 02:17 PM Sending a guarded action + 135 container + 136 text 02:17 PM Action proposal accepted Restart inspection 02 + 137 container + 138 text 02:17 PM Checking the planned action + 139 container + 140 text 02:17 PM AI planning requested Restart inspection 02 + 141 container + 142 text 02:17 PM Camera observation received Restart inspection 02 + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 143 text 16 model requests · Reported tokens: 185,456 input / 2,222 output · Reporting incomplete (14 of 16 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/completed.ledger.txt b/jevdual/results/workbench-restarts-20261012/ui/completed.ledger.txt new file mode 100644 index 0000000..f1886a3 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/completed.ledger.txt @@ -0,0 +1,54 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 140 checkbox Restart inspection 01 Completed, Value: 0 + 141 checkbox Restart inspection 02 Completed, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 142 text Restart inspection 02 + 143 text Running + 16 button Pause, ID: pause + 17 container Description: Body connection, ID: network-card + 144 text Body connected + 145 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button Lose next reply, ID: fault-drop + 73 button Delay next reply, ID: fault-delay + 74 button Duplicate next command, ID: fault-duplicate + 146 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 147 text Last camera observation + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 148 text Captured · generation 20 + 149 text COMPLETED + 150 text Measurement recorded and reset verified. The bench is ready for another job. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 151 text Historical result + 132 container measurement-result + 152 text 1,729 + 134 text Orange pixels + 135 container + 153 text 4,802.8 + 137 text mm² + 138 text Apparent footprint + 139 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 154 button Repeat job, ID: repeat +[Main-panel excerpt; full browser capture is completed.txt] diff --git a/jevdual/results/workbench-restarts-20261012/ui/completed.txt b/jevdual/results/workbench-restarts-20261012/ui/completed.txt new file mode 100644 index 0000000..10318ae --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/completed.txt @@ -0,0 +1,87 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 140 checkbox Restart inspection 01 Completed, Value: 0 + 141 checkbox Restart inspection 02 Completed, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 142 text Restart inspection 02 + 143 text Running + 16 button Pause, ID: pause + 17 container Description: Body connection, ID: network-card + 144 text Body connected + 145 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button Lose next reply, ID: fault-drop + 73 button Delay next reply, ID: fault-delay + 74 button Duplicate next command, ID: fault-duplicate + 146 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 147 text Last camera observation + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 148 text Captured · generation 20 + 149 text COMPLETED + 150 text Measurement recorded and reset verified. The bench is ready for another job. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 151 text Historical result + 132 container measurement-result + 152 text 1,729 + 134 text Orange pixels + 135 container + 153 text 4,802.8 + 137 text mm² + 138 text Apparent footprint + 139 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 154 button Repeat job, ID: repeat + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 155 text 24 events + 118 content list activity + 156 container + 157 text 02:21 PM Action receipt checked Restart inspection 02 + 158 container + 159 text 02:21 PM Sending a guarded action + 160 container + 161 text 02:21 PM Action proposal accepted Restart inspection 02 + 162 container + 163 text 02:21 PM Checking the planned action + 164 container + 165 text 02:21 PM AI planning requested Restart inspection 02 + 166 container + 167 text 02:21 PM Camera observation received Restart inspection 02 + 168 container + 169 text 02:21 PM Requesting camera observation + 170 container + 171 text 02:21 PM Agent resumed + 172 container + 173 text 02:20 PM Agent stopped for review timed out + 174 container + 175 text 02:20 PM Agent stopped for review timed out + 176 container + 177 text 02:20 PM Agent stopped for review timed out + 178 container + 179 text 02:20 PM Agent stopped for review timed out + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 180 text 29 model requests · Reported tokens: 343,310 input / 3,963 output · Reporting incomplete (26 of 29 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/disconnected.txt b/jevdual/results/workbench-restarts-20261012/ui/disconnected.txt new file mode 100644 index 0000000..11d90ad --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/disconnected.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 container queue + 75 checkbox Restart inspection 01 Needs attention, Value: 1 + 76 checkbox Restart inspection 02 Queued, Value: 0 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 77 text Restart inspection 01 + 78 text Needs attention + 16 button (disabled) Pause, ID: pause + 79 text Restarted with a retained command stage; check its exact receipt + 17 container Description: Body connection, ID: network-card + 80 text Body disconnected + 81 text Diagnostic link disconnected; cached observations are retained + 20 button Reconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button (disabled) Lose next reply, ID: fault-drop + 73 button (disabled) Delay next reply, ID: fault-delay + 74 button (disabled) Duplicate next command, ID: fault-duplicate + 82 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 83 text Retained observation · body offline + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 84 text Retained · offline · generation 0 + 85 text DISCONNECTED + 86 text The body connection is unavailable. The last camera observation is retained. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 87 text Pending + 38 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 88 text 21 events + 43 content list activity + 89 container + 90 text 02:13 PM Body disconnected Transport only; no body reset or replay + 91 container + 92 text 02:12 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt + 93 container + 94 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 95 container + 96 text 02:12 PM Sending a guarded action + 97 container + 98 text 02:12 PM Action proposal accepted Restart inspection 01 + 99 container + 100 text 02:12 PM Checking the planned action + 101 container + 102 text 02:12 PM AI planning requested Restart inspection 01 + 103 container + 104 text 02:12 PM Camera observation received Restart inspection 01 + 105 container + 106 text 02:12 PM Requesting camera observation + 107 container + 108 text 02:12 PM Agent resumed + 109 container + 110 text 02:12 PM Reply fault armed The next command reply will be lost. + 111 container + 112 text 02:10 PM Agent stopped for review operator_pause_requested + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 113 text 2 model requests · Reported tokens: 13,063 input / 199 output · Reporting incomplete (1 of 2 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/handoff.jpg b/jevdual/results/workbench-restarts-20261012/ui/handoff.jpg new file mode 100644 index 0000000..ad7eff3 Binary files /dev/null and b/jevdual/results/workbench-restarts-20261012/ui/handoff.jpg differ diff --git a/jevdual/results/workbench-restarts-20261012/ui/handoff.txt b/jevdual/results/workbench-restarts-20261012/ui/handoff.txt new file mode 100644 index 0000000..484d03a --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/handoff.txt @@ -0,0 +1,81 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 9 checkbox Restart inspection 01 Completed, Value: 0 + 10 checkbox Restart inspection 02 Completed, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 14 text Restart inspection 02 + 15 text Paused + 16 button Resume, ID: pause + 17 container Description: Body connection, ID: network-card + 18 text Body connected + 19 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 22 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 23 text Test the simulated connection + 24 container Camera observation + 25 text OVERHEAD CAMERA + 26 text Last camera observation + 27 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 29 text Captured · generation 20 + 30 text COMPLETED + 31 text Measurement recorded and reset verified. The bench is ready for another job. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 37 text Historical result + 38 container measurement-result + 39 text 1,729 + 40 text Orange pixels + 41 container + 42 text 4,802.8 + 43 text mm² + 44 text Apparent footprint + 45 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 46 button Repeat job, ID: repeat + 47 container Activity + 48 heading Activity, Value: 2, ID: activity-heading + 49 text Activity + 50 text 24 events + 51 content list activity + 52 container + 53 text 02:23 PM Body restarted 3 -> 4 + 54 container + 55 text 02:23 PM Agent stopped for review Body restarted from boot 3 to 4; check pending receipts and explicitly Resume for a fresh image + 56 container + 57 text 02:21 PM Action receipt checked Restart inspection 02 + 58 container + 59 text 02:21 PM Sending a guarded action + 60 container + 61 text 02:21 PM Action proposal accepted Restart inspection 02 + 62 container + 63 text 02:21 PM Checking the planned action + 64 container + 65 text 02:21 PM AI planning requested Restart inspection 02 + 66 container + 67 text 02:21 PM Camera observation received Restart inspection 02 + 68 container + 69 text 02:21 PM Requesting camera observation + 70 container + 71 text 02:21 PM Agent resumed + 72 container + 73 text 02:20 PM Agent stopped for review timed out + 74 container + 75 text 02:20 PM Agent stopped for review timed out + 76 button (collapsed) Session details, Secondary Actions: Expand + 77 text Session details + 78 text 29 model requests · Reported tokens: 343,310 input / 3,963 output · Reporting incomplete (26 of 29 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/reconnected.txt b/jevdual/results/workbench-restarts-20261012/ui/reconnected.txt new file mode 100644 index 0000000..60158fc --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/reconnected.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 container queue + 75 checkbox Restart inspection 01 Needs attention, Value: 1 + 76 checkbox Restart inspection 02 Queued, Value: 0 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 77 text Restart inspection 01 + 78 text Needs attention + 16 button (disabled) Pause, ID: pause + 79 text Restarted with a retained command stage; check its exact receipt + 17 container Description: Body connection, ID: network-card + 80 text Body connected + 81 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button (disabled) Lose next reply, ID: fault-drop + 73 button (disabled) Delay next reply, ID: fault-delay + 74 button (disabled) Duplicate next command, ID: fault-duplicate + 82 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 83 text Last observation · Restart inspection 01 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 84 text Captured · generation 0 + 85 text OUTCOME UNCERTAIN + 86 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 87 text Pending + 38 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 88 text 22 events + 43 content list activity + 89 container + 90 text 02:13 PM Body reconnecting No command sent or replayed + 91 container + 92 text 02:13 PM Body disconnected Transport only; no body reset or replay + 93 container + 94 text 02:12 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt + 95 container + 96 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 97 container + 98 text 02:12 PM Sending a guarded action + 99 container + 100 text 02:12 PM Action proposal accepted Restart inspection 01 + 101 container + 102 text 02:12 PM Checking the planned action + 103 container + 104 text 02:12 PM AI planning requested Restart inspection 01 + 105 container + 106 text 02:12 PM Camera observation received Restart inspection 01 + 107 container + 108 text 02:12 PM Requesting camera observation + 109 container + 110 text 02:12 PM Agent resumed + 111 container + 112 text 02:12 PM Reply fault armed The next command reply will be lost. + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 113 text 2 model requests · Reported tokens: 13,063 input / 199 output · Reporting incomplete (1 of 2 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/recovered.txt b/jevdual/results/workbench-restarts-20261012/ui/recovered.txt new file mode 100644 index 0000000..c2a4669 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/recovered.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 75 checkbox Restart inspection 01 Paused, Value: 1 + 76 checkbox Restart inspection 02 Queued, Value: 0 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 77 text Restart inspection 01 + 78 text Paused + 16 button Resume, ID: pause + 79 text Receipt recovered. The agent is paused; choose Resume to observe the bench again and continue. + 17 container Description: Body connection, ID: network-card + 80 text Body connected + 81 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button Lose next reply, ID: fault-drop + 73 button Delay next reply, ID: fault-delay + 74 button Duplicate next command, ID: fault-duplicate + 82 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 83 text Last observation · Restart inspection 01 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 84 text Captured · generation 0 + 85 text RECEIPT RECOVERED + 86 text The exact command receipt was recovered. The agent is paused until you resume. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 87 text Pending + 38 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 88 text 24 events + 90 content list activity + 91 container + 92 text 02:13 PM Command receipt recovered The agent remains paused until you resume. + 93 container + 94 text 02:13 PM Action receipt checked Restart inspection 01 + 95 container + 96 text 02:13 PM Body reconnecting No command sent or replayed + 97 container + 98 text 02:13 PM Body disconnected Transport only; no body reset or replay + 99 container + 100 text 02:12 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt + 101 container + 102 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 103 container + 104 text 02:12 PM Sending a guarded action + 105 container + 106 text 02:12 PM Action proposal accepted Restart inspection 01 + 107 container + 108 text 02:12 PM Checking the planned action + 109 container + 110 text 02:12 PM AI planning requested Restart inspection 01 + 111 container + 112 text 02:12 PM Camera observation received Restart inspection 01 + 113 container + 114 text 02:12 PM Requesting camera observation + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 89 text 2 model requests · Reported tokens: 13,063 input / 199 output · Reporting incomplete (1 of 2 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/reloaded.ledger.txt b/jevdual/results/workbench-restarts-20261012/ui/reloaded.ledger.txt new file mode 100644 index 0000000..e8de9a0 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/reloaded.ledger.txt @@ -0,0 +1,48 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 9 checkbox Restart inspection 01 Completed, Value: 0 + 10 checkbox Restart inspection 02 Completed, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 14 text Restart inspection 02 + 15 text Running + 16 button Pause, ID: pause + 17 container Description: Body connection, ID: network-card + 18 text Body connected + 19 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 22 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 23 text Test the simulated connection + 24 container Camera observation + 25 text OVERHEAD CAMERA + 26 text Last camera observation + 27 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 29 text Captured · generation 20 + 30 text COMPLETED + 31 text Measurement recorded and reset verified. The bench is ready for another job. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 37 text Historical result + 38 container measurement-result + 39 text 1,729 + 40 text Orange pixels + 41 container + 42 text 4,802.8 + 43 text mm² + 44 text Apparent footprint + 45 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 46 button Repeat job, ID: repeat +[Main-panel excerpt; full browser capture is reloaded.txt] diff --git a/jevdual/results/workbench-restarts-20261012/ui/reloaded.txt b/jevdual/results/workbench-restarts-20261012/ui/reloaded.txt new file mode 100644 index 0000000..b4d3765 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/reloaded.txt @@ -0,0 +1,81 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 9 checkbox Restart inspection 01 Completed, Value: 0 + 10 checkbox Restart inspection 02 Completed, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 14 text Restart inspection 02 + 15 text Running + 16 button Pause, ID: pause + 17 container Description: Body connection, ID: network-card + 18 text Body connected + 19 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 22 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 23 text Test the simulated connection + 24 container Camera observation + 25 text OVERHEAD CAMERA + 26 text Last camera observation + 27 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 29 text Captured · generation 20 + 30 text COMPLETED + 31 text Measurement recorded and reset verified. The bench is ready for another job. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 37 text Historical result + 38 container measurement-result + 39 text 1,729 + 40 text Orange pixels + 41 container + 42 text 4,802.8 + 43 text mm² + 44 text Apparent footprint + 45 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 46 button Repeat job, ID: repeat + 47 container Activity + 48 heading Activity, Value: 2, ID: activity-heading + 49 text Activity + 50 text 24 events + 51 content list activity + 52 container + 53 text 02:21 PM Action receipt checked Restart inspection 02 + 54 container + 55 text 02:21 PM Sending a guarded action + 56 container + 57 text 02:21 PM Action proposal accepted Restart inspection 02 + 58 container + 59 text 02:21 PM Checking the planned action + 60 container + 61 text 02:21 PM AI planning requested Restart inspection 02 + 62 container + 63 text 02:21 PM Camera observation received Restart inspection 02 + 64 container + 65 text 02:21 PM Requesting camera observation + 66 container + 67 text 02:21 PM Agent resumed + 68 container + 69 text 02:20 PM Agent stopped for review timed out + 70 container + 71 text 02:20 PM Agent stopped for review timed out + 72 container + 73 text 02:20 PM Agent stopped for review timed out + 74 container + 75 text 02:20 PM Agent stopped for review timed out + 76 button (collapsed) Session details, Secondary Actions: Expand + 77 text Session details + 78 text 29 model requests · Reported tokens: 343,310 input / 3,963 output · Reporting incomplete (26 of 29 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/resumed.txt b/jevdual/results/workbench-restarts-20261012/ui/resumed.txt new file mode 100644 index 0000000..adcdb1c --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/resumed.txt @@ -0,0 +1,79 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 91 checkbox Restart inspection 01 Paused, Value: 1 + 92 checkbox Restart inspection 02 Queued, Value: 0 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 93 text Restart inspection 01 + 94 text Running + 16 button Pause, ID: pause + 17 container Description: Body connection, ID: network-card + 95 text Body connected + 96 text Current body boot verified for explicit Resume + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button Lose next reply, ID: fault-drop + 73 button Delay next reply, ID: fault-delay + 74 button Duplicate next command, ID: fault-duplicate + 97 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 98 text Last observation · Restart inspection 01 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 99 text Captured · generation 0 + 100 text AGENT + 101 text Push the block right so its center enters the measurement zone. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 102 text Pending + 38 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 103 text 24 events + 90 content list activity + 104 container + 105 text 02:13 PM Requesting camera observation + 106 container + 107 text 02:13 PM Agent resumed + 108 container + 109 text 02:13 PM Command receipt recovered The agent remains paused until you resume. + 110 container + 111 text 02:13 PM Action receipt checked Restart inspection 01 + 112 container + 113 text 02:13 PM Body reconnecting No command sent or replayed + 114 container + 115 text 02:13 PM Body disconnected Transport only; no body reset or replay + 116 container + 117 text 02:12 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt + 118 container + 119 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 120 container + 121 text 02:12 PM Sending a guarded action + 122 container + 123 text 02:12 PM Action proposal accepted Restart inspection 01 + 124 container + 125 text 02:12 PM Checking the planned action + 126 container + 127 text 02:12 PM AI planning requested Restart inspection 01 + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 128 text 2 model requests · Reported tokens: 13,063 input / 199 output · Reporting incomplete (1 of 2 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/transient-health-pause.jpg b/jevdual/results/workbench-restarts-20261012/ui/transient-health-pause.jpg new file mode 100644 index 0000000..29e23a6 Binary files /dev/null and b/jevdual/results/workbench-restarts-20261012/ui/transient-health-pause.jpg differ diff --git a/jevdual/results/workbench-restarts-20261012/ui/transient-health-pause.txt b/jevdual/results/workbench-restarts-20261012/ui/transient-health-pause.txt new file mode 100644 index 0000000..1f5e63a --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/transient-health-pause.txt @@ -0,0 +1,87 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 119 checkbox Restart inspection 01 Completed, Value: 0 + 120 checkbox Restart inspection 02 Paused, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 121 text Restart inspection 02 + 122 text Paused + 16 button Resume, ID: pause + 123 text timed out + 17 container Description: Body connection, ID: network-card + 124 text Body connected + 125 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button Lose next reply, ID: fault-drop + 73 button Delay next reply, ID: fault-delay + 74 button Duplicate next command, ID: fault-duplicate + 126 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 127 text Last observation · Restart inspection 02 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 128 text Captured · generation 20 + 129 text PAUSED + 130 text Paused. Resume when you are ready for the next action. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 131 text Recorded + 132 container measurement-result + 133 text 1,729 + 134 text Orange pixels + 135 container + 136 text 4,802.8 + 137 text mm² + 138 text Apparent footprint + 139 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 140 text 24 events + 118 content list activity + 141 container + 142 text 02:20 PM Agent stopped for review timed out + 143 container + 144 text 02:20 PM Agent stopped for review timed out + 145 container + 146 text 02:20 PM Agent stopped for review timed out + 147 container + 148 text 02:20 PM Agent stopped for review timed out + 149 container + 150 text 02:20 PM Agent stopped for review operator_pause_requested + 151 container + 152 text 02:20 PM Agent stopped for review timed out + 153 container + 154 text 02:19 PM AI planning requested Restart inspection 02 + 155 container + 156 text 02:19 PM Camera observation received Restart inspection 02 + 157 container + 158 text 02:19 PM Requesting camera observation + 159 container + 160 text 02:19 PM Action receipt checked Restart inspection 02 + 161 container + 162 text 02:19 PM Sending a guarded action + 163 container + 164 text 02:19 PM Action proposal accepted Restart inspection 02 + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 165 text 28 model requests · Reported tokens: 329,774 input / 3,851 output · Reporting incomplete (25 of 28 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/transient-health-resumed.txt b/jevdual/results/workbench-restarts-20261012/ui/transient-health-resumed.txt new file mode 100644 index 0000000..7523bf6 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/transient-health-resumed.txt @@ -0,0 +1,87 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 140 checkbox Restart inspection 01 Completed, Value: 0 + 141 checkbox Restart inspection 02 Paused, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 142 text Restart inspection 02 + 143 text Running + 16 button Pause, ID: pause + 144 text timed out + 17 container Description: Body connection, ID: network-card + 145 text Body connected + 146 text Current body boot verified for explicit Resume + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button Lose next reply, ID: fault-drop + 73 button Delay next reply, ID: fault-delay + 74 button Duplicate next command, ID: fault-duplicate + 147 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 148 text Last observation · Restart inspection 02 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 149 text Captured · generation 20 + 150 text AGENT + 151 text Lower the pusher from the bypass lane to its home position while the block remains centered in the home zone. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 152 text Recorded + 132 container measurement-result + 153 text 1,729 + 134 text Orange pixels + 135 container + 154 text 4,802.8 + 137 text mm² + 138 text Apparent footprint + 139 text Image-based area in the calibrated simulation. This is a recorded measurement, separate from the AI’s judgment. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 155 text 24 events + 118 content list activity + 156 container + 157 text 02:21 PM Agent resumed + 158 container + 159 text 02:20 PM Agent stopped for review timed out + 160 container + 161 text 02:20 PM Agent stopped for review timed out + 162 container + 163 text 02:20 PM Agent stopped for review timed out + 164 container + 165 text 02:20 PM Agent stopped for review timed out + 166 container + 167 text 02:20 PM Agent stopped for review operator_pause_requested + 168 container + 169 text 02:20 PM Agent stopped for review timed out + 170 container + 171 text 02:19 PM AI planning requested Restart inspection 02 + 172 container + 173 text 02:19 PM Camera observation received Restart inspection 02 + 174 container + 175 text 02:19 PM Requesting camera observation + 176 container + 177 text 02:19 PM Action receipt checked Restart inspection 02 + 178 container + 179 text 02:19 PM Sending a guarded action + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 180 text 28 model requests · Reported tokens: 329,774 input / 3,851 output · Reporting incomplete (25 of 28 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/uncertain.txt b/jevdual/results/workbench-restarts-20261012/ui/uncertain.txt new file mode 100644 index 0000000..a1077dc --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/uncertain.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 container queue + 75 checkbox Restart inspection 01 Needs attention, Value: 1 + 76 checkbox Restart inspection 02 Queued, Value: 0 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 77 text Restart inspection 01 + 78 text Needs attention + 16 button (disabled) Pause, ID: pause + 79 text reply is uncertain; reconcile exact command without resending + 17 container Description: Body connection, ID: network-card + 80 text Body connected + 81 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button (disabled) Lose next reply, ID: fault-drop + 73 button (disabled) Delay next reply, ID: fault-delay + 74 button (disabled) Duplicate next command, ID: fault-duplicate + 82 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 83 text Last observation · Restart inspection 01 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 84 text Captured · generation 0 + 85 text OUTCOME UNCERTAIN + 86 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 87 text Pending + 38 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 88 text 19 events + 43 content list activity + 89 container + 90 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 91 container + 92 text 02:12 PM Sending a guarded action + 93 container + 94 text 02:12 PM Action proposal accepted Restart inspection 01 + 95 container + 96 text 02:12 PM Checking the planned action + 97 container + 98 text 02:12 PM AI planning requested Restart inspection 01 + 99 container + 100 text 02:12 PM Camera observation received Restart inspection 01 + 101 container + 102 text 02:12 PM Requesting camera observation + 103 container + 104 text 02:12 PM Agent resumed + 105 container + 106 text 02:12 PM Reply fault armed The next command reply will be lost. + 107 container + 108 text 02:10 PM Agent stopped for review operator_pause_requested + 109 container + 110 text 02:10 PM AI planning requested Restart inspection 01 + 111 container + 112 text 02:10 PM Camera observation received Restart inspection 01 + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 113 text 2 model requests · Reported tokens: 13,063 input / 199 output · Reporting incomplete (1 of 2 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/workbench-planning-offline.txt b/jevdual/results/workbench-restarts-20261012/ui/workbench-planning-offline.txt new file mode 100644 index 0000000..815a9e3 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/workbench-planning-offline.txt @@ -0,0 +1,68 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 container queue + 52 checkbox Restart inspection 01 Working, Value: 0 + 53 checkbox Restart inspection 02 Queued, Value: 1 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 54 text Restart inspection 02 + 55 text Disconnected + 16 button (disabled) Pause, ID: pause + 56 text Failed to fetch + 17 container Description: Body connection, ID: network-card + 57 text Body connected + 58 text Body identity verified by read-only TCP health + 20 button (disabled) Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 22 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 23 text Test the simulated connection + 24 container Camera observation + 25 text OVERHEAD CAMERA + 59 text Last observation · Restart inspection 01 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 61 text Last captured · generation 0 · disconnected + 62 text QUEUED + 63 text This job is queued. The agent will start when the bench is ready. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 64 text Pending + 38 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 65 text 9 events + 43 content list activity + 66 container + 67 text 02:10 PM AI planning requested Restart inspection 01 + 68 container + 69 text 02:10 PM Camera observation received Restart inspection 01 + 70 container + 71 text 02:10 PM Requesting camera observation + 72 container + 73 text 02:10 PM Inspection started Restart inspection 01 + 74 container + 75 text 02:10 PM Preparing inspection + 76 container + 77 text 02:10 PM Agent resumed + 78 container + 79 text 02:10 PM Job queued Restart inspection 02 + 80 container + 81 text 02:10 PM Job queued Restart inspection 01 + 82 container + 83 text 02:10 PM Pause requested Pause requested; a command already admitted by this host may finish within body bounds + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 84 text 1 model requests · Reported tokens: 0 input / 0 output · Reporting incomplete (0 of 1 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/workbench-planning-restored.txt b/jevdual/results/workbench-restarts-20261012/ui/workbench-planning-restored.txt new file mode 100644 index 0000000..0c91d83 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/workbench-planning-restored.txt @@ -0,0 +1,70 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 61 checkbox Restart inspection 01 Paused, Value: 1 + 62 checkbox Restart inspection 02 Queued, Value: 0 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 63 text Restart inspection 01 + 64 text Paused + 16 button Resume, ID: pause + 65 text operator_pause_requested + 17 container Description: Body connection, ID: network-card + 66 text Body connected + 67 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 22 button (collapsed) Test the simulated connection, Secondary Actions: Expand + 23 text Test the simulated connection + 24 container Camera observation + 25 text OVERHEAD CAMERA + 68 text Last observation · Restart inspection 01 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 69 text Captured · generation 0 + 70 text PAUSED + 71 text Paused. Resume when you are ready for the next action. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 72 text Pending + 38 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 73 text 10 events + 43 content list activity + 74 container + 75 text 02:10 PM Agent stopped for review operator_pause_requested + 76 container + 77 text 02:10 PM AI planning requested Restart inspection 01 + 78 container + 79 text 02:10 PM Camera observation received Restart inspection 01 + 80 container + 81 text 02:10 PM Requesting camera observation + 82 container + 83 text 02:10 PM Inspection started Restart inspection 01 + 84 container + 85 text 02:10 PM Preparing inspection + 86 container + 87 text 02:10 PM Agent resumed + 88 container + 89 text 02:10 PM Job queued Restart inspection 02 + 90 container + 91 text 02:10 PM Job queued Restart inspection 01 + 92 container + 93 text 02:10 PM Pause requested Pause requested; a command already admitted by this host may finish within body bounds + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 94 text 1 model requests · Reported tokens: 0 input / 0 output · Reporting incomplete (0 of 1 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/workbench-planning-resumed.txt b/jevdual/results/workbench-restarts-20261012/ui/workbench-planning-resumed.txt new file mode 100644 index 0000000..18dd46a --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/workbench-planning-resumed.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button + New job, ID: new-job + 8 container queue + 61 checkbox Restart inspection 01 Paused, Value: 1 + 62 checkbox Restart inspection 02 Queued, Value: 0 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 63 text Restart inspection 01 + 64 text Running + 16 button Pause, ID: pause + 65 text operator_pause_requested + 17 container Description: Body connection, ID: network-card + 66 text Body connected + 67 text Current body boot verified for explicit Resume + 20 button Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button (disabled) Lose next reply, ID: fault-drop + 73 button (disabled) Delay next reply, ID: fault-delay + 74 button (disabled) Duplicate next command, ID: fault-duplicate + 75 text The next command reply will be lost. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 76 text Last observation · Restart inspection 01 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 77 text Captured · generation 0 + 78 text AGENT + 79 text Waiting for the agent’s next observation. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 80 text Pending + 38 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 81 text 12 events + 43 content list activity + 82 container + 83 text 02:12 PM Agent resumed + 84 container + 85 text 02:12 PM Reply fault armed The next command reply will be lost. + 86 container + 87 text 02:10 PM Agent stopped for review operator_pause_requested + 88 container + 89 text 02:10 PM AI planning requested Restart inspection 01 + 90 container + 91 text 02:10 PM Camera observation received Restart inspection 01 + 92 container + 93 text 02:10 PM Requesting camera observation + 94 container + 95 text 02:10 PM Inspection started Restart inspection 01 + 96 container + 97 text 02:10 PM Preparing inspection + 98 container + 99 text 02:10 PM Agent resumed + 100 container + 101 text 02:10 PM Job queued Restart inspection 02 + 102 container + 103 text 02:10 PM Job queued Restart inspection 01 + 104 container + 105 text 02:10 PM Pause requested Pause requested; a command already admitted by this host may finish within body bounds + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 106 text 1 model requests · Reported tokens: 0 input / 0 output · Reporting incomplete (0 of 1 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/workbench-uncertain-offline.txt b/jevdual/results/workbench-restarts-20261012/ui/workbench-uncertain-offline.txt new file mode 100644 index 0000000..918569e --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/workbench-uncertain-offline.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 container queue + 75 checkbox Restart inspection 01 Needs attention, Value: 1 + 76 checkbox Restart inspection 02 Queued, Value: 0 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 77 text Restart inspection 01 + 78 text Disconnected + 16 button (disabled) Pause, ID: pause + 79 text Failed to fetch + 17 container Description: Body connection, ID: network-card + 80 text Body connected + 81 text Body identity verified by read-only TCP health + 20 button (disabled) Disconnect, ID: disconnect + 21 button (disabled) Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button (disabled) Lose next reply, ID: fault-drop + 73 button (disabled) Delay next reply, ID: fault-delay + 74 button (disabled) Duplicate next command, ID: fault-duplicate + 82 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 83 text Last observation · Restart inspection 01 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 84 text Last captured · generation 0 · disconnected + 85 text OUTCOME UNCERTAIN + 86 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 87 text Pending + 38 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 88 text 19 events + 43 content list activity + 89 container + 90 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 91 container + 92 text 02:12 PM Sending a guarded action + 93 container + 94 text 02:12 PM Action proposal accepted Restart inspection 01 + 95 container + 96 text 02:12 PM Checking the planned action + 97 container + 98 text 02:12 PM AI planning requested Restart inspection 01 + 99 container + 100 text 02:12 PM Camera observation received Restart inspection 01 + 101 container + 102 text 02:12 PM Requesting camera observation + 103 container + 104 text 02:12 PM Agent resumed + 105 container + 106 text 02:12 PM Reply fault armed The next command reply will be lost. + 107 container + 108 text 02:10 PM Agent stopped for review operator_pause_requested + 109 container + 110 text 02:10 PM AI planning requested Restart inspection 01 + 111 container + 112 text 02:10 PM Camera observation received Restart inspection 01 + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 113 text 2 model requests · Reported tokens: 13,063 input / 199 output · Reporting incomplete (1 of 2 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/results/workbench-restarts-20261012/ui/workbench-uncertain-restored.jpg b/jevdual/results/workbench-restarts-20261012/ui/workbench-uncertain-restored.jpg new file mode 100644 index 0000000..02280e0 Binary files /dev/null and b/jevdual/results/workbench-restarts-20261012/ui/workbench-uncertain-restored.jpg differ diff --git a/jevdual/results/workbench-restarts-20261012/ui/workbench-uncertain-restored.txt b/jevdual/results/workbench-restarts-20261012/ui/workbench-uncertain-restored.txt new file mode 100644 index 0000000..3e6b618 --- /dev/null +++ b/jevdual/results/workbench-restarts-20261012/ui/workbench-uncertain-restored.txt @@ -0,0 +1,80 @@ +Browser tab: 4, Title: "Footwork · Network bench", URL: "http://127.0.0.1:51377/". +1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ + 2 container + 3 text Footwork Bench + 4 text SIMULATION + 5 container Jobs + 6 text WORK QUEUE + 7 button (disabled) + New job, ID: new-job + 8 container queue + 75 checkbox Restart inspection 01 Needs attention, Value: 1 + 76 checkbox Restart inspection 02 Queued, Value: 0 + 11 container + 12 heading Bench, Value: 1 + 13 text Bench + 77 text Restart inspection 01 + 78 text Needs attention + 16 button (disabled) Pause, ID: pause + 79 text Restarted with a retained command stage; check its exact receipt + 17 container Description: Body connection, ID: network-card + 80 text Body connected + 81 text Body identity verified by read-only TCP health + 20 button Disconnect, ID: disconnect + 21 button Check receipt, ID: reconcile + 68 container + 69 button (expanded) Test the simulated connection, Secondary Actions: Collapse + 70 text Test the simulated connection + 71 text These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume. + 72 button (disabled) Lose next reply, ID: fault-drop + 73 button (disabled) Delay next reply, ID: fault-delay + 74 button (disabled) Duplicate next command, ID: fault-duplicate + 82 text No reply fault armed. + 24 container Camera observation + 25 text OVERHEAD CAMERA + 83 text Last observation · Restart inspection 01 + 60 image Description: Rendered overhead view of the orange part, blue pusher and green task region, ID: frame + 28 text Orange part Blue pusher Green task region + 84 text Captured · generation 0 + 85 text OUTCOME UNCERTAIN + 86 text The command outcome is uncertain. Reconnect if needed, then check its receipt before continuing. + 32 container Job progress + 33 text 1 Prepare 2 Measure 3 Reset 4 Ready + 34 container Measurement + 35 heading Measurement, Value: 2, ID: measurement-heading + 36 text Measurement + 87 text Pending + 38 text The part’s visible footprint will be measured after its placement is verified. + 39 container Activity + 40 heading Activity, Value: 2, ID: activity-heading + 41 text Activity + 88 text 20 events + 43 content list activity + 89 container + 90 text 02:12 PM Agent stopped for review Restarted with a retained command stage; check its exact receipt + 91 container + 92 text 02:12 PM Agent stopped for review reply is uncertain; reconcile exact command without resending + 93 container + 94 text 02:12 PM Sending a guarded action + 95 container + 96 text 02:12 PM Action proposal accepted Restart inspection 01 + 97 container + 98 text 02:12 PM Checking the planned action + 99 container + 100 text 02:12 PM AI planning requested Restart inspection 01 + 101 container + 102 text 02:12 PM Camera observation received Restart inspection 01 + 103 container + 104 text 02:12 PM Requesting camera observation + 105 container + 106 text 02:12 PM Agent resumed + 107 container + 108 text 02:12 PM Reply fault armed The next command reply will be lost. + 109 container + 110 text 02:10 PM Agent stopped for review operator_pause_requested + 111 container + 112 text 02:10 PM AI planning requested Restart inspection 01 + 50 button (collapsed) Session details, Secondary Actions: Expand + 51 text Session details + 113 text 2 model requests · Reported tokens: 13,063 input / 199 output · Reporting incomplete (1 of 2 requests) + +The focused UI element is 1 AXWebArea Footwork · Network bench, URL: 127.0.0.1:51377/ \ No newline at end of file diff --git a/jevdual/scripts/build_onboarding_packet.py b/jevdual/scripts/build_onboarding_packet.py new file mode 100644 index 0000000..8369a4b --- /dev/null +++ b/jevdual/scripts/build_onboarding_packet.py @@ -0,0 +1,240 @@ +"""Build a transportable, non-executing U2 qualification packet from fixed files.""" + +from __future__ import annotations + +import argparse +import hashlib +import io +import json +import os +import re +import stat +import subprocess +import tempfile +import zipfile +from pathlib import Path, PurePosixPath + +from build_capability_wheel import verify as verify_wheel + +PROJECT = Path(__file__).resolve().parents[2] +HELPERS = ( + "verify_onboarding_packet.py", + "capability_clean_install.py", + "capability_fixture.py", + "capability_fixture_config.py", + "capability_host_trial.py", + "capability_interrupt_trial.py", + "capability_restart_trial.py", + "capability_installed_trial.py", +) + + +def sha(data: bytes) -> str: + return hashlib.sha256(data).hexdigest() + + +def snapshot(path: Path, *, limit: int = 16 * 1024 * 1024) -> bytes: + """Read one bounded regular file without following its final symlink.""" + try: + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK) + try: + source = os.fdopen(fd, "rb") + except OSError: + os.close(fd) + raise + with source: + before = os.fstat(source.fileno()) + if not stat.S_ISREG(before.st_mode) or before.st_size > limit: + raise ValueError("Artifact must be a bounded regular nonsymlink file") + data = source.read(limit + 1) + after = os.fstat(source.fileno()) + identity = lambda s: (s.st_dev, s.st_ino, s.st_size, s.st_mtime_ns, s.st_ctime_ns) + if len(data) != before.st_size or len(data) > limit or identity(before) != identity(after): + raise ValueError("Artifact changed during snapshot") + return data + except OSError as exc: + raise ValueError("Artifact must be a readable regular nonsymlink file") from exc + + +def parse_archive(data: bytes, *, prefix: str | None = "footwork") -> dict[str, bytes]: + """Parse the same frozen bytes that are hashed and compared; never extract.""" + if len(data) > 16 * 1024 * 1024: + raise ValueError("Archive exceeds bounded candidate size") + files = {} + total = 0 + try: + with zipfile.ZipFile(io.BytesIO(data)) as archive: + for entry in archive.infolist(): + name = entry.filename + parts = PurePosixPath(name) + mode = entry.external_attr >> 16 + if ( + name in files + or not parts.parts + or parts.is_absolute() + or ".." in parts.parts + or parts.as_posix() != name + or (prefix is not None and (len(parts.parts) < 2 or parts.parts[0] != prefix)) + or "\\" in name + or entry.is_dir() + or stat.S_IFMT(mode) not in (0, stat.S_IFREG) + ): + raise ValueError("Unsafe or duplicate archive member") + total += entry.file_size + if entry.file_size > 8 * 1024 * 1024 or total > 16 * 1024 * 1024: + raise ValueError("Archive exceeds bounded candidate size") + files[name] = archive.read(entry) + except zipfile.BadZipFile as exc: + raise ValueError("Malformed archive") from exc + return files + + +def commit_identity(commit: str | None, project: Path) -> str: + commit = commit or subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=project, text=True).strip() + if not re.fullmatch(r"[0-9a-f]{40}", commit): + raise ValueError("A full immutable source commit is required") + try: + kind = subprocess.check_output(["git", "cat-file", "-t", commit], cwd=project, stderr=subprocess.PIPE) + except subprocess.CalledProcessError as exc: + raise ValueError("Source identity must resolve to a commit object") from exc + if kind.strip() != b"commit": + raise ValueError("Source identity must resolve to a commit object") + return commit + + +def committed_bytes(commit: str, relative: str, project: Path) -> bytes: + commit = commit_identity(commit, project) + entries = subprocess.check_output( + ["git", "ls-tree", "-z", commit, "--", relative], cwd=project, stderr=subprocess.PIPE + ).split(b"\0") + entries = [entry for entry in entries if entry] + if len(entries) != 1: + raise ValueError("Committed source file is absent: " + relative) + metadata, name = entries[0].split(b"\t", 1) + mode, kind, blob = metadata.split() + if mode not in (b"100644", b"100755") or kind != b"blob" or name.decode() != relative: + raise ValueError("Committed source must be a regular file") + return subprocess.check_output( + ["git", "cat-file", "blob", blob.decode()], cwd=project, stderr=subprocess.PIPE + ) + + +def verify_committed_wheel(name: str, data: bytes, commit: str, project: Path) -> None: + """Verify runtime bytes against the requested immutable source, not the checkout.""" + commit = commit_identity(commit, project) + parse_archive(data, prefix=None) + output = project / "output" + output.mkdir(exist_ok=True) + with tempfile.TemporaryDirectory(prefix="packet-wheel-check-", dir=output) as temporary: + root = Path(temporary) + package = root / "jevdual" + package.mkdir() + entries = subprocess.check_output( + ["git", "ls-tree", "-r", "-z", commit, "--", "jevdual/python/jevdual"], cwd=project + ).split(b"\0") + paths = ["LICENSE", "jevdual/pyproject.toml"] + for entry in entries: + if not entry: + continue + relative = entry.split(b"\t", 1)[1].decode() + if relative.endswith(".py") or relative == "jevdual/python/jevdual/arbiter.toml": + paths.append(relative) + for relative in paths: + target = root / relative + target.parent.mkdir(parents=True, exist_ok=True) + target.write_bytes(committed_bytes(commit, relative, project)) + wheel = root / Path(name).name + wheel.write_bytes(data) + verify_wheel(wheel, package) + + +def inventory(wheel: Path, source_commit: str | None = None) -> dict[str, bytes]: + source_commit = commit_identity(source_commit, PROJECT) + wheel_data = snapshot(wheel, limit=8 * 1024 * 1024) + verify_committed_wheel(wheel.name, wheel_data, source_commit, PROJECT) + sources = { + "footwork/LICENSE": PROJECT / "LICENSE", + "footwork/README.md": PROJECT / "jevdual/docs/independent-onboarding.md", + "footwork/vm-plan.md": PROJECT / "jevdual/docs/onboarding-vm-plan.md", + "footwork/installation.md": PROJECT / "jevdual/docs/capability-packaging.md", + "footwork/artifacts/" + wheel.name: wheel, + } + sources.update( + {"footwork/jevdual/scripts/" + name: PROJECT / "jevdual/scripts" / name for name in HELPERS} + ) + files = {} + for name, path in sources.items(): + if path == wheel: + files[name] = wheel_data + continue + relative = path.relative_to(PROJECT).as_posix() + files[name] = committed_bytes(source_commit, relative, PROJECT) + manifest = { + "schema_version": "1", + "source_commit": source_commit, + "wheel_sha256": sha(wheel_data), + "qualification": "not_performed", + "automatic_execution": False, + "members": {name: sha(data) for name, data in sorted(files.items())}, + } + files["footwork/MANIFEST.json"] = (json.dumps(manifest, indent=2) + "\n").encode() + return files + + +def verify(packet: Path, wheel: Path, source_commit: str | None = None) -> dict: + expected = inventory(wheel, source_commit) + packet_data = snapshot(packet) + actual = parse_archive(packet_data) + if set(actual) != set(expected): + raise ValueError("Packet inventory differs from its permitted files") + for name, data in expected.items(): + if actual[name] != data: + raise ValueError("Packet member differs: " + name) + manifest = json.loads(actual["footwork/MANIFEST.json"]) + for name, digest in manifest["members"].items(): + if sha(actual[name]) != digest: + raise ValueError("Packet manifest digest differs") + return { + "verified": True, + "members": len(expected), + "sha256": sha(packet_data), + "source_commit": manifest["source_commit"], + } + + +def build(wheel: Path, output: Path) -> Path: + files = inventory(wheel) + output.mkdir(parents=True, exist_ok=False, mode=0o700) + packet = output / "footwork-onboarding-u2.zip" + with ( + packet.open("xb") as stream, + zipfile.ZipFile(stream, "w", compression=zipfile.ZIP_DEFLATED) as archive, + ): + for name, data in sorted(files.items()): + info = zipfile.ZipInfo(name, (1980, 1, 1, 0, 0, 0)) + info.compress_type = zipfile.ZIP_DEFLATED + info.create_system = 3 + info.external_attr = 0o100644 << 16 + archive.writestr(info, data) + verify(packet, wheel, json.loads(files["footwork/MANIFEST.json"])["source_commit"]) + return packet + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--wheel", type=Path, required=True) + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument("--output", type=Path) + group.add_argument("--verify", type=Path) + parser.add_argument("--source-commit", help="Expected frozen commit for verification after later commits") + args = parser.parse_args() + if args.output and args.source_commit: + parser.error("Builds record HEAD; --source-commit is for verification only") + wheel = args.wheel.absolute() + packet = args.verify or build(wheel, args.output) + print(json.dumps({"packet": str(packet.resolve()), **verify(packet, wheel, args.source_commit)})) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/jevdual/scripts/build_release_candidate.py b/jevdual/scripts/build_release_candidate.py new file mode 100644 index 0000000..12e7f59 --- /dev/null +++ b/jevdual/scripts/build_release_candidate.py @@ -0,0 +1,340 @@ +"""Build or verify a deterministic, reviewed local capability release candidate. + +Offline only: preserve the qualified wheel/helpers, read release documentation +and evidence from an immutable commit, and never install or execute the product. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +import zipfile +from pathlib import Path + +from build_onboarding_packet import HELPERS, parse_archive, snapshot +from build_onboarding_packet import commit_identity as packet_commit_identity +from build_onboarding_packet import committed_bytes as packet_committed_bytes +from build_onboarding_packet import verify_committed_wheel as verify_packet_wheel + +PROJECT = Path(__file__).resolve().parents[2] +QUALIFIED_PACKET_SHA = "205577ee0848e9e68c8a15a9f25e1d308e820a054be449f631274ba7a9c7151d" +QUALIFIED_PRODUCT_COMMIT = "445a348ff31243c2f3d90baae9ce209d89bd7eac" +ARCHIVE_NAME = "footwork-capability-0.0.1-macos-arm64-candidate.zip" +DOCS = { + "footwork/README.md": "jevdual/docs/release-candidate.md", + "footwork/installation.md": "jevdual/docs/capability-packaging.md", + "footwork/agent-host.md": "jevdual/docs/installed-agent-host.md", +} +DOCUMENT_LINKS = { + "../results/clean-installation-20261010/report.md": "evidence/fresh-install/report.md", + "../results/u3-packet-v4-permission-20261012-v3/report.md": "evidence/permission_journey-report.md", + "../results/installed-agent-release-20261012-v2/report.md": "evidence/installed_agent-report.md", + "installed-agent-host.md": "agent-host.md", +} +CONSUMER_DOCUMENT_LINKS = { + "footwork/README.md": { + "capability-packaging.md": "installation.md", + "installed-agent-host.md": "agent-host.md", + }, + "footwork/agent-host.md": {"capability-packaging.md": "installation.md"}, + "footwork/installation.md": DOCUMENT_LINKS, +} +REPORTS = { + "footwork/evidence/fresh-install/report.md": "jevdual/results/clean-installation-20261010/report.md", + "footwork/evidence/permission_journey-report.md": "jevdual/results/u3-packet-v4-permission-20261012-v3/report.md", + "footwork/evidence/installed_agent-report.md": "jevdual/results/installed-agent-release-20261012-v2/report.md", +} +LEGACY_INSTALL_ATTACHMENTS = ( + "summary.json", + "py312-installation.json", + "py314-installation.json", + "py312-native.json", + "py314-native.json", + "unknown-report-negative.json", + "missing-prerequisites.json", + "retained-failures.json", + "cwd-shadow-baseline.json", + "cleanup.json", +) +EVIDENCE = { + "permission_journey": "jevdual/results/u3-packet-v4-permission-20261012-v3/summary.json", + "installed_agent": "jevdual/results/installed-agent-release-20261012-v2/summary.json", +} + + +def sha(data: bytes) -> str: + return hashlib.sha256(data).hexdigest() + + +def encoded(value: dict) -> bytes: + return (json.dumps(value, indent=2, sort_keys=True) + "\n").encode() + + +def sanitized_legacy_json(data: bytes) -> bytes: + """Derive shareable receipts from their content, never from host environment. + + URLs remain verbatim. Recognized Footwork project/worktree roots collapse to + WORKTREE; other personal Unix home roots collapse to HOST_HOME. These aliases + intentionally discard identity and are diagnostic copies, not raw evidence. + """ + segment = r"[^/\s\\\"'<>]+" + home = r"/(?:Users|home)/" + segment + worktree = home + r"/(?:\.codex/worktrees/" + segment + r"/footwork|projects/footwork)" + boundary = r"(?=/|$|[\s\\\"'<>])" + pattern = re.compile( + r"(?P[A-Za-z][A-Za-z0-9+.-]*://[^\s\"'<>]+)" + + r"|(?" + + worktree + + boundary + + r")" + + r"|(?" + + home + + r")" + ) + + def text(value: str) -> str: + return pattern.sub( + lambda match: ( + match.group(0) + if match.group("url") + else "" + if match.group("worktree") + else "" + ), + value, + ) + + def clean(value): + if isinstance(value, str): + return text(value) + if isinstance(value, list): + return [clean(item) for item in value] + if isinstance(value, dict): + result = {} + for key, item in value.items(): + key = text(key) + if key in result: + raise ValueError("Sanitized legacy JSON keys collide") + result[key] = clean(item) + return result + return value + + return encoded(clean(json.loads(data))) + + +def commit_identity(commit: str | None) -> str: + return packet_commit_identity(commit, PROJECT) + + +def committed_bytes(commit: str, relative: str) -> bytes: + return packet_committed_bytes(commit, relative, PROJECT) + + +def read_archive(path: Path) -> dict[str, bytes]: + return parse_archive(snapshot(path)) + + +def qualification_packet(path: Path) -> tuple[dict[str, bytes], dict]: + data = snapshot(path) + files = parse_archive(data) + if sha(data) != QUALIFIED_PACKET_SHA: + raise ValueError("Qualification packet differs from the immutable reviewed packet") + manifest = json.loads(files.get("footwork/MANIFEST.json", b"null")) + if not isinstance(manifest, dict) or manifest.get("source_commit") != QUALIFIED_PRODUCT_COMMIT: + raise ValueError("Qualification packet product source differs") + wheels = [name for name in files if name.startswith("footwork/artifacts/") and name.endswith(".whl")] + if len(wheels) != 1: + raise ValueError("Qualification packet must contain exactly one wheel") + expected = { + "footwork/MANIFEST.json", + "footwork/LICENSE", + "footwork/README.md", + "footwork/vm-plan.md", + "footwork/installation.md", + wheels[0], + *("footwork/jevdual/scripts/" + name for name in HELPERS), + } + if len(files) != 14 or set(files) != expected: + raise ValueError("Qualification packet inventory differs") + payloads = {name: sha(data) for name, data in files.items() if name != "footwork/MANIFEST.json"} + if manifest.get("members") != payloads or manifest.get("wheel_sha256") != sha(files[wheels[0]]): + raise ValueError("Qualification packet manifest digest differs") + if manifest.get("automatic_execution") is not False: + raise ValueError("Qualification packet must not execute automatically") + return files, manifest + + +def verify_committed_wheel(name: str, data: bytes, commit: str) -> None: + """Compare against the release commit, even after later runtime changes.""" + verify_packet_wheel(name, data, commit, PROJECT) + + +def approved_summary(data: bytes, label: str) -> dict: + summary = json.loads(data) + if ( + not isinstance(summary, dict) + or summary.get("passed") is not True + or summary.get("independent_review") != "approved" + ): + raise ValueError("Qualification must have passing evidence and independent approval: " + label) + return summary + + +def inventory(packet: Path, source_commit: str | None = None) -> dict[str, bytes]: + commit = commit_identity(source_commit) + packet_files, packet_manifest = qualification_packet(packet) + wheel_name = next(name for name in packet_files if name.endswith(".whl")) + verify_committed_wheel(wheel_name, packet_files[wheel_name], commit) + files = { + name: data + for name, data in packet_files.items() + if name == "footwork/LICENSE" or name == wheel_name or name.startswith("footwork/jevdual/scripts/") + } + files.update({name: committed_bytes(commit, relative) for name, relative in DOCS.items()}) + for name, rewrites in CONSUMER_DOCUMENT_LINKS.items(): + for source, target in rewrites.items(): + files[name] = files[name].replace(("](" + source + ")").encode(), ("](" + target + ")").encode()) + files.update({name: committed_bytes(commit, relative) for name, relative in REPORTS.items()}) + legacy_report = "footwork/evidence/fresh-install/report.md" + files[legacy_report] = files[legacy_report].replace( + b"](../../docs/capability-packaging.md)", b"](../../installation.md)" + ) + legacy_attachments = ["footwork/evidence/fresh-install/" + name for name in LEGACY_INSTALL_ATTACHMENTS] + legacy_derivations = {} + for name, target in zip(LEGACY_INSTALL_ATTACHMENTS, legacy_attachments, strict=True): + source = "jevdual/results/clean-installation-20261010/" + name + original = committed_bytes(commit, source) + files[target] = sanitized_legacy_json(original) + legacy_derivations[target] = { + "source": source, + "original_sha256": sha(original), + "derived_sha256": sha(files[target]), + } + summaries = {} + summary_paths = {} + for label, relative in EVIDENCE.items(): + data = committed_bytes(commit, relative) + summaries[label] = approved_summary(data, label) + target = "footwork/evidence/" + label + "-summary.json" + files[target] = data + summary_paths[label] = target + guest = summaries["permission_journey"] + if ( + guest.get("packet_sha256") != QUALIFIED_PACKET_SHA + or guest.get("source_commit") != QUALIFIED_PRODUCT_COMMIT + ): + raise ValueError("Guest qualification does not refer to the frozen product") + agent = summaries["installed_agent"] + if ( + agent.get("packet_sha256") != QUALIFIED_PACKET_SHA + or agent.get("source_commit") != QUALIFIED_PRODUCT_COMMIT + or agent.get("wheel_sha256") != packet_manifest["wheel_sha256"] + ): + raise ValueError("Agent qualification does not refer to the frozen product") + scope_keys = ( + "classification", + "qualified_scope", + "qualified_scopes", + "platform", + "os", + "architecture", + "python", + "host", + "model", + "host_version", + ) + limit_keys = ("limits", "limitations", "disk_persistence", "classification") + qualification = { + "qualification": "scoped_local_candidate", + "qualified_product_source_commit": packet_manifest["source_commit"], + "release_source_commit": commit, + "wheel_sha256": packet_manifest["wheel_sha256"], + "source_packet_sha256": QUALIFIED_PACKET_SHA, + "evidence": summary_paths, + "legacy_evidence": { + "role": "historical_context_only", + "report": legacy_report, + "attachments": legacy_attachments, + "derivations": legacy_derivations, + "placeholder_semantics": { + "": "Recognized personal Footwork project or Codex worktree root from evidence content.", + "": "Other /Users/ or /home/ roots from evidence content.", + "identity": "Different original identities may share a placeholder; originals remain unchanged and are identified by SHA256.", + "urls": "Preserved verbatim; placeholders normalize diagnostic paths only.", + }, + "qualification": "Separate from current reviewed permission_journey and installed_agent evidence.", + }, + "qualified_scopes": { + label: {key: summary[key] for key in scope_keys if key in summary} + for label, summary in summaries.items() + }, + "limits": { + label: {key: summary[key] for key in limit_keys if key in summary} + for label, summary in summaries.items() + }, + } + files["footwork/qualification.json"] = encoded(qualification) + manifest = { + "schema_version": "1", + "source_commit": commit, + "wheel_sha256": packet_manifest["wheel_sha256"], + "automatic_execution": False, + "qualification": "scoped_local_candidate", + "members": {name: sha(data) for name, data in sorted(files.items())}, + } + files["footwork/MANIFEST.json"] = encoded(manifest) + return files + + +def verify(candidate: Path, packet: Path, source_commit: str | None = None) -> dict: + expected = inventory(packet, source_commit) + candidate_data = snapshot(candidate) + actual = parse_archive(candidate_data) + if set(actual) != set(expected): + raise ValueError("Release candidate inventory differs") + for name, data in expected.items(): + if actual[name] != data: + raise ValueError("Release candidate member differs: " + name) + manifest = json.loads(actual["footwork/MANIFEST.json"]) + return { + "verified": True, + "sha256": sha(candidate_data), + "members": len(actual), + "source_commit": manifest["source_commit"], + "wheel_sha256": manifest["wheel_sha256"], + } + + +def build(packet: Path, output: Path, source_commit: str | None = None) -> Path: + files = inventory(packet, source_commit) + output.mkdir(parents=True, exist_ok=False, mode=0o700) + candidate = output / ARCHIVE_NAME + with candidate.open("xb") as stream, zipfile.ZipFile(stream, "w") as archive: + for name, data in sorted(files.items()): + entry = zipfile.ZipInfo(name, (1980, 1, 1, 0, 0, 0)) + entry.compress_type = zipfile.ZIP_DEFLATED + entry.create_system = 3 + entry.external_attr = 0o100644 << 16 + archive.writestr(entry, data) + return candidate + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--qualification-packet", type=Path, required=True) + parser.add_argument("--source-commit") + mode = parser.add_mutually_exclusive_group(required=True) + mode.add_argument("--output", type=Path) + mode.add_argument("--verify", type=Path) + args = parser.parse_args() + commit = commit_identity(args.source_commit) + packet = args.qualification_packet.absolute() + candidate = args.verify or build(packet, args.output, commit) + print(json.dumps({"candidate": str(candidate.absolute()), **verify(candidate, packet, commit)})) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/jevdual/scripts/capability_clean_install.py b/jevdual/scripts/capability_clean_install.py index 54ee253..fa0a1ac 100644 --- a/jevdual/scripts/capability_clean_install.py +++ b/jevdual/scripts/capability_clean_install.py @@ -11,6 +11,7 @@ import json import os import platform +import re import shutil import subprocess from pathlib import Path @@ -18,6 +19,22 @@ PROJECT = Path(__file__).resolve().parents[2] +def source_commit(project: Path) -> str: + """Packet provenance never calls Git or the macOS developer-tools shim.""" + manifest = project / "MANIFEST.json" + if manifest.exists() or manifest.is_symlink(): + if manifest.is_symlink() or not manifest.is_file(): + raise ValueError("Packet manifest must be a regular file") + payload = json.loads(manifest.read_text()) + commit = payload.get("source_commit") if isinstance(payload, dict) else None + if not isinstance(commit, str) or not re.fullmatch(r"[0-9a-f]{40}", commit): + raise ValueError("Packet manifest requires a full immutable source commit") + return commit + if (project / ".git").exists(): + return subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=project, text=True).strip() + return "unavailable" + + def isolated_environment(state_root: Path | None = None) -> dict[str, str]: env = {} for name, value in os.environ.items(): @@ -61,6 +78,7 @@ def main() -> int: root, wheel = args.run_dir.resolve(), args.wheel.resolve() if not root.is_relative_to(PROJECT / "output") or not wheel.is_file(): parser.error("owned output run and existing wheel required") + provenance = source_commit(PROJECT) root.mkdir(parents=True, exist_ok=False, mode=0o700) cwd, cache, venv = root / "outside-source", root / "empty-cache", root / "venv" cwd.mkdir(mode=0o700) @@ -74,9 +92,7 @@ def main() -> int: interpreter = os.path.abspath(args.python) common = [uv, "--no-config", "--no-progress", "--cache-dir", str(cache), "--no-python-downloads"] receipt = { - "source_commit": subprocess.check_output( - ["git", "rev-parse", "HEAD"], cwd=PROJECT, text=True - ).strip(), + "source_commit": provenance, "host": {"platform": platform.system(), "machine": platform.machine()}, "interpreter_requested": interpreter, "wheel": str(wheel), diff --git a/jevdual/scripts/capability_fixture.py b/jevdual/scripts/capability_fixture.py index 5ad8ec6..a9850d8 100644 --- a/jevdual/scripts/capability_fixture.py +++ b/jevdual/scripts/capability_fixture.py @@ -1,24 +1,40 @@ -"""Prepare and hold run-owned browser/TextEdit fixtures for capability trials. +"""Hold run-owned browser/TextEdit fixtures; Ctrl-C performs bounded owned cleanup. -Run in a terminal; Ctrl-C closes only this process's browser and named fixture -document. Scope and fixture files remain in the explicitly supplied directory. +Accessibility must already be granted. Closing the exact document uses AX only; +this helper never requests an Automation or Screen Recording grant. """ from __future__ import annotations import argparse import asyncio +import hashlib import json +import os +import signal +import stat import subprocess import threading import time from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path +from urllib.parse import unquote, urlsplit +from urllib.request import ProxyHandler, build_opener VALUE = "17.25 USD" PREFIX = "Invoice report\n" HTML = f"Footwork invoice fixture

Invoice

Invoice total: {VALUE}

" CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" +BROWSER_STARTUP_SECONDS = 180 +IDENTITY_SECONDS = 30 +CLEANUP_SECONDS = 10 +MAX_DOCUMENT_BYTES = 1024 * 1024 + + +class FixtureFailure(Exception): + def __init__(self, phase: str, reason: str, exit_code: int): + super().__init__(reason) + self.phase, self.reason, self.exit_code = phase, reason, exit_code class Page(BaseHTTPRequestHandler): @@ -34,54 +50,386 @@ def log_message(self, *args): pass -def script(source: str, *args: str) -> str: - return subprocess.run( - ["osascript", "-e", source, *args], check=True, capture_output=True, text=True, timeout=10 - ).stdout.strip() +def normalized_document(value) -> str | None: + if not value: + return None + parts = urlsplit(str(value)) + if parts.scheme != "file" or parts.netloc not in ("", "localhost") or parts.query or parts.fragment: + return None + return str(Path(unquote(parts.path)).resolve()) + + +def ax_deadline(element, deadline: float | None): + from ApplicationServices import AXUIElementSetMessagingTimeout + + if deadline is None: + return + remaining = deadline - time.monotonic() + if remaining <= 0: + raise FixtureFailure("document_cleanup", "cleanup_deadline", 6) + error = AXUIElementSetMessagingTimeout(element, min(1.0, remaining)) + if error: + raise FixtureFailure("document_cleanup", f"ax_timeout_refused:{error}", 6) + + +def ax_attribute(element, name: str, *, optional: bool = False, deadline: float | None = None): + from ApplicationServices import AXUIElementCopyAttributeValue + + ax_deadline(element, deadline) + error, value = AXUIElementCopyAttributeValue(element, name, None) + # AXErrorAttributeUnsupported / AXErrorNoValue are absence only for optional attributes. + if error in (-25205, -25212) and optional: + return None + if error: + raise FixtureFailure("document_cleanup", f"ax_attribute_refused:{name}:{error}", 6) + return value + + +def owned_window(app, document: Path, deadline: float | None = None): + expected = str(document.resolve()) + matches = [ + window + for window in ax_attribute(app, "AXWindows", deadline=deadline) or () + if normalized_document(ax_attribute(window, "AXDocument", optional=True, deadline=deadline)) + == expected + ] + if len(matches) > 1: + raise FixtureFailure("document_cleanup", "document_ambiguous", 6) + return matches[0] if matches else None + + +def press(element, deadline: float | None = None): + from ApplicationServices import AXUIElementPerformAction + + ax_deadline(element, deadline) + error = AXUIElementPerformAction(element, "AXPress") + if error: + raise FixtureFailure("document_cleanup", f"ax_press_refused:{error}", 6) + + +def discard_button(window, deadline: float | None = None): + sheets = list(ax_attribute(window, "AXSheets", optional=True, deadline=deadline) or ()) + # Some TextEdit versions expose attached sheets through AXChildren instead. + if not sheets: + sheets = [ + child + for child in ax_attribute(window, "AXChildren", optional=True, deadline=deadline) or () + if ax_attribute(child, "AXRole", optional=True, deadline=deadline) == "AXSheet" + ] + if not sheets: + return None + if len(sheets) != 1 or ax_attribute(sheets[0], "AXRole", deadline=deadline) != "AXSheet": + raise FixtureFailure("document_cleanup", "sheet_ambiguous", 6) + sheet = sheets[0] + # The sheet must be attached to this exact window, never app-global. + if ax_attribute(sheet, "AXParent", deadline=deadline) != window: + raise FixtureFailure("document_cleanup", "sheet_not_attached", 6) + stack, seen, matches, titles = [sheet], 0, [], set() + while stack: + node = stack.pop() + seen += 1 + if seen > 400: + raise FixtureFailure("document_cleanup", "sheet_tree_limit", 6) + if ax_attribute(node, "AXRole", optional=True, deadline=deadline) == "AXButton": + title = str(ax_attribute(node, "AXTitle", optional=True, deadline=deadline) or "") + titles.add(title) + if title in ("Don't Save", "Don’t Save", "Delete"): + matches.append(node) + stack.extend(ax_attribute(node, "AXChildren", optional=True, deadline=deadline) or ()) + if not {"Save", "Cancel"} <= titles: + raise FixtureFailure("document_cleanup", "sheet_not_save_prompt", 6) + if len(matches) != 1: + raise FixtureFailure("document_cleanup", "discard_button_ambiguous", 6) + return sheet, matches[0] + + +def close_owned_document(pid: int, document: Path) -> dict: + from ApplicationServices import AXUIElementCreateApplication + + try: + deadline = time.monotonic() + CLEANUP_SECONDS + app = AXUIElementCreateApplication(pid) + window = owned_window(app, document, deadline) + if window is None: + return {"status": "unknown", "reason": "document_not_observed", "pid": pid} + close = ax_attribute(window, "AXCloseButton", optional=True, deadline=deadline) + if close is None: + raise FixtureFailure("document_cleanup", "close_button_missing", 6) + if owned_window(app, document, deadline) != window: + raise FixtureFailure("document_cleanup", "document_identity_changed", 6) + if ax_attribute(window, "AXCloseButton", deadline=deadline) != close: + raise FixtureFailure("document_cleanup", "close_button_changed", 6) + press(close, deadline) + discarded = False + last_invalid = None + while time.monotonic() < deadline: + try: + current = owned_window(app, document, deadline) + if current is None: + # Do not mistake a renamed/reused window for observed disappearance. + if window in (ax_attribute(app, "AXWindows", deadline=deadline) or ()): + raise FixtureFailure("document_cleanup", "document_identity_changed", 6) + return {"status": "confirmed", "reason": "window_disappeared", "pid": pid} + if current != window: + raise FixtureFailure("document_cleanup", "document_identity_changed", 6) + discard = discard_button(window, deadline) + if discard is not None and not discarded: + if ( + owned_window(app, document, deadline) != window + or discard_button(window, deadline) != discard + ): + raise FixtureFailure("document_cleanup", "sheet_identity_changed", 6) + press(discard[1], deadline) + discarded = True + last_invalid = None + except FixtureFailure as exc: + # A successful close can invalidate a previously observed child or + # window between reads. Only that observation error is retryable, + # only after the close press; it is never evidence of disappearance. + # Restart from fresh AXWindows and exact identity, never repeat AXPress. + if not (exc.reason.startswith("ax_attribute_refused:") and exc.reason.endswith(":-25202")): + raise + last_invalid = exc.reason + time.sleep(0.1) + return { + "status": "unknown", + "reason": "invalid_element_unresolved" if last_invalid else "window_still_present", + "pid": pid, + } + except Exception as exc: # noqa: BLE001 - receipt boundary; independent cleanup must continue + return {"status": "error", "reason": getattr(exc, "reason", type(exc).__name__), "pid": pid} + + +def wait_browser_ready(browser, profile: Path) -> dict: + deadline = time.monotonic() + BROWSER_STARTUP_SECONDS + opener = build_opener(ProxyHandler({})) + while time.monotonic() < deadline: + if browser.poll() is not None: + raise FixtureFailure("browser_startup", "browser_exited", 3) + try: + lines = (profile / "DevToolsActivePort").read_text().splitlines() + port = int(lines[0]) + if not 1 <= port <= 65535 or len(lines) != 2 or not lines[1].startswith("/devtools/browser/"): + raise ValueError("invalid private-profile endpoint") + with opener.open(f"http://127.0.0.1:{port}/json/version", timeout=1) as response: + version = json.loads(response.read(65536)) + if version.get("webSocketDebuggerUrl") != f"ws://127.0.0.1:{port}{lines[1]}": + raise ValueError("endpoint mismatch") + if browser.poll() is not None: + raise FixtureFailure("browser_startup", "browser_exited", 3) + return {"status": "ready", "pid": browser.pid, "port": port} + except (OSError, ValueError, IndexError): + time.sleep(0.25) + raise FixtureFailure("browser_startup", "startup_timeout", 3) + + +async def bounded_call(awaitable, deadline: float, phase: str): + remaining = deadline - time.monotonic() + if remaining <= 0: + awaitable.close() + raise FixtureFailure(phase, "sdk_deadline", 4 if phase == "identity" else 6) + try: + return await asyncio.wait_for(awaitable, timeout=remaining) + except TimeoutError as exc: + raise FixtureFailure(phase, "sdk_timeout", 4 if phase == "identity" else 6) from exc + + +async def shutdown_driver(driver, phase: str, deadline: float): + await bounded_call(driver.shutdown(), deadline, phase) -async def scope_for(chrome_pid: int, url: str, document: Path) -> dict: +async def textedit_pid() -> int | None: + from cua_driver import CuaDriver, ListAppsInput + + driver = CuaDriver.create() + deadline = time.monotonic() + 3 + try: + apps = (await bounded_call(driver.list_apps(ListAppsInput()), deadline - 1, "document_cleanup")).apps + pids = {a.pid for a in apps if a.name == "TextEdit" and a.pid} + if len(pids) > 1: + raise FixtureFailure("document_cleanup", "textedit_process_ambiguous", 6) + return next(iter(pids), None) + finally: + await shutdown_driver(driver, "document_cleanup", deadline) + + +async def scope_for(chrome_pid: int, url: str, document: Path, browser=None) -> dict: + from ApplicationServices import AXUIElementCreateSystemWide, AXUIElementSetMessagingTimeout from cua_driver import CuaDriver, ListAppsInput, ListWindowsInput from jevdual import ax driver = CuaDriver.create() + global_ax = AXUIElementCreateSystemWide() try: - deadline = time.monotonic() + 30 + deadline = time.monotonic() + IDENTITY_SECONDS while time.monotonic() < deadline: - apps = (await driver.list_apps(ListAppsInput())).apps - textedit = next((a for a in apps if a.name == "TextEdit" and a.pid), None) + if browser is not None and browser.poll() is not None: + raise FixtureFailure("identity", "browser_exited", 4) + apps = (await bounded_call(driver.list_apps(ListAppsInput()), deadline, "identity")).apps + textedit_pids = {a.pid for a in apps if a.name == "TextEdit" and a.pid} + if len(textedit_pids) > 1: + raise FixtureFailure("identity", "textedit_process_ambiguous", 4) + textedit = next(iter(textedit_pids), None) browser_windows = ( - await driver.list_windows(ListWindowsInput(pid=chrome_pid, on_screen_only=True)) + await bounded_call( + driver.list_windows(ListWindowsInput(pid=chrome_pid, on_screen_only=True)), + deadline, + "identity", + ) ).windows native_windows = ( - (await driver.list_windows(ListWindowsInput(pid=textedit.pid, on_screen_only=True))).windows + ( + await bounded_call( + driver.list_windows(ListWindowsInput(pid=textedit, on_screen_only=True)), + deadline, + "identity", + ) + ).windows if textedit else [] ) matching = [] for window in native_windows: + # System-wide element timeout sets this helper process's global AX + # default, including references created inside the product lookup. + # Recheck the phase budget for each window and refuse late results. + remaining = deadline - time.monotonic() + if remaining <= 0: + raise FixtureFailure("identity", "identity_deadline", 4) + error = AXUIElementSetMessagingTimeout(global_ax, min(1.0, remaining)) + if error: + raise FixtureFailure("identity", f"ax_timeout_refused:{error}", 4) try: - if ax.document_path(textedit.pid, window.window_id) == str(document): + path = ax.document_path(textedit, window.window_id) + if path and str(Path(path).resolve()) == str(document.resolve()): matching.append(window) except ax.AXError as exc: - # WindowServer can publish an opening window before its AX - # surface exists. Retain the identity check and deadline. if exc.reason != "window_missing": raise if len(browser_windows) == 1 and len(matching) == 1: + if time.monotonic() >= deadline: + raise FixtureFailure("identity", "identity_deadline", 4) + if browser is not None and browser.poll() is not None: + raise FixtureFailure("identity", "browser_exited", 4) return { "task": document.parent.name, "source": {"pid": chrome_pid, "window_id": browser_windows[0].window_id, "url": url}, "destination": { - "pid": textedit.pid, + "pid": textedit, "window_id": matching[0].window_id, "document": str(document), }, } await asyncio.sleep(0.25) - raise RuntimeError("Owned browser/document windows could not be identified uniquely") + raise FixtureFailure("identity", "windows_not_unique", 4) + finally: + try: + await shutdown_driver(driver, "identity", time.monotonic() + 2) + finally: + # This standalone helper owns the process default. Zero resets the + # global default; it does not restore an unknown previous timeout. + error = AXUIElementSetMessagingTimeout(global_ax, 0) + if error: + raise FixtureFailure("identity", f"ax_timeout_reset_refused:{error}", 4) + + +def document_snapshot(document: Path) -> dict: + """Hash bounded actual file bytes; this says nothing about unsaved AX text.""" + directory_fd = file_fd = None + try: + directory_fd = os.open(document.parent, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW) + file_fd = os.open(document.name, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=directory_fd) + before = os.fstat(file_fd) + if not stat.S_ISREG(before.st_mode): + return {"status": "refused", "reason": "not_regular"} + if before.st_size > MAX_DOCUMENT_BYTES: + return {"status": "refused", "reason": "file_too_large"} + payload = bytearray() + while len(payload) <= MAX_DOCUMENT_BYTES: + chunk = os.read(file_fd, min(65536, MAX_DOCUMENT_BYTES + 1 - len(payload))) + if not chunk: + break + payload.extend(chunk) + if len(payload) > MAX_DOCUMENT_BYTES: + return {"status": "refused", "reason": "file_too_large"} + after = os.fstat(file_fd) + current = os.stat(document.name, dir_fd=directory_fd, follow_symlinks=False) + identity = lambda value: (value.st_dev, value.st_ino, value.st_size, value.st_mtime_ns) + if ( + identity(before) != identity(after) + or identity(after) != identity(current) + or len(payload) != after.st_size + ): + return {"status": "refused", "reason": "file_changed"} + return { + "status": "observed", + "size_bytes": len(payload), + "sha256": hashlib.sha256(payload).hexdigest(), + } + except FileNotFoundError: + return {"status": "absent", "reason": "file_missing"} + except OSError as exc: + return {"status": "error", "reason": type(exc).__name__, "errno": exc.errno} finally: - await driver.shutdown() + if file_fd is not None: + os.close(file_fd) + if directory_fd is not None: + os.close(directory_fd) + + +def cleanup(browser, http, thread, logfile, document: Path, *, opened: bool, pid: int | None) -> dict: + result = { + "document": {"status": "not_started"}, + "browser": {"status": "not_started"}, + "listener": {"status": "not_started"}, + } + before = document_snapshot(document) + if opened: + try: + observed_pid = asyncio.run(textedit_pid()) + if pid is not None and observed_pid != pid: + raise FixtureFailure("document_cleanup", "textedit_process_changed", 6) + pid = observed_pid + result["document"] = ( + close_owned_document(pid, document) + if pid + else {"status": "unknown", "reason": "textedit_not_observed"} + ) + except Exception as exc: # noqa: BLE001 - receipt boundary; independent cleanup must continue + result["document"] = {"status": "error", "reason": getattr(exc, "reason", type(exc).__name__)} + result["document"]["file_before"] = before + result["document"]["file_after"] = document_snapshot(document) + if browser is not None: + try: + if browser.poll() is None: + browser.terminate() + try: + browser.wait(timeout=10) + except subprocess.TimeoutExpired: + browser.kill() + browser.wait(timeout=10) + result["browser"] = {"status": "confirmed", "pid": browser.pid, "returncode": browser.returncode} + except Exception as exc: # noqa: BLE001 - receipt boundary; independent cleanup must continue + result["browser"] = {"status": "error", "reason": type(exc).__name__, "pid": browser.pid} + if http is not None: + try: + if thread is not None and thread.is_alive(): + http.shutdown() + http.server_close() + if thread is not None: + thread.join(timeout=2) + if thread.is_alive(): + raise RuntimeError("listener_thread_alive") + result["listener"] = {"status": "confirmed"} + except Exception as exc: # noqa: BLE001 - receipt boundary; independent cleanup must continue + result["listener"] = {"status": "error", "reason": type(exc).__name__} + if logfile is not None: + logfile.close() + return result + + +def record(root: Path, name: str, value: dict): + (root / name).write_text(json.dumps(value, indent=2) + "\n") def main() -> None: @@ -98,75 +446,95 @@ def main() -> None: root = args.run_dir.resolve() root.mkdir(parents=True, exist_ok=False, mode=0o700) document = root / f"{root.name}-invoice.txt" - document.write_text(PREFIX) - (root / "fixture.html").write_text(HTML) - http = ThreadingHTTPServer(("127.0.0.1", 0), Page) - threading.Thread(target=http.serve_forever, daemon=True).start() - url = f"http://127.0.0.1:{http.server_port}/" - logfile = (root / "chrome.log").open("w") - browser = subprocess.Popen( - [ - CHROME, - f"--user-data-dir={root / 'chrome-profile'}", - "--remote-debugging-port=0", - "--no-first-run", - "--no-default-browser-check", - "--disable-background-networking", - "--disable-sync", - "--disable-component-update", - "--new-window", - url, - ], - stdout=logfile, - stderr=logfile, - start_new_session=True, - ) + browser = http = thread = logfile = pid = None + opened, exit_code, phase = False, 0, "setup" try: - # Launch Services supplies the selected-file sandbox extension; a raw - # AppleEvent open can be refused by sandboxed TextEdit. - subprocess.run(["open", "-a", "TextEdit", str(document)], check=True) - scope = asyncio.run(scope_for(browser.pid, url, document)) - (root / "scope.json").write_text(json.dumps(scope, indent=2) + "\n") - (root / "fixture.json").write_text( - json.dumps( - { - "url": url, - "document": str(document), - "value": VALUE, - "prefix": PREFIX, - "browser_pid": browser.pid, - }, - indent=2, - ) - + "\n" + document.write_text(PREFIX) + (root / "fixture.html").write_text(HTML) + http = ThreadingHTTPServer(("127.0.0.1", 0), Page) + thread = threading.Thread(target=http.serve_forever, daemon=True) + thread.start() + url = f"http://127.0.0.1:{http.server_port}/" + logfile = (root / "chrome.log").open("w") + phase = "browser_startup" + browser = subprocess.Popen( + [ + CHROME, + f"--user-data-dir={root / 'chrome-profile'}", + "--remote-debugging-port=0", + "--no-first-run", + "--no-default-browser-check", + "--disable-background-networking", + "--disable-sync", + "--disable-component-update", + "--new-window", + url, + ], + stdout=logfile, + stderr=logfile, + start_new_session=True, ) - print(json.dumps({"ready": True, "scope": str(root / "scope.json")}), flush=True) - while True: + startup = wait_browser_ready(browser, root / "chrome-profile") + record(root, "startup.json", startup) + phase = "document_open" + # Mark attempted ownership before launch, so partial opens still get cleanup. + opened = True + subprocess.run(["open", "-a", "TextEdit", str(document)], check=True, timeout=10) + phase = "identity" + scope = asyncio.run(scope_for(browser.pid, url, document, browser)) + pid = scope["destination"]["pid"] + record(root, "scope.json", scope) + record( + root, + "fixture.json", + { + "url": url, + "document": str(document), + "value": VALUE, + "prefix": PREFIX, + "browser_pid": browser.pid, + }, + ) + ready = {"ready": True, "phase": "hold", "scope": str(root / "scope.json")} + record(root, "fixture-status.json", ready) + print(json.dumps(ready), flush=True) + phase = "hold" + while browser.poll() is None: time.sleep(1) + raise FixtureFailure("hold", "browser_exited", 3) except KeyboardInterrupt: - pass + if phase != "hold": + exit_code = 130 + failure = {"ready": False, "phase": phase, "reason": "interrupted", "exit_code": exit_code} + record(root, "fixture-status.json", failure) + print(json.dumps(failure), flush=True) + except Exception as exc: # noqa: BLE001 - receipt boundary; independent cleanup must continue + exit_code = getattr( + exc, "exit_code", 3 if phase == "browser_startup" else 4 if phase == "identity" else 5 + ) + failure = { + "ready": False, + "phase": getattr(exc, "phase", phase), + "reason": getattr(exc, "reason", type(exc).__name__), + "exit_code": exit_code, + } + record(root, "fixture-status.json", failure) + print(json.dumps(failure), flush=True) finally: - # This exact file was created and opened by this fixture owner. + previous_sigint = signal.signal(signal.SIGINT, signal.SIG_IGN) try: - script( - 'on run argv\ntell application "TextEdit"\n' - "set matches to every document whose name is (item 2 of argv)\n" - "if (count of matches) is 1 then\nset d to item 1 of matches\n" - "if path of d is (item 1 of argv) then close d saving no\n" - "end if\nend tell\nend run", - str(document), - document.name, - ) - except (subprocess.CalledProcessError, subprocess.TimeoutExpired): - print("Fixture document cleanup could not be confirmed; browser cleanup continues.", flush=True) - browser.terminate() - try: - browser.wait(timeout=10) - except subprocess.TimeoutExpired: - browser.kill() - browser.wait(timeout=10) - http.shutdown() - logfile.close() + shutdown = cleanup(browser, http, thread, logfile, document, opened=opened, pid=pid) + record(root, "cleanup.json", shutdown) + print(json.dumps({"shutdown": shutdown}), flush=True) + if ( + any(item["status"] not in ("confirmed", "not_started") for item in shutdown.values()) + and not exit_code + ): + exit_code = 6 + finally: + signal.signal(signal.SIGINT, previous_sigint) + if exit_code: + raise SystemExit(exit_code) if __name__ == "__main__": diff --git a/jevdual/scripts/capability_fixture_config.py b/jevdual/scripts/capability_fixture_config.py new file mode 100644 index 0000000..ec89b3c --- /dev/null +++ b/jevdual/scripts/capability_fixture_config.py @@ -0,0 +1,55 @@ +"""Create per-job configuration from an already observed, owned fixture scope. + +This copies resource identifiers, not permission or execution authority. Binding +still checks actual resource identity. No app is started and no input is sent. +""" + +from __future__ import annotations + +import argparse +import json +import subprocess +import sys +from pathlib import Path + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--scope", type=Path, required=True) + parser.add_argument("--run-dir", type=Path, required=True) + args = parser.parse_args() + with args.scope.open("rb") as stream: + raw = stream.read(65537) + if len(raw) > 65536: + parser.error("Fixture scope exceeds the configuration input bound") + try: + scope = json.loads(raw) + source, destination = scope["source"], scope["destination"] + command = [ + sys.executable, + "-I", + "-m", + "jevdual.capability.cli", + "config", + "--run-dir", + str(args.run_dir), + "--source-pid", + str(source["pid"]), + "--source-window", + str(source["window_id"]), + "--source-url", + source["url"], + "--destination-pid", + str(destination["pid"]), + "--destination-window", + str(destination["window_id"]), + "--document", + destination["document"], + ] + except (ValueError, RecursionError, KeyError, TypeError): + parser.error("Invalid observed fixture scope") + return subprocess.run(command, check=False, timeout=30).returncode + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/jevdual/scripts/capability_installed_agent_trial.py b/jevdual/scripts/capability_installed_agent_trial.py new file mode 100644 index 0000000..7828581 --- /dev/null +++ b/jevdual/scripts/capability_installed_agent_trial.py @@ -0,0 +1,382 @@ +"""One real Claude Code task through an installed, scoped Footwork MCP server. + +No retries, relay, source product imports, permission changes or fixture writes. +The caller owns installation, permissions, fixture preparation and retirement. +Raw host prose is private; only the installed readonly adapter supplies a result. +""" + +from __future__ import annotations + +import argparse +import contextlib +import json +import math +import os +import signal +import subprocess +import time +from pathlib import Path +from typing import Any + +import psutil +from capability_clean_install import isolated_environment, source_commit +from capability_comparison_trial import SESSION_CAP, TIMEOUT, host_command, parse_transcript, prompt +from capability_host_trial import ax_document_text +from capability_restart_trial import _rows, _wait_no_worker + +PROJECT = Path(__file__).resolve().parents[2] +TOOLS = [f"mcp__footwork__{name}" for name in ("bind", "observe", "act", "status", "report")] +PREFIX, VALUE = "Invoice report\n", "17.25 USD" +SCHEDULING_RESERVE_USD = 2.0 +ISOLATION_KEYS = { + "HOME", + "XDG_CONFIG_HOME", + "XDG_CACHE_HOME", + "TMPDIR", + "TMP", + "TEMP", + "BROWSER_USE_CONFIG_DIR", + "PLAYWRIGHT_BROWSERS_PATH", + "PYTHONNOUSERSITE", + "ANONYMIZED_TELEMETRY", + "BROWSER_USE_CLOUD_SYNC", +} + + +def require(condition: bool, message: str) -> None: + if not condition: + raise RuntimeError("Qualification gate failed: " + message) + + +def write(path: Path, value: Any) -> None: + with path.open("x") as stream: + json.dump(value, stream, indent=2) + stream.write("\n") + path.chmod(0o600) + + +def installed_configuration( + python: Path, + scope_path: Path, + journal: Path, + host_path: Path, + isolation_root: Path, + audit: Path, +) -> tuple[dict, dict, dict]: + """Check generated commands verbatim; add only public isolation overrides.""" + scope = json.loads(scope_path.read_text()) + config = json.loads(host_path.read_text()) + manifest = json.loads((host_path.parent / "run.json").read_text()) + require(set(config) == {"mcpServers"}, "unexpected host configuration") + require(set(config["mcpServers"]) == {"footwork"}, "unexpected MCP server") + server = config["mcpServers"]["footwork"] + expected = [ + "-I", + "-m", + "jevdual.capability.cli", + "server", + "--scope", + str(scope_path), + "--journal", + str(journal), + ] + require(set(server) == {"command", "args", "env"}, "unexpected server configuration") + require( + server["command"] == str(python) and server["args"] == expected, + "server must directly use the installed generated command", + ) + expected_env = { + "PYTHONPATH": "", + "PYTHONHOME": "", + "PYTHONNOUSERSITE": "1", + "ANONYMIZED_TELEMETRY": "false", + "BROWSER_USE_CLOUD_SYNC": "false", + } + require(server["env"] == expected_env, "unexpected generated server environment") + require( + manifest["scope"] == str(scope_path) + and manifest["host_config"] == str(host_path) + and manifest["task"] == scope["task"], + "generated task identity differs", + ) + require( + manifest["report_adapter"] + == [ + str(python), + "-I", + "-m", + "jevdual.capability.cli", + "report", + "--journal", + str(journal), + "--task", + scope["task"], + ], + "report adapter must directly use the installed generated command", + ) + require( + manifest["report_schema"] == str(host_path.parent / "report-schema.json"), + "unexpected report schema path", + ) + schema = json.loads(Path(manifest["report_schema"]).read_text()) + env = isolated_environment(isolation_root) + # Never serialize inherited PATH, authentication or any other ambient value. + server["env"].update({key: env[key] for key in ISOLATION_KEYS}) + # Enumerate names only. MCP must not inherit ambient provider credentials; + # the real agent host keeps its existing opaque authentication unchanged. + server["env"].update({name: "" for name in os.environ if name.endswith(("_KEY", "_TOKEN", "_SECRET"))}) + server["env"]["FOOTWORK_RUNTIME_AUDIT_DIR"] = str(audit) + return config, manifest, schema + + +def transcript_evidence(path: Path) -> tuple[dict, Any]: + evidence = parse_transcript(path, TOOLS, ["footwork"]) + events = [json.loads(line) for line in path.read_text().splitlines() if line.strip()] + init = next( + (event for event in events if event.get("type") == "system" and event.get("subtype") == "init"), {} + ) + # The schema finalizer is a host output mechanism, never an effect capability. + offered = init.get("tools", []) + evidence["schema_finalizer_offered"] = "StructuredOutput" in offered + if sorted(offered) == sorted([*TOOLS, "StructuredOutput"]): + evidence["valid_environment"] = init.get("model") == "claude-opus-5-5" and [ + server["name"] for server in init.get("mcp_servers", []) + ] == ["footwork"] + evidence["out_of_arm"] = [name for name in evidence["out_of_arm"] if name != "StructuredOutput"] + evidence["effect_calls"] = [name for name in evidence["calls"] if name != "StructuredOutput"] + result = next((event for event in reversed(events) if event.get("type") == "result"), {}) + claims = result.get("structured_output") + evidence["structured_output_present"] = isinstance(claims, dict) + evidence["budget"] = cost_evidence(evidence.get("cost_usd")) + return evidence, claims + + +def cost_evidence(cost: Any) -> dict: + """Unavailable or invalid accounting cannot qualify a bounded paid trial.""" + available = type(cost) in (int, float) + finite = available and math.isfinite(cost) + valid = finite and 0 <= cost <= SCHEDULING_RESERVE_USD + return { + "configured_softcap_usd": SESSION_CAP, + "scheduling_reserve_usd": SCHEDULING_RESERVE_USD, + "reported_cost_usd": cost if finite else None, + "available": available, + "finite": finite, + "valid": valid, + } + + +def run_host(command: list[str], task: str, root: Path, cwd: Path) -> tuple[int, bool]: + """Host inherits opaque existing authentication; MCP receives separate overrides.""" + with (root / "transcript.jsonl").open("x") as out, (root / "host-stderr.log").open("x") as err: + process = subprocess.Popen( + command, + stdin=subprocess.PIPE, + stdout=out, + stderr=err, + text=True, + cwd=cwd, + start_new_session=True, + ) + timed_out = False + try: + process.communicate(task, timeout=TIMEOUT) + except subprocess.TimeoutExpired: + timed_out = True + with contextlib.suppress(ProcessLookupError): + os.killpg(process.pid, signal.SIGTERM) + try: + process.communicate(timeout=8) + except subprocess.TimeoutExpired: + with contextlib.suppress(ProcessLookupError): + os.killpg(process.pid, signal.SIGKILL) + process.communicate() + finally: + # Retire only this owned host group, including any lingering server. + with contextlib.suppress(ProcessLookupError): + os.killpg(process.pid, signal.SIGTERM) + return process.returncode, timed_out + + +def validate_report(adapter: list[str], claims: Any, env: dict, cwd: Path, root: Path) -> dict: + """Retain rejected claims and adapter output; never fall back to host prose.""" + write(root / "structured-output.json", claims) + completed = subprocess.run( + adapter, + input=json.dumps(claims), + cwd=cwd, + env=env, + capture_output=True, + text=True, + timeout=15, + check=False, + ) + (root / "adapter-stdout.txt").write_text(completed.stdout) + (root / "adapter-stderr.log").write_text(completed.stderr) + write(root / "adapter-exit.json", {"returncode": completed.returncode}) + try: + validated = json.loads(completed.stdout) + except ValueError: + validated = None + write(root / "validated.json", validated) + accepted = ( + completed.returncode == 0 + and isinstance(validated, dict) + and validated.get("validation", {}).get("accepted") is True + and validated.get("operation_completion") == "completed" + and isinstance(validated.get("trusted_summary"), str) + ) + if accepted: + (root / "published.txt").write_text(validated["trusted_summary"] + "\n") + return {"accepted": accepted, "returncode": completed.returncode, "validated": validated} + + +def runtime_evidence(audit: Path, python: Path, isolation_root: Path, journal: Path) -> dict: + contexts = [json.loads(path.read_text()) for path in sorted(audit.glob("*.json"))] + prefix = python.parent.parent + failures = [] + for context in contexts: + if ( + Path(context["prefix"]) != prefix + or context["python"] != str(python) + or context["home"] != str(isolation_root / "private-home") + or not context["user_site_disabled"] + or not context["browser_telemetry_disabled"] + or not context["browser_cloud_sync_disabled"] + ): + failures.append("runtime isolation differs") + origins = [path for paths in context["modules"].values() if paths for path in paths] + if not origins or any(not Path(path).is_relative_to(prefix) for path in origins): + failures.append("module outside installed prefix") + if context["cua_library"] and not Path(context["cua_library"]).is_relative_to(prefix): + failures.append("native library outside installed prefix") + for role in ("server", "worker"): + if not any( + context["role"] == role + and context["modules"].get("jevdual") + and context["modules"].get("cua_driver") + and context["modules"].get("ApplicationServices") + and context["cua_library"] + for context in contexts + ): + failures.append("loaded installed " + role + " audit missing") + registrations = [ + json.loads(path.read_text()) + for path in Path(str(journal) + ".executions").glob("*/registration.json") + ] + if not registrations or not any( + context["role"] == "worker" and context["pid"] == registration["pid"] + for context in contexts + for registration in registrations + ): + failures.append("worker audit does not match registration") + return {"contexts": contexts, "registrations": registrations, "failures": failures} + + +def server_absent(contexts: list[dict]) -> bool: + """A still-live audited PID conservatively fails, including possible PID reuse.""" + return all(not psutil.pid_exists(context["pid"]) for context in contexts if context["role"] == "server") + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + for name in ("python", "scope", "host-config", "journal", "isolation-root", "run-dir"): + parser.add_argument("--" + name, type=Path, required=True) + args = parser.parse_args() + root = args.run_dir.resolve() + isolation = args.isolation_root.resolve() + python = Path(os.path.abspath(args.python)) # Preserve venv executable symlink. + scope_path, host_path, journal = args.scope.resolve(), args.host_config.resolve(), args.journal.resolve() + for path in (root, isolation, scope_path, host_path, journal): + if not path.is_relative_to(PROJECT / "output"): + parser.error("all state must be in owned project output") + if journal.exists() or Path(str(journal) + ".executions").exists(): + parser.error("a fresh job journal is required; no effect retries") + root.mkdir(mode=0o700, exist_ok=False) + cwd, audit = root / "empty-agent-cwd", root / "runtime-context" + cwd.mkdir(mode=0o700) + audit.mkdir(mode=0o700) + receipt: dict[str, Any] = { + "passed": False, + "source_commit": source_commit(PROJECT), + "effect_retry": False, + "configured_softcap_usd": SESSION_CAP, + "scheduling_reserve_usd": SCHEDULING_RESERVE_USD, + "limits": "One Claude Code host, existing authenticated account and existing macOS permissions. " + "No general host, clean OS or disk persistence qualification.", + } + started = time.monotonic() + try: + config, manifest, schema = installed_configuration( + python, + scope_path, + journal, + host_path, + isolation, + audit, + ) + emitted = root / "host.json" + write(emitted, config) + write(root / "report-schema.json", schema) + write(root / "run.json", manifest) + scope = json.loads(scope_path.read_text()) + destination = scope["destination"] + initial = ax_document_text(destination["pid"], destination["document"]) + receipt["oracle_before"] = initial + require(initial == PREFIX, "fixture starting text differs") + command = host_command(emitted, TOOLS, []) + ["--json-schema", json.dumps(schema)] + task = prompt(scope) + ( + "\nReturn a structured final report matching the supplied JSON schema: completion, " + "operations with their operation IDs and dispositions, and persistence. Include the full " + "operation ledger for this task. Do not claim disk persistence without evidence. " + "The host will validate this report against durable operation state before publishing it." + ) + write(root / "command.json", command) + (root / "prompt.txt").write_text(task) + code, timed_out = run_host(command, task, root, cwd) + receipt.update(returncode=code, timed_out=timed_out) + evidence, claims = transcript_evidence(root / "transcript.jsonl") + receipt["host"] = evidence + # Run the readonly installed adapter even if the host rejected/omitted its output. + product_env = isolated_environment(isolation) + product_env.update(config["mcpServers"]["footwork"]["env"]) + receipt["report"] = validate_report(manifest["report_adapter"], claims, product_env, cwd, root) + receipt["worker_absent"] = _wait_no_worker(journal) + receipt["journal_rows"] = _rows(journal) + receipt["runtime"] = runtime_evidence(audit, python, isolation, journal) + # Allow the host's group retirement to finish; this performs no effect dispatch. + deadline = time.monotonic() + 5 + while not server_absent(receipt["runtime"]["contexts"]) and time.monotonic() < deadline: + time.sleep(0.05) + receipt["server_absent"] = server_absent(receipt["runtime"]["contexts"]) + receipt["oracle_actual"] = ax_document_text(destination["pid"], destination["document"]) + require(code == 0 and not timed_out and not evidence["result_is_error"], "host failed") + require(evidence["budget"]["valid"], "host cost missing, invalid or beyond scheduling reserve") + require( + evidence["valid_environment"] and not evidence["out_of_arm"] and evidence["effect_calls"], + "host catalog/model/calls differ from qualification scope", + ) + require(receipt["oracle_actual"] == PREFIX + VALUE, "independent final AX text differs") + rows = receipt["journal_rows"] + require( + len(rows) == 1 + and rows[0]["state"] == "final" + and rows[0]["disposition"] == "applied" + and rows[0]["phase"] == "sent", + "durable operation is not one final applied sent row", + ) + require(not receipt["runtime"]["failures"], "installed runtime provenance failed") + require(receipt["worker_absent"] and receipt["server_absent"], "owned processes remain") + require(receipt["report"]["accepted"], "structured completion rejected") + receipt["passed"] = True + except (OSError, ValueError, KeyError, RuntimeError, psutil.Error, subprocess.SubprocessError) as exc: + receipt["failure"] = f"{type(exc).__name__}: {exc}" + finally: + receipt["elapsed_seconds"] = time.monotonic() - started + write(root / "receipt.json", receipt) + print(json.dumps({"passed": receipt["passed"], "failure": receipt.get("failure")}), flush=True) + return 0 if receipt["passed"] else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/jevdual/scripts/capability_installed_trial.py b/jevdual/scripts/capability_installed_trial.py index b978012..4312637 100644 --- a/jevdual/scripts/capability_installed_trial.py +++ b/jevdual/scripts/capability_installed_trial.py @@ -14,7 +14,7 @@ from pathlib import Path import psutil -from capability_clean_install import isolated_environment +from capability_clean_install import isolated_environment, source_commit from capability_host_trial import ax_document_text, ax_reset_text from capability_interrupt_trial import Server, destination_target from capability_restart_trial import _rows, _wait_no_worker @@ -99,6 +99,7 @@ def main() -> int: root = args.run_dir.resolve() if not root.is_relative_to(PROJECT / "output"): parser.error("owned output directory required") + provenance = source_commit(PROJECT) root.mkdir(parents=True, mode=0o700, exist_ok=False) cwd = root / "outside-source" cwd.mkdir(mode=0o700) @@ -326,9 +327,7 @@ def watch(): ) result = { "passed": True, - "source_commit": subprocess.check_output( - ["git", "rev-parse", "HEAD"], cwd=PROJECT, text=True - ).strip(), + "source_commit": provenance, "origins": origins, "actual_runtime_contexts": actual_contexts, "emitted_configuration_checked": configured is not None, diff --git a/jevdual/scripts/verify_onboarding_packet.py b/jevdual/scripts/verify_onboarding_packet.py new file mode 100644 index 0000000..5dea792 --- /dev/null +++ b/jevdual/scripts/verify_onboarding_packet.py @@ -0,0 +1,80 @@ +"""Verify extracted packet members before creating any qualification output.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +from pathlib import Path, PurePosixPath + + +def verify(root: Path) -> dict: + root = root.resolve() + manifest_path = root / "MANIFEST.json" + if manifest_path.is_symlink() or not manifest_path.is_file(): + raise ValueError("Manifest must be a regular file") + manifest = json.loads(manifest_path.read_text()) + if not isinstance(manifest, dict) or not re.fullmatch( + r"[0-9a-f]{40}", str(manifest.get("source_commit", "")) + ): + raise ValueError("Manifest requires a full immutable source commit") + members = manifest.get("members") + if not isinstance(members, dict) or not members: + raise ValueError("Manifest requires payload member hashes") + expected = {"MANIFEST.json"} + for name, digest in members.items(): + relative = PurePosixPath(name) + if ( + not relative.parts + or relative.parts[0] != "footwork" + or len(relative.parts) < 2 + or relative.is_absolute() + or ".." in relative.parts + or relative.as_posix() != name + or not isinstance(digest, str) + or not re.fullmatch(r"[0-9a-f]{64}", digest) + ): + raise ValueError("Invalid packet member or hash") + path = root.joinpath(*relative.parts[1:]) + if any(parent.is_symlink() for parent in (path, *path.parents) if parent != root.parent): + raise ValueError("Symlinked packet member") + if not path.is_file() or not path.resolve().is_relative_to(root): + raise ValueError("Missing or nonregular packet member") + if hashlib.sha256(path.read_bytes()).hexdigest() != digest: + raise ValueError("Packet member hash mismatch: " + name) + expected.add(path.relative_to(root).as_posix()) + actual = { + path.relative_to(root).as_posix() for path in root.rglob("*") if path.is_file() or path.is_symlink() + } + if actual != expected: + raise ValueError("Extracted packet inventory differs from manifest") + wheels = [ + digest + for name, digest in members.items() + if name.startswith("footwork/artifacts/") and name.endswith(".whl") + ] + if wheels != [manifest.get("wheel_sha256")]: + raise ValueError("Manifest wheel hash differs") + return { + "verified": True, + "members": len(actual), + "source_commit": manifest["source_commit"], + "wheel_sha256": manifest["wheel_sha256"], + } + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--packet-root", type=Path, required=True) + args = parser.parse_args() + try: + result = verify(args.packet_root) + except (ValueError, OSError, TypeError) as exc: + parser.exit(1, "Packet verification failed: " + str(exc) + "\n") + print(json.dumps(result)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/jevdual/tests/test_artifacts.py b/jevdual/tests/test_artifacts.py new file mode 100644 index 0000000..f8f2bca --- /dev/null +++ b/jevdual/tests/test_artifacts.py @@ -0,0 +1,91 @@ +"""External artifacts never acquire native/browser session provenance.""" + +from __future__ import annotations + +import hashlib +import json +import os +import subprocess +import sys + +from jevdual.artifacts import MAX_ARTIFACT_BYTES, check_artifact +from jevdual.cli import main + + +def test_exact_multiline_bytes_and_no_normalization(tmp_path): + expected = tmp_path / "reviewed.txt" + target = tmp_path / "note.txt" + text = b"PRIVATE_NOTE_LINE\nsecond line\n" + expected.write_bytes(text) + target.write_bytes(text) + digest = hashlib.sha256(text).hexdigest() + result = check_artifact(target, expected, digest) + assert result["ok"] and result["claims"] == ["current_artifact_byte_equality"] + assert "PRIVATE_NOTE_LINE" not in json.dumps(result) + for changed in (text.replace(b"\n", b"\r\n"), text[:-1], text + b" "): + target.write_bytes(changed) + assert not check_artifact(target, expected, digest)["ok"] + + +def test_source_digest_change_and_invalid_digest_fail_closed(tmp_path): + expected = tmp_path / "reviewed.txt" + target = tmp_path / "note.txt" + expected.write_bytes(b"before\n") + digest = hashlib.sha256(expected.read_bytes()).hexdigest() + expected.write_bytes(b"after\n") + target.write_bytes(expected.read_bytes()) + result = check_artifact(target, expected, digest) + assert not result["ok"] and "bound digest" in result["detail"] + for bad in ("invalid", "a" * 63, "A" * 64): + assert not check_artifact(target, expected, bad)["ok"] + + +def test_missing_symlink_nonregular_and_oversize_files(tmp_path): + expected = tmp_path / "expected" + target = tmp_path / "target" + expected.write_bytes(b"same") + digest = hashlib.sha256(b"same").hexdigest() + assert not check_artifact(target, expected, digest)["ok"] + target.symlink_to(expected) + assert not check_artifact(target, expected, digest)["ok"] + target.unlink() + target.mkdir() + assert not check_artifact(target, expected, digest)["ok"] + target.rmdir() + os.mkfifo(target) + assert not check_artifact(target, expected, digest)["ok"] + target.unlink() + target.write_bytes(b"x" * (MAX_ARTIFACT_BYTES + 1)) + assert not check_artifact(target, expected, digest)["ok"] + expected.write_bytes(target.read_bytes()) + digest = hashlib.sha256(expected.read_bytes()).hexdigest() + assert not check_artifact(target, expected, digest)["ok"] + + +def test_public_cli_exit_codes_and_output_do_not_contain_text(tmp_path, capsys): + expected, target = tmp_path / "expected", tmp_path / "target" + expected.write_bytes(b"PRIVATE_NOTE\n") + target.write_bytes(expected.read_bytes()) + digest = hashlib.sha256(expected.read_bytes()).hexdigest() + args = ["check-artifact", str(target), "--expected-file", str(expected), "--expected-sha256", digest] + assert main(args) == 0 + output = capsys.readouterr().out + assert json.loads(output)["ok"] and "PRIVATE_NOTE" not in output + target.write_bytes(b"different") + assert main(args) == 6 + assert not json.loads(capsys.readouterr().out)["ok"] + assert not (tmp_path / "session.json").exists() + + +def test_cli_fresh_process_without_credentials_or_native_driver(tmp_path): + expected, target = tmp_path / "expected", tmp_path / "target" + expected.write_bytes(b"multiline\nartifact\n") + target.write_bytes(expected.read_bytes()) + digest = hashlib.sha256(expected.read_bytes()).hexdigest() + code = "from jevdual.cli import main; import sys; sys.exit(main(sys.argv[1:]))" + # No credentials are read by this command and no Driver/model is constructed. + result = subprocess.run([sys.executable, "-c", code, "check-artifact", str(target), "--expected-file", + str(expected), "--expected-sha256", digest], capture_output=True, + text=True, check=False, timeout=15) + assert result.returncode == 0, result.stderr + assert json.loads(result.stdout)["ok"] diff --git a/jevdual/tests/test_capability_fixture.py b/jevdual/tests/test_capability_fixture.py index deebbf9..545886b 100644 --- a/jevdual/tests/test_capability_fixture.py +++ b/jevdual/tests/test_capability_fixture.py @@ -3,22 +3,63 @@ from __future__ import annotations import importlib.util +import json +import subprocess import sys +import time from pathlib import Path -from types import SimpleNamespace +from types import ModuleType, SimpleNamespace import pytest +@pytest.fixture(autouse=True) +def simulated_identity_timeout_api(monkeypatch): + # Scope tests use fake PIDs: never send an AX RPC to an unrelated real process. + if sys.platform == "darwin": + import ApplicationServices + else: + # Linux CI has no PyObjC framework. Model only this suite's explicit AX + # seam; unconfigured queries/actions must fail instead of granting access. + ApplicationServices = ModuleType("ApplicationServices") + + def unconfigured(*args): + raise AssertionError("synthetic AX call requires an explicit test response") + + for name in ( + "AXUIElementCreateApplication", + "AXUIElementCreateSystemWide", + "AXUIElementSetMessagingTimeout", + "AXUIElementCopyAttributeValue", + "AXUIElementPerformAction", + "AXIsProcessTrusted", + ): + setattr(ApplicationServices, name, unconfigured) + monkeypatch.setitem(sys.modules, "ApplicationServices", ApplicationServices) + + monkeypatch.setattr( + ApplicationServices, "AXUIElementCreateApplication", lambda pid: SimpleNamespace(pid=pid) + ) + monkeypatch.setattr( + ApplicationServices, "AXUIElementCreateSystemWide", lambda: SimpleNamespace(systemwide=True) + ) + monkeypatch.setattr(ApplicationServices, "AXUIElementSetMessagingTimeout", lambda *args: 0) + + def fixture_module(): path = Path(__file__).resolve().parents[1] / "scripts/capability_fixture.py" spec = importlib.util.spec_from_file_location("fixture_helper", path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) + # Every test-local timing override must stay off the shared stdlib module, + # including asyncio's event-loop clock and any background library threads. + module.time = SimpleNamespace(monotonic=time.monotonic, sleep=time.sleep) return module -@pytest.mark.skipif(sys.platform != "darwin", reason="macOS Accessibility readiness gate requires ApplicationServices") +@pytest.mark.skipif( + sys.platform != "darwin", reason="macOS Accessibility readiness gate requires ApplicationServices" +) def test_locked_fixture_refuses_before_creating_files(monkeypatch, tmp_path): import ApplicationServices from jevdual import posture @@ -92,3 +133,931 @@ def path(*_): with pytest.raises(ax.AXError, match="no access"): await module.scope_for(42, "http://127.0.0.1:1", tmp_path / "owned.txt") assert stopped == [True] + + +def test_cleanup_uses_ax_without_apple_events(monkeypatch, tmp_path): + module = fixture_module() + assert not hasattr(module, "script"), "fixture cleanup still invokes osascript/Automation" + assert callable(module.close_owned_document) + + +def test_browser_startup_has_separate_bounded_phase(): + module = fixture_module() + assert module.BROWSER_STARTUP_SECONDS == 180 + assert module.IDENTITY_SECONDS == 30 + assert callable(module.wait_browser_ready) + + +def test_missing_owned_document_is_unknown_not_closed(monkeypatch, tmp_path): + module = fixture_module() + import ApplicationServices + + monkeypatch.setattr(ApplicationServices, "AXUIElementSetMessagingTimeout", lambda *args: 0) + + monkeypatch.setattr(ApplicationServices, "AXUIElementCreateApplication", lambda pid: {}) + monkeypatch.setattr(ApplicationServices, "AXUIElementCopyAttributeValue", lambda el, key, _: (0, [])) + result = module.close_owned_document(43, tmp_path / "owned.txt") + assert result["status"] == "unknown" + assert result["reason"] == "document_not_observed" + + +def ax_world(monkeypatch, tmp_path, *, edited=False): + import ApplicationServices + + module = fixture_module() + monkeypatch.setattr(ApplicationServices, "AXUIElementSetMessagingTimeout", lambda *args: 0) + document = tmp_path / "owned.txt" + window = {"AXDocument": document.as_uri(), "AXRole": "AXWindow"} + other = {"AXDocument": (tmp_path / "elsewhere" / "owned.txt").as_uri(), "AXRole": "AXWindow"} + app = {"AXWindows": [window, other]} + close = {"AXRole": "AXButton"} + window["AXCloseButton"] = close + window["AXChildren"] = [] + sheet = {"AXRole": "AXSheet", "AXParent": window, "AXChildren": []} + discard = {"AXRole": "AXButton", "AXTitle": "Don't Save", "AXChildren": []} + sheet["AXChildren"].extend( + [ + discard, + {"AXRole": "AXButton", "AXTitle": "Save", "AXChildren": []}, + {"AXRole": "AXButton", "AXTitle": "Cancel", "AXChildren": []}, + ] + ) + pressed = [] + + def action(el, _): + pressed.append(el) + if el is close: + if edited: + window["AXSheets"] = [sheet] + else: + app["AXWindows"].remove(window) + elif el is discard: + app["AXWindows"].remove(window) + return 0 + + monkeypatch.setattr(ApplicationServices, "AXUIElementCreateApplication", lambda pid: app) + monkeypatch.setattr( + ApplicationServices, + "AXUIElementCopyAttributeValue", + lambda el, key, _: (0, el[key]) if key in el else (-25205, None), + ) + monkeypatch.setattr(ApplicationServices, "AXUIElementPerformAction", action) + return module, document, app, window, other, close, sheet, discard, pressed + + +def test_linux_without_native_framework_runs_exact_owned_cleanup(monkeypatch, tmp_path): + original = sys.modules["ApplicationServices"] + with monkeypatch.context() as linux: + linux.setattr(sys, "platform", "linux") + linux.setitem(sys.modules, "ApplicationServices", None) # Native import must fail. + simulated_identity_timeout_api.__wrapped__(linux) + api = sys.modules["ApplicationServices"] + assert isinstance(api, ModuleType) + assert api is not original + with pytest.raises(AssertionError, match="explicit test response"): + api.AXIsProcessTrusted() + with pytest.raises(AssertionError, match="explicit test response"): + api.AXUIElementPerformAction({}, "AXPress") + module, document, app, _window, other, close, _sheet, _discard, pressed = ax_world(linux, tmp_path) + result = module.close_owned_document(43, document) + assert result["status"] == "confirmed" + assert pressed == [close] + assert app["AXWindows"] == [other] + assert sys.modules["ApplicationServices"] is original + + +@pytest.mark.parametrize("edited", [False, True]) +def test_exact_owned_ax_window_closes_and_preserves_same_name(monkeypatch, tmp_path, edited): + module, document, app, _window, other, close, _sheet, discard, pressed = ax_world( + monkeypatch, tmp_path, edited=edited + ) + result = module.close_owned_document(43, document) + assert result["status"] == "confirmed" + assert app["AXWindows"] == [other] + assert pressed == ([close, discard] if edited else [close]) + + +def test_duplicate_exact_document_refuses_any_action(monkeypatch, tmp_path): + module, document, app, window, *_ = ax_world(monkeypatch, tmp_path) + app["AXWindows"].append(dict(window)) + result = module.close_owned_document(43, document) + assert result["reason"] == "document_ambiguous" + assert result["status"] == "error" + + +def test_identity_changed_before_close_refuses(monkeypatch, tmp_path): + module, document, _app, window, other, _close, _sheet, _discard, pressed = ax_world(monkeypatch, tmp_path) + original = module.owned_window + calls = [] + + def changed(*args): + calls.append(True) + if len(calls) == 2: + window["AXDocument"] = other["AXDocument"] + return original(*args) + + monkeypatch.setattr(module, "owned_window", changed) + assert module.close_owned_document(43, document)["reason"] == "document_identity_changed" + assert pressed == [] + + +def test_ax_windows_unreadable_after_press_is_not_absence(monkeypatch, tmp_path): + import ApplicationServices + + module, document, _app, _window, _other, _close, _sheet, _discard, pressed = ax_world( + monkeypatch, tmp_path + ) + original = ApplicationServices.AXUIElementCopyAttributeValue + + def attribute(el, key, arg): + if key == "AXWindows" and pressed: + return -25211, None + return original(el, key, arg) + + monkeypatch.setattr(ApplicationServices, "AXUIElementCopyAttributeValue", attribute) + assert module.close_owned_document(43, document)["status"] == "error" + + +@pytest.mark.parametrize( + "problem", ["unattached", "two_sheets", "two_buttons", "foreign_sheet", "failed_press"] +) +def test_discard_refuses_unbound_or_ambiguous_targets(monkeypatch, tmp_path, problem): + import ApplicationServices + + module, document, app, window, other, _close, sheet, discard, pressed = ax_world( + monkeypatch, tmp_path, edited=True + ) + if problem == "unattached": + sheet["AXParent"] = other + elif problem == "two_sheets": + window["AXSheets"] = [sheet, dict(sheet)] + elif problem == "two_buttons": + sheet["AXChildren"].append(dict(discard)) + elif problem == "foreign_sheet": + window["AXSheets"] = [{"AXRole": "AXSheet", "AXParent": other, "AXChildren": [discard]}] + else: + monkeypatch.setattr(ApplicationServices, "AXUIElementPerformAction", lambda *args: -25211) + # Preserve pre-existing malicious AXSheets across the mocked close action. + if problem in ("two_sheets", "foreign_sheet"): + original_sheets = window["AXSheets"] + monkeypatch.setattr( + ApplicationServices, + "AXUIElementPerformAction", + lambda el, _: pressed.append(el) or window.update(AXSheets=original_sheets) or 0, + ) + result = module.close_owned_document(43, document) + assert result["status"] == "error" + assert discard not in pressed + assert other in app["AXWindows"] + + +def test_changed_document_remaining_window_is_not_confirmed_disappearance(monkeypatch, tmp_path): + import ApplicationServices + + module, document, _app, window, other, _close, _sheet, _discard, _pressed = ax_world( + monkeypatch, tmp_path + ) + monkeypatch.setattr( + ApplicationServices, + "AXUIElementPerformAction", + lambda *args: window.update(AXDocument=other["AXDocument"]) or 0, + ) + assert module.close_owned_document(43, document)["reason"] == "document_identity_changed" + + +def fake_clock(monkeypatch, module, *, step=1): + ticks = iter(range(10000)) + monkeypatch.setattr( + module, "time", SimpleNamespace(monotonic=lambda: next(ticks) * step, sleep=lambda _: None) + ) + + +def test_fake_clock_preserves_process_wide_time_functions(monkeypatch): + real_monotonic, real_sleep = time.monotonic, time.sleep + module = fixture_module() + fake_clock(monkeypatch, module, step=2) + assert time.monotonic is real_monotonic + assert time.sleep is real_sleep + assert module.time is not time + assert [module.time.monotonic() for _ in range(3)] == [0, 2, 4] + + +def test_browser_readiness_waits_for_private_profile_endpoint(monkeypatch, tmp_path): + module = fixture_module() + fake_clock(monkeypatch, module) + profile = tmp_path / "chrome-profile" + profile.mkdir() + attempts = [] + browser = SimpleNamespace(pid=42, poll=lambda: None) + + def delay(_): + attempts.append(True) + if len(attempts) == 40: # Beyond the old thirty-second combined phase. + (profile / "DevToolsActivePort").write_text("1234\n/devtools/browser/owned\n") + + class Response: + def __enter__(self): + return self + + def __exit__(self, *args): + pass + + def read(self, limit): + return b'{"webSocketDebuggerUrl":"ws://127.0.0.1:1234/devtools/browser/owned"}' + + urls = [] + monkeypatch.setattr(module.time, "sleep", delay) + monkeypatch.setattr( + module, + "build_opener", + lambda _: SimpleNamespace(open=lambda url, timeout: urls.append(url) or Response()), + ) + assert module.wait_browser_ready(browser, profile)["pid"] == 42 + assert len(attempts) == 40 + assert urls == ["http://127.0.0.1:1234/json/version"] + + +@pytest.mark.parametrize("exited,reason", [(True, "browser_exited"), (False, "startup_timeout")]) +def test_browser_readiness_classifies_failure(monkeypatch, tmp_path, exited, reason): + module = fixture_module() + fake_clock(monkeypatch, module, step=60) + with pytest.raises(module.FixtureFailure) as failure: + module.wait_browser_ready(SimpleNamespace(pid=42, poll=lambda: 1 if exited else None), tmp_path) + assert failure.value.phase == "browser_startup" + assert failure.value.reason == reason + assert failure.value.exit_code == 3 + + +@pytest.mark.asyncio +async def test_identity_stays_strict_after_startup(monkeypatch, tmp_path): + import cua_driver + from jevdual import ax + + module = fixture_module() + elapsed = [0] + monkeypatch.setattr(module.time, "monotonic", lambda: elapsed[0]) + + async def advance(_): + elapsed[0] += 10 + + monkeypatch.setattr(module.asyncio, "sleep", advance) + + class Driver: + async def list_apps(self, _): + return SimpleNamespace(apps=[SimpleNamespace(name="TextEdit", pid=43)]) + + async def list_windows(self, args): + return SimpleNamespace(windows=[SimpleNamespace(window_id=7), SimpleNamespace(window_id=8)]) + + async def shutdown(self): + pass + + monkeypatch.setattr(cua_driver.CuaDriver, "create", lambda: Driver()) + monkeypatch.setattr(ax, "document_path", lambda *args: str(tmp_path / "owned.txt")) + with pytest.raises(module.FixtureFailure) as failure: + await module.scope_for(42, "http://127.0.0.1:1", tmp_path / "owned.txt") + assert failure.value.reason == "windows_not_unique" + assert failure.value.exit_code == 4 + + +def test_cleanup_ax_refusal_still_stops_owned_browser_listener(monkeypatch, tmp_path): + module = fixture_module() + calls = [] + + async def pid(): + return 43 + + monkeypatch.setattr(module, "textedit_pid", pid) + monkeypatch.setattr( + module, "close_owned_document", lambda *args: {"status": "error", "reason": "refused"} + ) + browser = SimpleNamespace( + pid=42, + returncode=0, + poll=lambda: None, + terminate=lambda: calls.append("terminate"), + wait=lambda timeout: calls.append("wait"), + ) + http = SimpleNamespace( + shutdown=lambda: calls.append("shutdown"), server_close=lambda: calls.append("close") + ) + alive = iter([True, False]) + thread = SimpleNamespace(is_alive=lambda: next(alive), join=lambda timeout: calls.append("join")) + logfile = SimpleNamespace(close=lambda: calls.append("log_close")) + result = module.cleanup(browser, http, thread, logfile, tmp_path / "owned.txt", opened=True, pid=43) + assert result["document"]["status"] == "error" + assert result["browser"]["status"] == result["listener"]["status"] == "confirmed" + assert calls == ["terminate", "wait", "shutdown", "close", "join", "log_close"] + + +def test_attached_delete_sheet_without_save_context_refuses(monkeypatch, tmp_path): + module, document, _app, _window, _other, close, sheet, discard, pressed = ax_world( + monkeypatch, tmp_path, edited=True + ) + discard["AXTitle"] = "Delete" + sheet["AXChildren"] = [discard] + result = module.close_owned_document(43, document) + assert result["reason"] == "sheet_not_save_prompt" + assert pressed == [close] + + +@pytest.mark.parametrize( + "stage,expected_code", + [("popen", 3), ("startup", 3), ("open", 5), ("identity", 4), ("identity_hang", 4), ("interrupt", 130)], +) +def test_setup_failures_always_stop_owned_resources(monkeypatch, tmp_path, stage, expected_code): + import ApplicationServices + from jevdual import posture + + module = fixture_module() + monkeypatch.setattr(posture, "screen_locked", lambda: False) + monkeypatch.setattr(ApplicationServices, "AXIsProcessTrusted", lambda: True) + root = tmp_path / "run" + monkeypatch.setattr(sys, "argv", ["fixture", "--run-dir", str(root)]) + calls = [] + alive = [False] + http = SimpleNamespace( + server_port=1234, + serve_forever=lambda: None, + shutdown=lambda: calls.append("listener_shutdown"), + server_close=lambda: calls.append("listener_close"), + ) + + class Thread: + def __init__(self, **kwargs): + pass + + def start(self): + alive[0] = True + + def is_alive(self): + return alive[0] + + def join(self, timeout): + alive[0] = False + + monkeypatch.setattr(module, "ThreadingHTTPServer", lambda *args: http) + monkeypatch.setattr(module.threading, "Thread", Thread) + browser = SimpleNamespace( + pid=42, + returncode=0, + poll=lambda: None, + terminate=lambda: calls.append("browser_terminate"), + wait=lambda timeout: calls.append("browser_wait"), + ) + + def popen(*args, **kwargs): + if stage == "popen": + raise OSError("launch refused") + return browser + + def ready(*args): + if stage == "startup": + raise module.FixtureFailure("browser_startup", "startup_timeout", 3) + if stage == "interrupt": + raise KeyboardInterrupt() + return {"status": "ready", "pid": 42, "port": 1234} + + def open_document(*args, **kwargs): + if stage == "open": + raise subprocess.CalledProcessError(1, "open") + + async def identity(*args): + raise module.FixtureFailure("identity", "windows_not_unique", 4) + + async def pid(): + return 43 + + monkeypatch.setattr(module.subprocess, "Popen", popen) + monkeypatch.setattr(module.subprocess, "run", open_document) + monkeypatch.setattr(module, "wait_browser_ready", ready) + cancelled = [] + if stage == "identity_hang": + import asyncio + + import cua_driver + + class Driver: + async def list_apps(self, _): + try: + await asyncio.Future() + finally: + cancelled.append(True) + + async def shutdown(self): + pass + + monkeypatch.setattr(cua_driver.CuaDriver, "create", lambda: Driver()) + monkeypatch.setattr(module, "IDENTITY_SECONDS", 0.02) + else: + monkeypatch.setattr(module, "scope_for", identity) + monkeypatch.setattr(module, "textedit_pid", pid) + monkeypatch.setattr( + module, "close_owned_document", lambda *args: {"status": "unknown", "reason": "not_observed"} + ) + with pytest.raises(SystemExit) as failure: + module.main() + assert failure.value.code == expected_code + status = json.loads((root / "fixture-status.json").read_text()) + cleanup = json.loads((root / "cleanup.json").read_text()) + assert status["exit_code"] == expected_code + if stage == "identity_hang": + assert status["reason"] == "sdk_timeout" + assert cancelled == [True] + assert not (root / "scope.json").exists() + assert "listener_shutdown" in calls and "listener_close" in calls + assert cleanup["listener"]["status"] == "confirmed" + if stage != "popen": + assert "browser_terminate" in calls and "browser_wait" in calls + assert cleanup["browser"]["status"] == "confirmed" + + +def test_changed_textedit_pid_refuses_document_cleanup_and_still_stops_browser(monkeypatch, tmp_path): + module = fixture_module() + touched = [] + + async def pid(): + return 99 + + monkeypatch.setattr(module, "textedit_pid", pid) + monkeypatch.setattr(module, "close_owned_document", lambda *args: touched.append(True)) + browser = SimpleNamespace(pid=42, returncode=0, poll=lambda: 0, wait=lambda timeout: None) + result = module.cleanup(browser, None, None, None, tmp_path / "owned.txt", opened=True, pid=43) + assert result["document"]["reason"] == "textedit_process_changed" + assert not touched + assert result["browser"]["status"] == "confirmed" + + +def test_unresponsive_ax_has_rpc_timeout_and_no_confirmed_cleanup(monkeypatch, tmp_path): + import ApplicationServices + + module, document, *_ = ax_world(monkeypatch, tmp_path) + timeouts = [] + monkeypatch.setattr( + ApplicationServices, + "AXUIElementSetMessagingTimeout", + lambda el, timeout: timeouts.append(timeout) or 0, + ) + monkeypatch.setattr(ApplicationServices, "AXUIElementCopyAttributeValue", lambda *args: (-25204, None)) + result = module.close_owned_document(43, document) + assert result["status"] == "error" + assert timeouts and all(0 < timeout <= 1 for timeout in timeouts) + + +def test_save_sheet_traversal_deadline_stops_before_discard(monkeypatch, tmp_path): + module, document, _app, _window, _other, _close, sheet, discard, pressed = ax_world( + monkeypatch, tmp_path, edited=True + ) + ticks = iter([0] + [0.1] * 22 + [11] * 100) + monkeypatch.setattr(module.time, "monotonic", lambda: next(ticks)) + sheet["AXChildren"] += [{"AXRole": "AXGroup", "AXChildren": []} for _ in range(100)] + result = module.close_owned_document(43, document) + assert result["status"] == "error" + assert result["reason"] == "cleanup_deadline" + assert discard not in pressed + + +def test_main_automatic_ax_cleanup_with_automation_denied(monkeypatch, tmp_path): + import ApplicationServices + from jevdual import posture + + root = tmp_path / "run" + module, _document, app, window, other, _close, _sheet, _discard, _pressed = ax_world(monkeypatch, root) + window["AXDocument"] = (root / "run-invoice.txt").as_uri() + monkeypatch.setattr(posture, "screen_locked", lambda: False) + monkeypatch.setattr(ApplicationServices, "AXIsProcessTrusted", lambda: True) + monkeypatch.setattr(sys, "argv", ["fixture", "--run-dir", str(root)]) + stopped = [] + alive = [False] + http = SimpleNamespace( + server_port=1234, + serve_forever=lambda: None, + shutdown=lambda: stopped.append("http_shutdown"), + server_close=lambda: stopped.append("http_close"), + ) + + class Thread: + def __init__(self, **kwargs): + pass + + def start(self): + alive[0] = True + + def is_alive(self): + return alive[0] + + def join(self, timeout): + alive[0] = False + + browser = SimpleNamespace( + pid=42, + returncode=0, + poll=lambda: None, + terminate=lambda: stopped.append("browser"), + wait=lambda timeout: None, + ) + + def run(command, **kwargs): + if command[0] == "osascript": + raise subprocess.CalledProcessError(1, command) # Automation explicitly denied. + return SimpleNamespace(stdout="") + + async def scope(*args): + return {"destination": {"pid": 43}, "source": {"pid": 42}} + + async def pid(): + return 43 + + def stop(_): + raise KeyboardInterrupt() + + monkeypatch.setattr(module, "ThreadingHTTPServer", lambda *args: http) + monkeypatch.setattr(module.threading, "Thread", Thread) + monkeypatch.setattr(module.subprocess, "Popen", lambda *args, **kwargs: browser) + monkeypatch.setattr(module.subprocess, "run", run) + monkeypatch.setattr(module, "scope_for", scope) + if hasattr(module, "wait_browser_ready"): + monkeypatch.setattr(module, "wait_browser_ready", lambda *args: {"status": "ready"}) + monkeypatch.setattr(module, "textedit_pid", pid) + monkeypatch.setattr(module.time, "sleep", stop) + module.main() + assert app["AXWindows"] == [other], "Automation-denied old cleanup leaves the owned AX window open" + assert "browser" in stopped and "http_shutdown" in stopped + assert json.loads((root / "cleanup.json").read_text())["document"]["status"] == "confirmed" + + +@pytest.mark.asyncio +async def test_indefinite_sdk_enumeration_is_cancelled_then_owned_cleanup_runs(monkeypatch, tmp_path): + import asyncio + + import cua_driver + + module = fixture_module() + monkeypatch.setattr(module, "IDENTITY_SECONDS", 0.02) + cancelled, shutdown = [], [] + + class Driver: + async def list_apps(self, _): + try: + await asyncio.Future() + finally: + cancelled.append(True) + + async def shutdown(self): + shutdown.append(True) + + monkeypatch.setattr(cua_driver.CuaDriver, "create", lambda: Driver()) + with pytest.raises(module.FixtureFailure) as failure: + await module.scope_for(42, "http://127.0.0.1:1", tmp_path / "owned.txt") + assert failure.value.reason == "sdk_timeout" + assert failure.value.exit_code == 4 + assert cancelled == [True] and shutdown == [True] + stopped = [] + browser = SimpleNamespace( + pid=42, + returncode=0, + poll=lambda: None, + terminate=lambda: stopped.append("browser"), + wait=lambda timeout: None, + ) + http = SimpleNamespace(server_close=lambda: stopped.append("listener")) + result = module.cleanup(browser, http, None, None, tmp_path / "owned.txt", opened=False, pid=None) + assert result["browser"]["status"] == result["listener"]["status"] == "confirmed" + assert stopped == ["browser", "listener"] + assert not [ + task for task in asyncio.all_tasks() if task is not asyncio.current_task() and not task.done() + ] + + +@pytest.mark.asyncio +async def test_indefinite_driver_shutdown_is_cancelled(monkeypatch): + import asyncio + + module = fixture_module() + cancelled = [] + + class Driver: + async def shutdown(self): + try: + await asyncio.Future() + finally: + cancelled.append(True) + + with pytest.raises(module.FixtureFailure) as failure: + await module.shutdown_driver(Driver(), "document_cleanup", module.time.monotonic() + 0.02) + assert failure.value.reason == "sdk_timeout" + assert cancelled == [True] + assert not [ + task for task in asyncio.all_tasks() if task is not asyncio.current_task() and not task.done() + ] + + +@pytest.mark.asyncio +async def test_scope_sets_process_timeout_and_rejects_late_document_identity(monkeypatch, tmp_path): + import ApplicationServices + import cua_driver + from jevdual import ax + + module = fixture_module() + document = tmp_path / "owned.txt" + elapsed, timeouts = [0], [] + monkeypatch.setattr(module.time, "monotonic", lambda: elapsed[0]) + monkeypatch.setattr( + ApplicationServices, + "AXUIElementSetMessagingTimeout", + lambda app, timeout: timeouts.append((app.systemwide, timeout)) or 0, + ) + + class Driver: + async def list_apps(self, _): + return SimpleNamespace(apps=[SimpleNamespace(name="TextEdit", pid=43)]) + + async def list_windows(self, args): + return SimpleNamespace(windows=[SimpleNamespace(window_id=7)]) + + async def shutdown(self): + pass + + def path(*args): + elapsed[0] = 31 # Synchronous native call returned beyond identity deadline. + return str(document) + + monkeypatch.setattr(cua_driver.CuaDriver, "create", lambda: Driver()) + monkeypatch.setattr(ax, "document_path", path) + with pytest.raises(module.FixtureFailure) as failure: + await module.scope_for(42, "http://127.0.0.1:1", document) + assert failure.value.reason == "identity_deadline" + assert failure.value.exit_code == 4 + assert timeouts == [(True, 1.0), (True, 0)] + + +def test_cleanup_snapshots_actual_file_bytes_before_and_after_close(monkeypatch, tmp_path): + import hashlib + + module = fixture_module() + document = tmp_path / "owned.txt" + before, after = b"actual on disk\n", b"different actual bytes\n" + document.write_bytes(before) + + async def pid(): + return 43 + + def close(*args): + document.write_bytes(after) + return {"status": "confirmed", "reason": "window_disappeared"} + + monkeypatch.setattr(module, "textedit_pid", pid) + monkeypatch.setattr(module, "close_owned_document", close) + result = module.cleanup(None, None, None, None, document, opened=True, pid=43)["document"] + assert result["file_before"] == { + "status": "observed", + "size_bytes": len(before), + "sha256": hashlib.sha256(before).hexdigest(), + } + assert result["file_after"] == { + "status": "observed", + "size_bytes": len(after), + "sha256": hashlib.sha256(after).hexdigest(), + } + assert "text" not in result["file_before"] + + +@pytest.mark.parametrize("kind", ["missing", "symlink", "large", "fifo"]) +def test_snapshot_refuses_unowned_or_unbounded_file_types(tmp_path, kind): + import os + + module = fixture_module() + document = tmp_path / "owned.txt" + if kind == "symlink": + target = tmp_path / "other.txt" + target.write_text("unrelated") + document.symlink_to(target) + elif kind == "large": + document.write_bytes(b"x" * (module.MAX_DOCUMENT_BYTES + 1)) + elif kind == "fifo": + os.mkfifo(document) + snapshot = module.document_snapshot(document) + assert snapshot["status"] != "observed" + assert "sha256" not in snapshot + if kind == "missing": + assert snapshot["status"] == "absent" + elif kind in ("large", "fifo"): + assert snapshot["status"] == "refused" + + +@pytest.mark.parametrize("cleanup_fails", [False, True]) +def test_second_sigint_is_ignored_only_during_cleanup_and_handler_restored( + monkeypatch, tmp_path, cleanup_fails +): + import signal + + import ApplicationServices + from jevdual import posture + + module = fixture_module() + previous = signal.getsignal(signal.SIGINT) + monkeypatch.setattr(posture, "screen_locked", lambda: False) + monkeypatch.setattr(ApplicationServices, "AXIsProcessTrusted", lambda: True) + monkeypatch.setattr(sys, "argv", ["fixture", "--run-dir", str(tmp_path / "run")]) + # Fail before owning a server; only the final-cleanup SIGINT guard is exercised. + monkeypatch.setattr(module, "ThreadingHTTPServer", lambda *args: (_ for _ in ()).throw(OSError())) + observed = [] + + def cleanup(*args, **kwargs): + observed.append(signal.getsignal(signal.SIGINT)) + signal.raise_signal(signal.SIGINT) # Ignored: no second KeyboardInterrupt escapes. + if cleanup_fails: + raise RuntimeError("cleanup receipt fault") + return { + "document": {"status": "not_started"}, + "browser": {"status": "not_started"}, + "listener": {"status": "not_started"}, + } + + monkeypatch.setattr(module, "cleanup", cleanup) + with pytest.raises(RuntimeError if cleanup_fails else SystemExit): + module.main() + assert observed == [signal.SIG_IGN] + assert signal.getsignal(signal.SIGINT) == previous + + +@pytest.mark.asyncio +@pytest.mark.parametrize("outcome", ["success", "identity_failure", "shutdown_failure", "setter_failure"]) +async def test_identity_uses_global_ax_reference_and_resets_default_even_on_shutdown_failure( + monkeypatch, tmp_path, outcome +): + import ApplicationServices + import cua_driver + from jevdual import ax + + module = fixture_module() + global_reference = object() + calls = [] + document = tmp_path / "owned.txt" + monkeypatch.setattr(ApplicationServices, "AXUIElementCreateSystemWide", lambda: global_reference) + + def timeout(element, seconds): + assert element is global_reference # An application-element timeout would not cover new references. + calls.append(seconds) + return -25202 if outcome == "setter_failure" and seconds else 0 + + monkeypatch.setattr(ApplicationServices, "AXUIElementSetMessagingTimeout", timeout) + + class Driver: + async def list_apps(self, _): + return SimpleNamespace(apps=[SimpleNamespace(name="TextEdit", pid=43)]) + + async def list_windows(self, args): + return SimpleNamespace(windows=[SimpleNamespace(window_id=7)]) + + async def shutdown(self): + if outcome == "shutdown_failure": + raise RuntimeError("SDK shutdown refused") + + def path(*args): + if outcome == "identity_failure": + raise ax.AXError("permission_denied", "AX lookup refused") + return str(document) + + monkeypatch.setattr(cua_driver.CuaDriver, "create", lambda: Driver()) + monkeypatch.setattr(ax, "document_path", path) + if outcome == "success": + result = await module.scope_for(42, "http://127.0.0.1:1", document) + assert result["destination"]["document"] == str(document) + else: + expected = ( + ax.AXError + if outcome == "identity_failure" + else RuntimeError + if outcome == "shutdown_failure" + else module.FixtureFailure + ) + with pytest.raises(expected): + await module.scope_for(42, "http://127.0.0.1:1", document) + assert calls == [1.0, 0] + + +@pytest.mark.parametrize("stale_target", ["child", "window"]) +def test_close_stale_observation_requires_fresh_readable_absence(monkeypatch, tmp_path, stale_target): + import ApplicationServices + + module, document, app, window, other, close, _, _, pressed = ax_world(monkeypatch, tmp_path) + child = {"AXRole": "AXGroup", "AXChildren": []} + window["AXChildren"] = [child] + original = ApplicationServices.AXUIElementCopyAttributeValue + observations = [] + + def press_close(element, _): + pressed.append(element) + return 0 # Closing is asynchronous; AX reference survives the press briefly. + + def attribute(element, key, arg): + if key == "AXWindows": + observations.append(list(app["AXWindows"])) + stale = ( + (element is child and key == "AXRole") + if stale_target == "child" + else (element is window and key == "AXDocument") + ) + if pressed and stale: + app["AXWindows"].remove(window) + return -25202, None + return original(element, key, arg) + + monkeypatch.setattr(ApplicationServices, "AXUIElementPerformAction", press_close) + monkeypatch.setattr(ApplicationServices, "AXUIElementCopyAttributeValue", attribute) + monkeypatch.setattr(module.time, "sleep", lambda _: None) + result = module.close_owned_document(43, document) + assert result["status"] == "confirmed", result + assert result["reason"] == "window_disappeared" + assert observations[-1] == [other] # A new successful AXWindows read certifies absence. + assert pressed == [close] # Never press the stale reference again. + + +@pytest.mark.parametrize("fresh", ["persistent_invalid", "unreadable", "renamed", "reused"]) +def test_close_stale_retry_refuses_unconfirmed_or_changed_identity(monkeypatch, tmp_path, fresh): + import ApplicationServices + + module, document, app, window, other, close, _, _, pressed = ax_world(monkeypatch, tmp_path) + child = {"AXRole": "AXGroup", "AXChildren": []} + window["AXChildren"] = [child] + original = ApplicationServices.AXUIElementCopyAttributeValue + stale, invalid_reads, elapsed = [False], [0], [0] + + def clock(): + value = elapsed[0] + elapsed[0] += 0.1 + return value + + monkeypatch.setattr(module.time, "monotonic", clock) + monkeypatch.setattr(module.time, "sleep", lambda _: None) + monkeypatch.setattr( + ApplicationServices, "AXUIElementPerformAction", lambda el, _: pressed.append(el) or 0 + ) + + def attribute(element, key, arg): + if pressed and element is child and key == "AXRole": + stale[0] = True + invalid_reads[0] += 1 + if fresh == "renamed": + window["AXDocument"] = other["AXDocument"] + elif fresh == "reused": + replacement = dict(window) + replacement["AXCloseButton"] = {"AXRole": "AXButton", "different": True} + app["AXWindows"] = [replacement, other] + return -25202, None + if stale[0] and fresh == "unreadable" and key == "AXWindows": + return -25211, None + return original(element, key, arg) + + monkeypatch.setattr(ApplicationServices, "AXUIElementCopyAttributeValue", attribute) + result = module.close_owned_document(43, document) + assert result["status"] != "confirmed" + assert pressed == [close] + assert other in app["AXWindows"] + if fresh in ("renamed", "reused"): + assert result["reason"] == "document_identity_changed" + elif fresh == "unreadable": + assert result["reason"] == "ax_attribute_refused:AXWindows:-25211" + + if fresh == "persistent_invalid": + assert invalid_reads[0] > 1 + assert elapsed[0] < module.CLEANUP_SECONDS + 0.3 + + +def test_invalid_element_before_close_never_retries_or_presses(monkeypatch, tmp_path): + import ApplicationServices + + module, document, _, window, _, _, _, _, pressed = ax_world(monkeypatch, tmp_path) + original = ApplicationServices.AXUIElementCopyAttributeValue + invalid_reads = [] + + def attribute(element, key, arg): + if element is window and key == "AXDocument": + invalid_reads.append(True) + return -25202, None + return original(element, key, arg) + + monkeypatch.setattr(ApplicationServices, "AXUIElementCopyAttributeValue", attribute) + result = module.close_owned_document(43, document) + assert result["status"] == "error" + assert invalid_reads == [True] and not pressed + + +def test_invalid_axpress_is_not_observation_retry(monkeypatch, tmp_path): + import ApplicationServices + + module, document, _, _, _, close, _, discard, pressed = ax_world(monkeypatch, tmp_path, edited=True) + original = ApplicationServices.AXUIElementPerformAction + + def action(element, name): + if element is discard: + pressed.append(element) + return -25202 + return original(element, name) + + monkeypatch.setattr(ApplicationServices, "AXUIElementPerformAction", action) + result = module.close_owned_document(43, document) + assert result["status"] == "error" + assert result["reason"] == "ax_press_refused:-25202" + assert pressed == [close, discard] diff --git a/jevdual/tests/test_capability_installed_agent_trial.py b/jevdual/tests/test_capability_installed_agent_trial.py new file mode 100644 index 0000000..594ee0d --- /dev/null +++ b/jevdual/tests/test_capability_installed_agent_trial.py @@ -0,0 +1,193 @@ +"""Pure host output, isolation and readonly completion boundary checks.""" + +from __future__ import annotations + +import importlib.util +import json +from pathlib import Path +from types import SimpleNamespace + +import pytest + + +@pytest.fixture +def harness(monkeypatch): + scripts = Path(__file__).resolve().parents[1] / "scripts" + monkeypatch.syspath_prepend(str(scripts)) + spec = importlib.util.spec_from_file_location( + "installed_agent_trial", scripts / "capability_installed_agent_trial.py" + ) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def generated_job(tmp_path): + python = tmp_path / "install/venv/bin/python" + job = tmp_path / "job" + job.mkdir() + scope, journal, host = job / "scope.json", job / "operations.sqlite", job / "host.json" + scope.write_text(json.dumps({"task": "task_fixture"})) + config = { + "mcpServers": { + "footwork": { + "command": str(python), + "args": [ + "-I", + "-m", + "jevdual.capability.cli", + "server", + "--scope", + str(scope), + "--journal", + str(journal), + ], + "env": { + "PYTHONPATH": "", + "PYTHONHOME": "", + "PYTHONNOUSERSITE": "1", + "ANONYMIZED_TELEMETRY": "false", + "BROWSER_USE_CLOUD_SYNC": "false", + }, + } + } + } + host.write_text(json.dumps(config)) + manifest = { + "task": "task_fixture", + "scope": str(scope), + "host_config": str(host), + "report_schema": str(job / "report-schema.json"), + "report_adapter": [ + str(python), + "-I", + "-m", + "jevdual.capability.cli", + "report", + "--journal", + str(journal), + "--task", + "task_fixture", + ], + } + (job / "run.json").write_text(json.dumps(manifest)) + (job / "report-schema.json").write_text(json.dumps({"type": "object"})) + return python, scope, journal, host + + +def test_emitted_mcp_environment_contains_only_public_overrides(harness, monkeypatch, tmp_path): + python, scope, journal, host = generated_job(tmp_path) + monkeypatch.setenv("QUALIFICATION_OPAQUE", "opaque-not-for-serialization") + monkeypatch.setenv("QUALIFICATION_TOKEN", "opaque-not-for-serialization") + config, _, _ = harness.installed_configuration( + python, scope, journal, host, tmp_path / "install", tmp_path / "audit" + ) + encoded = json.dumps(config) + assert "opaque-not-for-serialization" not in encoded + assert "QUALIFICATION_OPAQUE" not in encoded + env = config["mcpServers"]["footwork"]["env"] + assert env["HOME"] == str(tmp_path / "install/private-home") + assert env["PYTHONPATH"] == "" + assert env["FOOTWORK_RUNTIME_AUDIT_DIR"] == str(tmp_path / "audit") + assert env["QUALIFICATION_TOKEN"] == "" + + +@pytest.mark.parametrize("cost", [None, "0.25", True, -0.01, float("nan"), float("inf"), 2.01]) +def test_unavailable_or_invalid_cost_cannot_qualify_trial(harness, cost): + evidence = harness.cost_evidence(cost) + assert not evidence["valid"] + assert evidence["configured_softcap_usd"] == 1.0 + assert evidence["scheduling_reserve_usd"] == 2.0 + + +@pytest.mark.parametrize("cost", [0, 0.25, 1.5, 2.0]) +def test_actual_finite_cost_uses_scheduling_reserve_not_softcap(harness, cost): + evidence = harness.cost_evidence(cost) + assert evidence["valid"] + assert evidence["reported_cost_usd"] == cost + + +def test_changed_adapter_is_rejected_before_dispatch(harness, tmp_path): + python, scope, journal, host = generated_job(tmp_path) + path = host.parent / "run.json" + manifest = json.loads(path.read_text()) + manifest["report_adapter"] = [str(python), "-c", "effect()"] + path.write_text(json.dumps(manifest)) + with pytest.raises(RuntimeError, match="report adapter"): + harness.installed_configuration(python, scope, journal, host, tmp_path, tmp_path) + + +@pytest.mark.parametrize("extra", [[], ["StructuredOutput"], ["Bash"], ["StructuredOutput", "Bash"]]) +def test_schema_finalizer_does_not_admit_effect_tools(harness, tmp_path, extra): + path = tmp_path / "transcript.jsonl" + events = [ + { + "type": "system", + "subtype": "init", + "tools": [*harness.TOOLS, *extra], + "model": "claude-opus-5-5", + "mcp_servers": [{"name": "footwork"}], + }, + { + "type": "assistant", + "message": { + "content": [{"type": "tool_use", "name": name} for name in [harness.TOOLS[0], *extra]] + }, + }, + {"type": "result", "is_error": False, "structured_output": {"completion": "completed"}}, + ] + path.write_text("\n".join(json.dumps(event) for event in events)) + evidence, claims = harness.transcript_evidence(path) + assert evidence["valid_environment"] is ("Bash" not in extra) + assert evidence["out_of_arm"] == (["Bash"] if "Bash" in extra else []) + assert "StructuredOutput" not in evidence["effect_calls"] + assert claims == {"completion": "completed"} + + +def test_completed_host_prose_cannot_replace_rejected_structured_claims(harness, monkeypatch, tmp_path): + rejection = { + "validation": {"accepted": False}, + "operation_completion": "unknown", + "trusted_summary": "Unavailable", + } + monkeypatch.setattr( + harness.subprocess, + "run", + lambda *args, **kwargs: SimpleNamespace(returncode=0, stdout=json.dumps(rejection), stderr=""), + ) + result = harness.validate_report(["installed-readonly-adapter"], None, {}, tmp_path, tmp_path) + assert not result["accepted"] + assert json.loads((tmp_path / "structured-output.json").read_text()) is None + assert json.loads((tmp_path / "validated.json").read_text()) == rejection + assert not (tmp_path / "published.txt").exists() + + +def test_runtime_audit_rejects_borrowed_module_even_with_private_home(harness, tmp_path): + audit = tmp_path / "audit" + audit.mkdir() + python = tmp_path / "install/venv/bin/python" + prefix = python.parent.parent + journal = tmp_path / "job/operations.sqlite" + registrations = Path(str(journal) + ".executions") / "op_fixture" + registrations.mkdir(parents=True) + (registrations / "registration.json").write_text(json.dumps({"pid": 1234})) + for role in ("server", "worker"): + context = { + "role": role, + "pid": 1234, + "prefix": str(prefix), + "python": str(python), + "home": str(tmp_path / "install/private-home"), + "user_site_disabled": True, + "browser_telemetry_disabled": True, + "browser_cloud_sync_disabled": True, + "cua_library": str(prefix / "lib/libcua.dylib"), + "modules": { + "jevdual": [str(tmp_path / "source/jevdual/__init__.py")], + "cua_driver": [str(prefix / "lib/cua_driver/__init__.py")], + "ApplicationServices": [str(prefix / "lib/ApplicationServices/__init__.py")], + }, + } + (audit / (role + ".json")).write_text(json.dumps(context)) + evidence = harness.runtime_evidence(audit, python, tmp_path / "install", journal) + assert evidence["failures"] == ["module outside installed prefix"] * 2 diff --git a/jevdual/tests/test_onboarding_packet.py b/jevdual/tests/test_onboarding_packet.py new file mode 100644 index 0000000..f26de86 --- /dev/null +++ b/jevdual/tests/test_onboarding_packet.py @@ -0,0 +1,333 @@ +"""Standalone qualification receipts must not require a guest Git installation.""" + +from __future__ import annotations + +import hashlib +import importlib.util +import json +import os +import stat +import subprocess +import sys +import zipfile +from pathlib import Path +from types import SimpleNamespace + +import pytest + +SCRIPTS = Path(__file__).resolve().parents[1] / "scripts" +COMMIT = "a" * 40 + + +def installer(): + spec = importlib.util.spec_from_file_location("packet_installer", SCRIPTS / "capability_clean_install.py") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def test_packet_installer_writes_receipt_without_git(tmp_path, monkeypatch): + module = installer() + project = tmp_path / "footwork" + project.mkdir() + (project / "MANIFEST.json").write_text(json.dumps({"source_commit": COMMIT})) + wheel = project / "fixture.whl" + wheel.write_bytes(b"qualification fixture") + monkeypatch.setattr(module, "PROJECT", project) + monkeypatch.setattr(module.shutil, "which", lambda name: "fixture-uv") + + def no_git(*args, **kwargs): + raise RuntimeError("Guest Git must never be invoked") + + monkeypatch.setattr(module.subprocess, "check_output", no_git) + # Stop at the external uv boundary; execute the real installer receipt path. + monkeypatch.setattr(module.subprocess, "run", lambda *args, **kwargs: SimpleNamespace(returncode=17)) + monkeypatch.setattr( + sys, + "argv", + [ + "install", + "--python", + sys.executable, + "--wheel", + str(wheel), + "--run-dir", + str(project / "output/install"), + ], + ) + assert module.main() == 1 + receipt = json.loads((project / "output/install/receipt.json").read_text()) + assert receipt["source_commit"] == COMMIT + assert receipt["commands"][0]["stage"] == "venv" + assert receipt["commands"][0]["returncode"] == 17 + + +def test_manifest_overrides_enclosing_repository(tmp_path): + subprocess.run(["git", "init", "--quiet", str(tmp_path)], check=True) + project = tmp_path / "footwork" + project.mkdir() + (project / "MANIFEST.json").write_text(json.dumps({"source_commit": COMMIT})) + assert installer().source_commit(project) == COMMIT + + +@pytest.mark.parametrize("value", ["bad", None, 123, "a" * 39]) +def test_bad_packet_provenance_refuses_without_git(tmp_path, monkeypatch, value): + module = installer() + (tmp_path / "MANIFEST.json").write_text(json.dumps({"source_commit": value})) + monkeypatch.setattr(module.subprocess, "check_output", lambda *a, **k: pytest.fail("Git was invoked")) + with pytest.raises(ValueError, match="source commit"): + module.source_commit(tmp_path) + + +def test_no_manifest_or_owned_git_returns_unavailable(tmp_path, monkeypatch): + module = installer() + monkeypatch.setattr(module.subprocess, "check_output", lambda *a, **k: pytest.fail("Git was invoked")) + assert module.source_commit(tmp_path) == "unavailable" + + +def test_checkout_provenance_remains_available(): + project = SCRIPTS.parents[1] + expected = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=project, text=True).strip() + assert installer().source_commit(project) == expected + + +def test_native_bad_manifest_refuses_before_resource_creation(tmp_path, monkeypatch): + monkeypatch.syspath_prepend(str(SCRIPTS)) + import capability_installed_trial as trial + + (tmp_path / "MANIFEST.json").write_text(json.dumps({"source_commit": "invalid"})) + monkeypatch.setattr(trial, "PROJECT", tmp_path) + monkeypatch.setattr(trial, "InstalledServer", lambda *a, **k: pytest.fail("Server must not start")) + monkeypatch.setattr( + sys, + "argv", + [ + "trial", + "--python", + sys.executable, + "--scope", + str(tmp_path / "absent-scope.json"), + "--run-dir", + str(tmp_path / "output/native"), + ], + ) + with pytest.raises(ValueError, match="source commit"): + trial.main() + assert not (tmp_path / "output").exists() + + +@pytest.mark.parametrize("mutation", [None, "modified", "extra", "symlink", "escape"]) +def test_guest_verifier_checks_extracted_payload(tmp_path, mutation): + root = tmp_path / "footwork" + (root / "artifacts").mkdir(parents=True) + wheel = root / "artifacts/fixture.whl" + wheel.write_bytes(b"fixture wheel") + digest = hashlib.sha256(wheel.read_bytes()).hexdigest() + manifest = { + "source_commit": COMMIT, + "wheel_sha256": digest, + "members": {"footwork/artifacts/fixture.whl": digest}, + } + if mutation == "modified": + wheel.write_bytes(b"modified wheel") + elif mutation == "extra": + (root / "unexpected.txt").write_text("extra") + elif mutation == "symlink": + outside = tmp_path / "outside.whl" + wheel.rename(outside) + wheel.symlink_to(outside) + elif mutation == "escape": + manifest["members"]["footwork/../outside.whl"] = digest + (root / "MANIFEST.json").write_text(json.dumps(manifest)) + run = subprocess.run( + [sys.executable, "-I", str(SCRIPTS / "verify_onboarding_packet.py"), "--packet-root", str(root)], + capture_output=True, + text=True, + check=False, + ) + if mutation is None: + assert run.returncode == 0, run.stderr + assert json.loads(run.stdout)["source_commit"] == COMMIT + else: + assert run.returncode == 1 + assert "verification failed" in run.stderr + + +def test_frozen_packet_uses_committed_runtime_after_later_changes(tmp_path, monkeypatch): + monkeypatch.syspath_prepend(str(SCRIPTS)) + import build_capability_wheel as wheels + import build_onboarding_packet as packets + + project = tmp_path / "project" + project.mkdir() + package = project / "jevdual" + package.mkdir() + (project / "LICENSE").write_text("MIT fixture license") + (package / "pyproject.toml").write_text( + '[project]\nname="jevdual"\nversion="0.0.1"\ndescription="fixture"\n' + 'requires-python=">=3.11"\ndependencies=[]\n' + '[project.scripts]\nfootwork-capability="jevdual.capability.cli:main"\n' + ) + for relative in wheels.REQUIRED: + path = package / "python" / relative + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("# frozen runtime fixture\n") + for relative in ( + "docs/independent-onboarding.md", + "docs/onboarding-vm-plan.md", + "docs/capability-packaging.md", + *("scripts/" + name for name in packets.HELPERS), + ): + path = package / relative + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text("frozen helper or instructions\n") + + def git(*arguments): + return subprocess.check_output( + ["git", "-c", "user.name=Packet Fixture", "-c", "user.email=fixture@example.invalid", *arguments], + cwd=project, + text=True, + ).strip() + + git("init", "--quiet") + git("add", "LICENSE", "jevdual") + git("commit", "--quiet", "-m", "freeze fixture") + frozen = git("rev-parse", "HEAD") + monkeypatch.setattr(packets, "PROJECT", project) + wheel = wheels.build(package, tmp_path / "wheel") + packet = packets.build(wheel, tmp_path / "packet") + packet_bytes = packet.read_bytes() + + (package / "python/jevdual/capability/cli.py").write_text("# changed runtime\n") + (package / "python/jevdual/later.py").write_text("# added after qualification\n") + git("add", "jevdual") + git("commit", "--quiet", "-m", "later runtime") + with pytest.raises(ValueError, match="inventory"): + wheels.verify(wheel, package) + assert packets.verify(packet, wheel, frozen)["source_commit"] == frozen + with pytest.raises(ValueError, match="inventory"): + packets.verify(packet, wheel) + + # A replacement runtime with valid RECORD hashes still cannot borrow the old provenance. + replacement = wheels.build(package, tmp_path / "replacement") + with pytest.raises(ValueError, match="inventory"): + packets.verify(packet, replacement, frozen) + corrupted = tmp_path / "corrupted" / wheel.name + corrupted.parent.mkdir() + with zipfile.ZipFile(wheel) as original, zipfile.ZipFile(corrupted, "w") as modified: + for name in original.namelist(): + data = original.read(name) + if name == "jevdual/capability/cli.py": + data += b"# tampered payload\n" + modified.writestr(name, data) + with pytest.raises(ValueError, match="Artifact bytes differ"): + packets.verify(packet, corrupted, frozen) + assert packet.read_bytes() == packet_bytes + + +def test_packet_inventory_uses_one_verified_wheel_snapshot(tmp_path, monkeypatch): + monkeypatch.syspath_prepend(str(SCRIPTS)) + import build_onboarding_packet as packets + + wheel = tmp_path / "fixture.whl" + original = b"first immutable snapshot" + wheel.write_bytes(original) + monkeypatch.setattr(packets, "PROJECT", tmp_path) + monkeypatch.setattr(packets, "commit_identity", lambda commit, project: COMMIT) + monkeypatch.setattr(packets, "committed_bytes", lambda *args: b"fixture source") + + def verified_then_replaced(name, data, commit, project): + assert data == original + wheel.write_bytes(b"concurrent replacement") + + monkeypatch.setattr(packets, "verify_committed_wheel", verified_then_replaced) + files = packets.inventory(wheel, COMMIT) + assert files["footwork/artifacts/fixture.whl"] == original + assert json.loads(files["footwork/MANIFEST.json"])["wheel_sha256"] == hashlib.sha256(original).hexdigest() + + +@pytest.mark.parametrize("mode", [stat.S_IFLNK, stat.S_IFDIR, stat.S_IFIFO, stat.S_IFCHR]) +def test_packet_verifier_refuses_unsafe_member_modes(tmp_path, monkeypatch, mode): + monkeypatch.syspath_prepend(str(SCRIPTS)) + import build_onboarding_packet as packets + + path = tmp_path / "packet.zip" + entry = zipfile.ZipInfo("footwork/payload") + entry.create_system = 3 + entry.external_attr = (mode | 0o600) << 16 + with zipfile.ZipFile(path, "w") as archive: + archive.writestr(entry, b"same expected bytes") + monkeypatch.setattr(packets, "inventory", lambda *args: {entry.filename: b"same expected bytes"}) + with pytest.raises(ValueError, match="Unsafe"): + packets.verify(path, tmp_path / "unused-wheel", COMMIT) + + +def test_snapshot_refuses_symlink_and_observed_mutation(tmp_path, monkeypatch): + monkeypatch.syspath_prepend(str(SCRIPTS)) + import build_onboarding_packet as packets + + path = tmp_path / "artifact" + path.write_bytes(b"original") + link = tmp_path / "link" + link.symlink_to(path) + with pytest.raises(ValueError, match="nonsymlink"): + packets.snapshot(link) + directory = tmp_path / "directory" + directory.mkdir() + fifo = tmp_path / "fifo" + os.mkfifo(fifo, 0o600) + for nonregular in (directory, fifo): + with pytest.raises(ValueError, match="regular"): + packets.snapshot(nonregular) + original = packets.os.fdopen + + class MutatingRead: + def __init__(self, *args): + self.source = original(*args) + + def __enter__(self): + return self + + def __exit__(self, *args): + self.source.close() + + def fileno(self): + return self.source.fileno() + + def read(self, size): + data = self.source.read(size) + path.write_bytes(b"replacement after read") + return data + + monkeypatch.setattr(packets.os, "fdopen", MutatingRead) + with pytest.raises(ValueError, match="changed during snapshot"): + packets.snapshot(path) + + +def test_source_identity_rejects_tree_and_blob_objects(monkeypatch): + monkeypatch.syspath_prepend(str(SCRIPTS)) + import build_onboarding_packet as packets + import build_release_candidate as release + + project = SCRIPTS.parents[1] + for expression in ("HEAD^{tree}", "HEAD:LICENSE"): + identity = subprocess.check_output(["git", "rev-parse", expression], cwd=project, text=True).strip() + with pytest.raises(ValueError, match="commit object"): + packets.commit_identity(identity, project) + with pytest.raises(ValueError, match="commit object"): + release.commit_identity(identity) + with pytest.raises(ValueError, match="commit object"): + packets.committed_bytes(identity, "LICENSE", project) + with pytest.raises(ValueError, match="commit object"): + release.committed_bytes(identity, "LICENSE") + + +def test_archive_size_budget_refuses_compressed_oversize(tmp_path, monkeypatch): + monkeypatch.syspath_prepend(str(SCRIPTS)) + import build_onboarding_packet as packets + + path = tmp_path / "oversize.zip" + with zipfile.ZipFile(path, "w", compression=zipfile.ZIP_DEFLATED) as archive: + archive.writestr("footwork/oversize", b"x" * (8 * 1024 * 1024 + 1)) + with pytest.raises(ValueError, match="bounded candidate size"): + packets.parse_archive(path.read_bytes()) diff --git a/jevdual/tests/test_release_candidate.py b/jevdual/tests/test_release_candidate.py new file mode 100644 index 0000000..1aa81df --- /dev/null +++ b/jevdual/tests/test_release_candidate.py @@ -0,0 +1,381 @@ +"""Offline immutable packet and release provenance boundary checks.""" + +from __future__ import annotations + +import importlib.util +import json +import posixpath +import re +import stat +import warnings +import zipfile +from pathlib import Path + +import pytest + + +@pytest.fixture +def builder(monkeypatch): + scripts = Path(__file__).resolve().parents[1] / "scripts" + monkeypatch.syspath_prepend(str(scripts)) + spec = importlib.util.spec_from_file_location("release_builder", scripts / "build_release_candidate.py") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def save_archive(path, files): + with zipfile.ZipFile(path, "w") as archive: + for name, data in files.items(): + archive.writestr(name, data) + + +@pytest.fixture +def source_packet(builder, monkeypatch, tmp_path): + files = { + "footwork/LICENSE": b"MIT fixture license", + "footwork/README.md": b"old instructions", + "footwork/vm-plan.md": b"guest plan", + "footwork/installation.md": b"old installation", + "footwork/artifacts/jevdual-0.0.1-py3-none-any.whl": b"synthetic wheel", + } + files.update({"footwork/jevdual/scripts/" + name: name.encode() for name in builder.HELPERS}) + wheel_hash = builder.sha(b"synthetic wheel") + files["footwork/MANIFEST.json"] = builder.encoded( + { + "source_commit": builder.QUALIFIED_PRODUCT_COMMIT, + "automatic_execution": False, + "wheel_sha256": wheel_hash, + "members": {name: builder.sha(data) for name, data in files.items()}, + } + ) + packet = tmp_path / "qualified.zip" + save_archive(packet, files) + monkeypatch.setattr(builder, "QUALIFIED_PACKET_SHA", builder.sha(packet.read_bytes())) + return packet, files + + +@pytest.mark.parametrize( + "name", ["/footwork/escape", "footwork/../escape", "footwork//escape", "footwork\\escape"] +) +def test_archive_rejects_unsafe_member_paths(builder, tmp_path, name): + path = tmp_path / "unsafe.zip" + save_archive(path, {name: b"unsafe"}) + with pytest.raises(ValueError, match="Unsafe"): + builder.read_archive(path) + + +def test_duplicate_and_symlink_members_are_rejected(builder, tmp_path): + duplicate = tmp_path / "duplicate.zip" + with warnings.catch_warnings(): + warnings.simplefilter("ignore", UserWarning) + with zipfile.ZipFile(duplicate, "w") as archive: + archive.writestr("footwork/file", b"first") + archive.writestr("footwork/file", b"second") + with pytest.raises(ValueError, match="duplicate"): + builder.read_archive(duplicate) + symlink = tmp_path / "symlink.zip" + with zipfile.ZipFile(symlink, "w") as archive: + entry = zipfile.ZipInfo("footwork/link") + entry.create_system = 3 + entry.external_attr = (stat.S_IFLNK | 0o777) << 16 + archive.writestr(entry, "outside") + with pytest.raises(ValueError, match="Unsafe"): + builder.read_archive(symlink) + + +def test_frozen_packet_digest_rejects_self_consistent_replacement(builder, source_packet, tmp_path): + packet, files = source_packet + manifest = json.loads(files["footwork/MANIFEST.json"]) + files["footwork/LICENSE"] = b"different license" + manifest["members"]["footwork/LICENSE"] = builder.sha(files["footwork/LICENSE"]) + files["footwork/MANIFEST.json"] = builder.encoded(manifest) + replacement = tmp_path / "replacement.zip" + save_archive(replacement, files) + with pytest.raises(ValueError, match="immutable"): + builder.qualification_packet(replacement) + assert builder.qualification_packet(packet)[1]["source_commit"] == builder.QUALIFIED_PRODUCT_COMMIT + + +def test_packet_manifest_checks_every_payload_even_when_archive_pin_is_updated( + builder, source_packet, monkeypatch, tmp_path +): + _, files = source_packet + files["footwork/LICENSE"] = b"tampered" + tampered = tmp_path / "tampered.zip" + save_archive(tampered, files) + monkeypatch.setattr(builder, "QUALIFIED_PACKET_SHA", builder.sha(tampered.read_bytes())) + with pytest.raises(ValueError, match="digest"): + builder.qualification_packet(tampered) + + +@pytest.mark.parametrize( + "summary", + [ + {"passed": False, "independent_review": "approved"}, + {"passed": True, "independent_review": "pending"}, + {"passed": 1, "independent_review": "approved"}, + ], +) +def test_unqualified_or_unreviewed_evidence_cannot_release(builder, summary): + with pytest.raises(ValueError, match="independent approval"): + builder.approved_summary(json.dumps(summary).encode(), "installed_agent") + + +def mocked_release_source(builder, monkeypatch, agent_changes=None): + commit = "f" * 40 + blobs = {path: ("committed " + path).encode() for path in builder.DOCS.values()} + blobs.update({path: b"exact committed report" for path in builder.REPORTS.values()}) + blobs[builder.REPORTS["footwork/evidence/fresh-install/report.md"]] = ( + "\n".join("[Receipt](" + name + ")" for name in builder.LEGACY_INSTALL_ATTACHMENTS) + + "\n[Consumer guide](../../docs/capability-packaging.md)" + ).encode() + blobs.update( + { + "jevdual/results/clean-installation-20261010/" + name: b'{"historical":true}' + for name in builder.LEGACY_INSTALL_ATTACHMENTS + } + ) + blobs[builder.DOCS["footwork/installation.md"]] = "\n".join( + "[Link](" + source + ")" for source in builder.DOCUMENT_LINKS + ).encode() + blobs[builder.DOCS["footwork/README.md"]] = ( + b"[Install](capability-packaging.md) [Host](installed-agent-host.md)" + ) + blobs[builder.DOCS["footwork/agent-host.md"]] = ( + b"[Install](capability-packaging.md) [Remote](https://example.test) [Section](#setup)" + ) + blobs[builder.EVIDENCE["permission_journey"]] = builder.encoded( + { + "passed": True, + "independent_review": "approved", + "packet_sha256": builder.QUALIFIED_PACKET_SHA, + "source_commit": builder.QUALIFIED_PRODUCT_COMMIT, + "classification": "fresh dependencies on provisioned guest", + "disk_persistence": "not_checked", + } + ) + agent = { + "passed": True, + "independent_review": "approved", + "packet_sha256": builder.QUALIFIED_PACKET_SHA, + "source_commit": builder.QUALIFIED_PRODUCT_COMMIT, + "wheel_sha256": builder.sha(b"synthetic wheel"), + "host": "Claude Code", + "limits": "one bounded fixture task", + } + agent.update(agent_changes or {}) + blobs[builder.EVIDENCE["installed_agent"]] = builder.encoded(agent) + + def committed(requested, relative): + assert requested == commit + return blobs[relative] + + monkeypatch.setattr(builder, "committed_bytes", committed) + monkeypatch.setattr( + builder, + "commit_identity", + lambda requested: commit if requested == commit else pytest.fail("unexpected fixture commit"), + ) + monkeypatch.setattr(builder, "verify_committed_wheel", lambda name, data, requested: None) + return commit + + +def test_qualification_packet_hash_and_parse_share_snapshot(builder, source_packet, monkeypatch): + packet, files = source_packet + original = packet.read_bytes() + + def read_then_replace(path): + assert path == packet + packet.write_bytes(b"concurrent replacement") + return original + + monkeypatch.setattr(builder, "snapshot", read_then_replace) + parsed, manifest = builder.qualification_packet(packet) + assert parsed == files + assert manifest["source_commit"] == builder.QUALIFIED_PRODUCT_COMMIT + + +def test_candidate_verifier_hash_and_parse_share_snapshot(builder, source_packet, monkeypatch, tmp_path): + packet, _ = source_packet + commit = mocked_release_source(builder, monkeypatch) + candidate = builder.build(packet, tmp_path / "candidate", commit) + original = candidate.read_bytes() + expected_hash = builder.sha(original) + real_snapshot = builder.snapshot + + def read_then_replace(path): + data = real_snapshot(path) + if path == candidate: + candidate.write_bytes(b"concurrent replacement") + return data + + monkeypatch.setattr(builder, "snapshot", read_then_replace) + assert builder.verify(candidate, packet, commit)["sha256"] == expected_hash + + +def test_all_markdown_links_resolve_inside_final_bundle( + builder, + source_packet, + monkeypatch, +): + packet, _ = source_packet + commit = mocked_release_source(builder, monkeypatch) + files = builder.inventory(packet, commit) + assert len(files) == 30 + checked = [] + for name in (name for name in files if name.endswith(".md")): + for link in re.findall(r"\[[^\]]*\]\(([^)]+)\)", files[name].decode()): + if link.startswith(("http://", "https://", "#")): + continue + relative = link.split("#", 1)[0] + target = posixpath.normpath(posixpath.join(posixpath.dirname(name), relative)) + assert target in files, (name, link) + checked.append(target) + assert set(builder.REPORTS).issubset(checked) + assert "footwork/agent-host.md" in checked + assert b"](installation.md)" in files["footwork/README.md"] + assert b"](agent-host.md)" in files["footwork/README.md"] + assert b"](installation.md)" in files["footwork/agent-host.md"] + for name in builder.REPORTS.keys() - {"footwork/evidence/fresh-install/report.md"}: + assert files[name] == b"exact committed report" + legacy = files["footwork/evidence/fresh-install/report.md"] + assert b"](../../installation.md)" in legacy + assert b"](../../docs/capability-packaging.md)" not in legacy + for name in builder.LEGACY_INSTALL_ATTACHMENTS: + assert json.loads(files["footwork/evidence/fresh-install/" + name]) == {"historical": True} + qualification = json.loads(files["footwork/qualification.json"]) + assert qualification["legacy_evidence"]["role"] == "historical_context_only" + + +@pytest.mark.parametrize( + "changes", + [ + {"packet_sha256": "0" * 64}, + {"source_commit": "0" * 40}, + {"wheel_sha256": "0" * 64}, + ], +) +def test_approved_agent_evidence_cannot_qualify_another_artifact( + builder, + source_packet, + monkeypatch, + changes, +): + packet, _ = source_packet + commit = mocked_release_source(builder, monkeypatch, changes) + with pytest.raises(ValueError, match="Agent qualification"): + builder.inventory(packet, commit) + + +def test_release_repeats_exactly_and_extracted_manifest_remains_compatible( + builder, source_packet, monkeypatch, tmp_path +): + packet, _ = source_packet + commit = mocked_release_source(builder, monkeypatch) + one = builder.build(packet, tmp_path / "one", commit) + two = builder.build(packet, tmp_path / "two", commit) + assert one.read_bytes() == two.read_bytes() + assert builder.verify(one, packet, commit)["verified"] + with pytest.raises(FileExistsError): + builder.build(packet, tmp_path / "one", commit) + root = tmp_path / "extracted" + for name, data in builder.read_archive(one).items(): + path = root / name + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(data) + from verify_onboarding_packet import verify + + assert verify(root / "footwork")["source_commit"] == commit + qualification = json.loads((root / "footwork/qualification.json").read_text()) + assert qualification["qualified_product_source_commit"] == builder.QUALIFIED_PRODUCT_COMMIT + assert qualification["release_source_commit"] == commit + assert qualification["limits"]["installed_agent"] == {"limits": "one bounded fixture task"} + + +def test_candidate_member_tampering_is_rejected(builder, source_packet, monkeypatch, tmp_path): + packet, _ = source_packet + commit = mocked_release_source(builder, monkeypatch) + candidate = builder.build(packet, tmp_path / "original", commit) + files = builder.read_archive(candidate) + files["footwork/agent-host.md"] = b"unreviewed change" + tampered = tmp_path / "tampered-candidate.zip" + save_archive(tampered, files) + with pytest.raises(ValueError, match="member differs"): + builder.verify(tampered, packet, commit) + + +def test_distributed_legacy_receipts_do_not_expose_personal_home_paths( + builder, + source_packet, + monkeypatch, +): + packet, packet_files = source_packet + commit = mocked_release_source(builder, monkeypatch) + committed = builder.committed_bytes + original = json.dumps( + { + "python": "/Users/alice/.codex/worktrees/trial-one/footwork/output/install/venv/bin/python", + "nested": [{"argv": ["/home/bob/projects/footwork/output/install/venv/bin/python"]}], + "log": 'context={"home":"/Users/alice","cache":"/home/bob/.cache"}', + "oracle_actual": "Invoice report\n17.25 USD", + } + ).encode() + + def with_personal_paths(requested, relative): + if relative == "jevdual/results/clean-installation-20261010/py312-installation.json": + return original + return committed(requested, relative) + + monkeypatch.setattr(builder, "committed_bytes", with_personal_paths) + files = builder.inventory(packet, commit) + distributed = files["footwork/evidence/fresh-install/py312-installation.json"] + assert b"/Users/alice" not in distributed + assert b"/home/bob" not in distributed + assert json.loads(distributed)["oracle_actual"] == "Invoice report\n17.25 USD" + qualification = json.loads(files["footwork/qualification.json"]) + derivation = qualification["legacy_evidence"]["derivations"][ + "footwork/evidence/fresh-install/py312-installation.json" + ] + assert derivation["original_sha256"] == builder.sha(original) + assert derivation["derived_sha256"] == builder.sha(distributed) + assert ( + builder.committed_bytes(commit, "jevdual/results/clean-installation-20261010/py312-installation.json") + == original + ) + for name, data in packet_files.items(): + if name.endswith(".whl") or "/scripts/" in name or name == "footwork/LICENSE": + assert files[name] == data + + +def test_sanitized_json_preserves_urls_oracles_nonpath_text_and_multiple_identities(builder): + original = { + "first": "/Users/alice/.codex/worktrees/first/footwork/output/run.json", + "second": "/home/bob/projects/footwork/output/run.json", + "nested": [{"home": "/Users/charlie", "cache": "/home/dana/.cache/file"}], + "embedded": 'log={"path":"/Users/erin/projects/footwork/output/a.json"}', + "url": "https://example.test/Users/alice/path", + "file_url": "file:///Users/bob/path", + "text": "Users Alice, home Bob and Invoice report are ordinary words.", + "oracle": "Invoice report\n17.25 USD", + "number": 17.25, + "flag": False, + "none": None, + } + data = json.dumps(original).encode() + normalized = builder.sanitized_legacy_json(data) + result = json.loads(normalized) + assert result["first"] == "/output/run.json" + assert result["second"] == "/output/run.json" + assert result["nested"] == [{"home": "", "cache": "/.cache/file"}] + assert result["embedded"] == 'log={"path":"/output/a.json"}' + for key in ("url", "file_url", "text", "oracle", "number", "flag", "none"): + assert result[key] == original[key] + assert builder.sanitized_legacy_json(data) == normalized + assert json.loads(data) == original + + +def test_sanitized_json_refuses_key_collision_instead_of_losing_evidence(builder): + data = json.dumps({"/Users/alice/file": "first", "/home/bob/file": "second"}).encode() + with pytest.raises(ValueError, match="keys collide"): + builder.sanitized_legacy_json(data) diff --git a/tools/camera-capture-experiment/.gitignore b/tools/camera-capture-experiment/.gitignore new file mode 100644 index 0000000..fd6aad7 --- /dev/null +++ b/tools/camera-capture-experiment/.gitignore @@ -0,0 +1,2 @@ +output/ +__pycache__/ diff --git a/tools/camera-capture-experiment/README.md b/tools/camera-capture-experiment/README.md new file mode 100644 index 0000000..2adcbe8 --- /dev/null +++ b/tools/camera-capture-experiment/README.md @@ -0,0 +1,110 @@ +# Native camera capture experiment + +Acquire one saved PNG from an exactly selected physical Mac camera. This is an isolated +Footwork experiment using Python's standard library and installed FFmpeg/ffprobe. It does +not change Footwork's runtime, TextEdit recovery, or production adapter interfaces. + +The experiment asks whether a requested frame can be acquired, decoded, and associated +with a retained operation record. It does not establish exposure freshness, scene meaning, +document quality, page identity, or exactly-once physical execution. + +## Inspect and capture + +Run from the repository root. Private run directories must be descendants of this package's +ignored `output/` directory. Directories use permissions 0700; records and diagnostics use 0600. +The program creates a missing run directory and refuses symlinked or publicly readable directories. + +```sh +python3 tools/camera-capture-experiment/capture.py doctor \ + --run-dir tools/camera-capture-experiment/output/desk-session +``` + +`doctor` reports model names from `system_profiler`, FFmpeg's AVFoundation video inventory, +their unique physical-name intersection, and camera authorization status. It excludes known +screen, virtual, OBS, and Continuity routes. Device IDs and microphone inventory are omitted. +Inventory is a selection aid, not authenticated hardware identity or proof of access. + +The optional Swift helper only queries `authorizationStatus(.video)`; it never requests +permission. Its status is specific to that process's privacy identity and does not prove +FFmpeg is authorized. An unavailable Swift query is reported explicitly. + +Use an exact name from `eligible_cameras`: + +```sh +python3 tools/camera-capture-experiment/capture.py capture \ + --run-dir tools/camera-capture-experiment/output/desk-session \ + --camera 'MacBook Pro Camera' --token frame-001 +``` + +If the status is undetermined or unavailable, an intentional first capture requires +`--allow-permission-prompt`. That lets FFmpeg's explicit camera acquisition trigger the +macOS authorization flow. `doctor` never opens the camera or requests access. A queried +denied/restricted status is refused; inspect macOS privacy settings before proceeding. + +Capture selects the observed device by name, explicitly disables microphone capture, +and saves one PNG without overwriting an existing artifact. It explicitly requests 30 fps, +a supported mode reported by this MacBook camera during NC1. It makes no resolution +assumption. Another device may reject this fixed mode; there is no mode fallback. +Capture defaults to a 15-second timeout; `--timeout` accepts 1-60 +seconds. Inventory and permission inspection have separate bounded process lifetimes. +FFmpeg and its descendants are terminated on completion, timeout, or cancellation. + +## Read the result + +```sh +python3 tools/camera-capture-experiment/capture.py status \ + --run-dir tools/camera-capture-experiment/output/desk-session --token frame-001 +``` + +An `acquired` result means FFmpeg completed and ffprobe decoded one PNG frame, with +positive dimensions and a matching retained hash/byte count. `status` rechecks that saved +artifact without opening a camera. It provides no exposure timestamp or semantic claim. + +The ledger records intent before dispatch. Repeating the same token and arguments reads +the existing record, even in a fresh process. The request binds the fixed `framerate_fps: 30` +alongside camera, timeout and permission-prompt arguments. Changed arguments or an older +request without that mode are refused by `capture`; `status` can still inspect the earlier +record. A nonblocking process lock permits one owner per run. + +After dispatch, any incomplete capture or receipt produces `unknown`, including the first +CLI response. A pending ledger found after a restart remains unknown. A valid image without +a completed receipt cannot promote it to acquired. Unresolved operations block further +captures in that run; there is no automatic replay or reset command. + +If an acquired image later disappears or changes, current `state` becomes `unknown` and +claims are empty. `acquisition_outcome` and the persisted original receipt still retain the +historical acquisition; the current artifact is unusable. New captures in that run are blocked. + +FFmpeg stderr is retained in a private, bounded diagnostic file named in the record. It +is not printed by the CLI. Use it to distinguish permission, device-format, or runtime +failures. The experiment never retries by changing camera modes or selecting another device. +Inspect an unresolved run manually before deliberately beginning a new run. A new run is +a new operation, not proof that the earlier operation did not happen. + +Exit code 0 means the inventory completed, a retained acquisition is currently verifiable, +or a status token is absent. Exit code 2 means refusal, unknown outcome, or invalid retained +evidence. JSON `state` distinguishes those cases. This is process-restart testing; power-loss +recovery, hostile replacement, and cross-run camera exclusivity are not qualified. + +## Conformance checks + +```sh +python3 -m unittest discover -s tools/camera-capture-experiment -p 'test_*.py' -v +``` + +Tests use synthetic inventory and subprocess fixtures, never a live camera. One test uses +installed ffprobe to decode a tiny generated fixture. Live acquisition belongs to the +preregistered NC1 experiment and its engineering notebook. Private images, device diagnostics, +Swift module cache, and test scratch files remain under ignored `output/`. + +## Reference seams + +- Rerun's local `examples/python/live_camera_edge_detection/` illustrates camera acquisition + and visual inspection. A later exporter can project saved frames and operation events. +- libgphoto2's `examples/sample-trigger-capture.c` illustrates camera event and file handling. + A tethered still-camera transaction needs its own interface and qualification. +- Footwork's capability backend has validation, dispatch and readback seams. This experiment + deliberately keeps its acquisition ledger separate from native document recovery. + +Preregistration: `jevdual/docs/experiments/NC1-native-camera.md`. +Notebook: `jevdual/docs/notebook/2026-10-10-native-camera-experiment.md`. diff --git a/tools/camera-capture-experiment/capture.py b/tools/camera-capture-experiment/capture.py new file mode 100644 index 0000000..a948355 --- /dev/null +++ b/tools/camera-capture-experiment/capture.py @@ -0,0 +1,380 @@ +"""Bounded native-camera acquisition; no Footwork runtime dependencies.""" + +import argparse +import contextlib +import fcntl +import hashlib +import json +import os +import re +import shutil +import signal +import stat +import subprocess +import sys +import time +import uuid +from pathlib import Path + +BASE = Path(__file__).resolve().parent +OUTPUT = BASE / "output" +TOKEN = re.compile(r"[A-Za-z0-9][A-Za-z0-9_.-]{0,127}\Z") +AV_DEVICE = re.compile(r"\[AVFoundation indev @ [^]]+\] \[(\d+)\] (.+)$") + + +class Refusal(Exception): + """An unmet prerequisite or unverifiable operation.""" + + def __init__(self, reason, private_stderr=None): + super().__init__(reason) + self.private_stderr = private_stderr + + +def run_process(argv, timeout): + """Start one owned process group; clean descendants on exit or interruption.""" + child = subprocess.Popen(argv, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, start_new_session=True, umask=0o077) + try: + stdout, stderr = child.communicate(timeout=timeout) + return child.returncode, stdout.decode(errors="replace"), stderr.decode(errors="replace") + except subprocess.TimeoutExpired as exc: + detail = exc.stderr or b"" + if isinstance(detail, bytes): + detail = detail.decode(errors="replace") + raise Refusal("child process exceeded its time limit", private_stderr=detail) from exc + finally: + # The group may outlive its leader (including pipe-holding grandchildren). + with contextlib.suppress(ProcessLookupError): + os.killpg(child.pid, signal.SIGKILL) + with contextlib.suppress(subprocess.TimeoutExpired): + child.communicate(timeout=2) + + +def private_directory(path): + """Create private directories without following caller-selected symlinks.""" + raw = Path(os.path.abspath(os.fspath(path))) + if raw == OUTPUT or OUTPUT not in raw.parents: + raise Refusal("run directory must be a subdirectory of the experiment's output directory") + current = OUTPUT + for part in (None, *raw.relative_to(OUTPUT).parts): + if part is not None: + current = current / part + try: + current.mkdir(mode=0o700) + except FileExistsError: + pass + info = current.lstat() + if not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid(): + raise Refusal("run directory must contain only owned directories, without symlinks") + if info.st_mode & 0o077: + raise Refusal("run directories must be private (permissions 0700)") + return raw + + +@contextlib.contextmanager +def locked(root): + fd = os.open(root / "lock", os.O_RDWR | os.O_CREAT | os.O_NOFOLLOW, 0o600) + try: + if not stat.S_ISREG(os.fstat(fd).st_mode): + raise Refusal("invalid lock file") + try: + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError as exc: + raise Refusal("another process owns this run directory") from exc + yield + finally: + os.close(fd) + + +def read_json(path): + try: + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + with os.fdopen(fd) as source: + if not stat.S_ISREG(os.fstat(source.fileno()).st_mode): + raise Refusal("ledger is not a regular file") + record = json.load(source) + if (not isinstance(record, dict) or record.get("state") not in {"pending", "unknown", "acquired"} + or not isinstance(record.get("token"), str) or not TOKEN.fullmatch(record["token"]) + or not isinstance(record.get("request"), dict)): + raise Refusal("ledger structure is invalid; inspect this run directory") + return record + except (OSError, ValueError) as exc: + raise Refusal("ledger cannot be read; inspect this run directory") from exc + + +def persist(path, record): + staging = path.with_name(f".{path.name}.{uuid.uuid4().hex}.tmp") + try: + fd = os.open(staging, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) + with os.fdopen(fd, "w") as sink: + json.dump(record, sink, indent=2, sort_keys=True) + sink.write("\n") + sink.flush() + os.fsync(sink.fileno()) + os.replace(staging, path) + directory = os.open(path.parent, os.O_RDONLY) + try: + os.fsync(directory) + finally: + os.close(directory) + finally: + with contextlib.suppress(FileNotFoundError): + staging.unlink() + + +def private_diagnostic(path, stderr): + fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) + with os.fdopen(fd, "wb") as sink: + sink.write(stderr.encode(errors="replace")[:65536]) + sink.flush() + os.fsync(sink.fileno()) + + +def parse_physical(data): + entries = data.get("SPCameraDataType") + if not isinstance(entries, list): + raise Refusal("system_profiler camera inventory is unavailable") + # The system inventory may include software cameras: require a model identifier. + result = [] + for item in entries: + name = item.get("_name", "") + model = item.get("spcamera_model-id", "") + if name and model and not re.search(r"virtual|screen|obs|continuity", name + " " + model, re.IGNORECASE): + result.append(name) + return result + + +def parse_avfoundation(stderr): + names = [] + in_video = False + for line in stderr.splitlines(): + if "AVFoundation video devices:" in line: + in_video = True + elif "AVFoundation audio devices:" in line: + in_video = False + elif in_video: + match = AV_DEVICE.search(line) + if match: + names.append(match.group(2).strip()) + return names + + +def binaries(): + found = {name: shutil.which(name) for name in ("ffmpeg", "ffprobe", "swift", "system_profiler")} + if not found["ffmpeg"] or not found["ffprobe"]: + raise Refusal("installed ffmpeg and ffprobe are required; nothing will be installed") + return found + + +def permission(root, programs): + if not programs["swift"]: + return "unavailable" + cache = root / "swift-module-cache" + if cache.is_symlink(): + raise Refusal("invalid Swift cache directory") + cache.mkdir(mode=0o700, exist_ok=True) + rc, stdout, _ = run_process([programs["swift"], "-module-cache-path", str(cache), + str(BASE / "permission.swift")], 20) + status = stdout.strip() + if rc or status not in {"authorized", "denied", "restricted", "not_determined", "unknown"}: + return "unavailable" + return status + + +def doctor(root): + programs = binaries() + if not programs["system_profiler"]: + raise Refusal("macOS system_profiler is required") + rc, stdout, _ = run_process([programs["system_profiler"], "SPCameraDataType", "-json"], 15) + if rc: + raise Refusal("physical camera inventory failed") + try: + physical = parse_physical(json.loads(stdout)) + except ValueError as exc: + raise Refusal("physical camera inventory was not valid JSON") from exc + # AVFoundation enumeration returns a nonzero code because no input is opened. + _, _, stderr = run_process([programs["ffmpeg"], "-hide_banner", "-nostdin", "-f", + "avfoundation", "-list_devices", "true", "-i", ""], 10) + video = parse_avfoundation(stderr) + if not video: + raise Refusal("FFmpeg AVFoundation video inventory was unavailable") + physical_unique = [name for name in physical if physical.count(name) == 1 and video.count(name) == 1] + return {"physical_cameras": physical, "avfoundation_video_devices": video, + "eligible_cameras": physical_unique, "permission_status": permission(root, programs), + "permission_query": "Swift AVFoundation authorizationStatus(.video); no request", + "programs": programs} + + +def select_camera(camera, inventory, allow_permission_prompt=False): + if not camera or ":" in camera or camera.startswith("-"): + raise Refusal("camera name is invalid") + if camera not in inventory["eligible_cameras"]: + raise Refusal("camera must exactly match one unique physical and AVFoundation device name") + if inventory["permission_status"] in {"denied", "restricted"}: + raise Refusal("camera permission query reports denied or restricted; inspect macOS privacy settings") + if inventory["permission_status"] != "authorized" and not allow_permission_prompt: + raise Refusal("camera permission is unestablished; explicit --allow-permission-prompt is required") + + +def artifact_info(path, ffprobe): + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + with os.fdopen(fd, "rb") as source: + if not stat.S_ISREG(os.fstat(source.fileno()).st_mode): + raise Refusal("artifact is not a regular file") + digest = hashlib.sha256() + size = 0 + for chunk in iter(lambda: source.read(1024 * 1024), b""): + digest.update(chunk) + size += len(chunk) + if not size: + raise Refusal("artifact is empty") + rc, stdout, stderr = run_process([ffprobe, "-v", "error", "-count_frames", "-show_entries", + "stream=codec_name,width,height,nb_read_frames", "-of", "json", + str(path)], 10) + if rc or stderr.strip(): + raise Refusal("ffprobe could not decode the artifact") + try: + streams = json.loads(stdout)["streams"] + stream = streams[0] + if (len(streams) != 1 or stream["codec_name"] != "png" or + stream["width"] <= 0 or stream["height"] <= 0 or + str(stream["nb_read_frames"]) != "1"): + raise Refusal("artifact is not one decoded PNG frame") + except (KeyError, IndexError, ValueError, TypeError) as exc: + raise Refusal("ffprobe returned incomplete artifact evidence") from exc + return {"sha256": digest.hexdigest(), "bytes": size, "codec": "png", + "width": stream["width"], "height": stream["height"]} + + +def paths(root, token): + if not TOKEN.fullmatch(token): + raise Refusal("token must be 1-128 letters, digits, underscores, periods or hyphens") + key = hashlib.sha256(token.encode()).hexdigest() + return root / f"{key}.json", root / f"{key}.png", root / f"{key}.pending.png" + + +def status(root, token): + ledger, artifact, _ = paths(root, token) + if not ledger.exists() and not ledger.is_symlink(): + return {"token": token, "state": "absent"} + record = read_json(ledger) + if record.get("token") != token: + raise Refusal("ledger identity does not match requested operation") + result = dict(record) + if record.get("state") == "acquired": + result["acquisition_outcome"] = "acquired" + try: + observed = artifact_info(artifact, binaries()["ffprobe"]) + if observed != record["artifact"]: + raise Refusal("artifact no longer matches its receipt") + result["artifact_status"] = "verified" + except (OSError, KeyError, Refusal): + # Preserve historical acquisition; current artifact usability is unresolved. + result["state"] = "unknown" + result["artifact_status"] = "invalid_or_unverifiable" + result["reason"] = "retained artifact is missing, changed or unverifiable" + if result.get("state") == "pending": + result["state"] = "unknown" + result["reason"] = "dispatch was recorded without a complete persisted receipt; inspect manually" + result["claims"] = ["saved-frame acquisition", "retained artifact integrity"] if result["state"] == "acquired" else [] + return result + + +def capture(root, camera, token, timeout, allow_permission_prompt=False): + ledger, artifact, staging = paths(root, token) + diagnostic = ledger.with_suffix(".ffmpeg.stderr.log") + request = {"camera": camera, "format": "png", "framerate_fps": 30, "timeout_seconds": timeout, + "allow_permission_prompt": allow_permission_prompt} + if ledger.exists() or ledger.is_symlink(): + record = read_json(ledger) + if record.get("request") != request: + raise Refusal("token already belongs to different capture arguments") + return status(root, token) + for existing in root.glob("*.json"): + record = read_json(existing) + if record.get("state") != "acquired": + raise Refusal("an unresolved operation blocks new captures; inspect this run directory") + # Invalid retained artifacts also block conflicting captures. + if status(root, record["token"])["state"] != "acquired": + raise Refusal("an unverifiable artifact blocks new captures; inspect this run directory") + if any(path.exists() or path.is_symlink() for path in (artifact, staging, diagnostic)): + raise Refusal("an unreceipted artifact already exists; it will not be overwritten") + inventory = doctor(root) + select_camera(camera, inventory, allow_permission_prompt) + programs = inventory["programs"] + record = {"version": 1, "token": token, "request": request, "state": "pending", + "intent_time_unix": time.time(), "artifact_name": artifact.name} + persist(ledger, record) + try: + rc, _, stderr = run_process([programs["ffmpeg"], "-hide_banner", "-loglevel", "error", + "-nostdin", "-n", "-f", "avfoundation", "-framerate", "30", + "-i", f"{camera}:none", + "-an", "-frames:v", "1", "-c:v", "png", str(staging)], timeout) + private_diagnostic(diagnostic, stderr) + record["diagnostic_name"] = diagnostic.name + if rc: + raise Refusal("FFmpeg capture failed; execution outcome remains unresolved") + info = artifact_info(staging, programs["ffprobe"]) + # Hard-link publication cannot replace an existing capture. + os.link(staging, artifact, follow_symlinks=False) + staging.unlink() + fd = os.open(artifact, os.O_RDONLY | os.O_NOFOLLOW) + try: + os.fsync(fd) + finally: + os.close(fd) + record.update(state="acquired", acquisition_outcome="acquired", artifact=info, + receipt_time_unix=time.time()) + persist(ledger, record) + # Every post-intent error must preserve uncertainty, including unexpected adapter errors. + except (Exception, KeyboardInterrupt) as exc: # noqa: BLE001 + if isinstance(exc, Refusal) and exc.private_stderr is not None: + with contextlib.suppress(OSError): + private_diagnostic(diagnostic, exc.private_stderr) + record["diagnostic_name"] = diagnostic.name + record["state"] = "unknown" + record["acquisition_outcome"] = "unknown" + record["reason"] = "capture or receipt did not complete; inspect artifacts before any further operation" + with contextlib.suppress(OSError): + persist(ledger, record) + # A first response after intent must expose uncertainty, never imply no dispatch. + return dict(record, claims=[]) + return status(root, token) + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + sub = parser.add_subparsers(dest="command", required=True) + for command in ("doctor", "capture", "status"): + child = sub.add_parser(command) + child.add_argument("--run-dir", required=True) + if command != "doctor": + child.add_argument("--token", required=True) + if command == "capture": + child.add_argument("--camera", required=True) + child.add_argument("--timeout", type=float, default=15) + child.add_argument("--allow-permission-prompt", action="store_true", + help="allow an explicit capture to trigger macOS camera authorization") + args = parser.parse_args(argv) + try: + if args.command == "capture" and not 1 <= args.timeout <= 60: + raise Refusal("capture timeout must be between 1 and 60 seconds") + root = private_directory(args.run_dir) + with locked(root): + if args.command == "doctor": + result = doctor(root) + elif args.command == "status": + result = status(root, args.token) + else: + result = capture(root, args.camera, args.token, args.timeout, args.allow_permission_prompt) + print(json.dumps(result, indent=2, sort_keys=True)) + return 0 if result.get("state", "acquired") in {"acquired", "absent"} else 2 + except (Refusal, OSError, KeyboardInterrupt) as exc: + # Do not print raw process output, personal images, or native device IDs. + reason = str(exc) if isinstance(exc, Refusal) else "operation interrupted or local resource unavailable" + print(json.dumps({"state": "refused", "reason": reason})) + return 2 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/camera-capture-experiment/permission.swift b/tools/camera-capture-experiment/permission.swift new file mode 100644 index 0000000..0e04048 --- /dev/null +++ b/tools/camera-capture-experiment/permission.swift @@ -0,0 +1,11 @@ +import AVFoundation + +// Inspection only: this helper never requests camera authorization. +let status = AVCaptureDevice.authorizationStatus(for: .video) +switch status { +case .authorized: print("authorized") +case .denied: print("denied") +case .restricted: print("restricted") +case .notDetermined: print("not_determined") +@unknown default: print("unknown") +} diff --git a/tools/camera-capture-experiment/test_capture.py b/tools/camera-capture-experiment/test_capture.py new file mode 100644 index 0000000..c69d34f --- /dev/null +++ b/tools/camera-capture-experiment/test_capture.py @@ -0,0 +1,318 @@ +"""Conformance checks use fixtures, never a live camera.""" + +import base64 +import contextlib +import importlib.util +import json +import os +import shutil +import subprocess +import sys +import tempfile +import time +import unittest +from pathlib import Path +from string import Template +from unittest.mock import patch + +BASE = Path(__file__).resolve().parent +spec = importlib.util.spec_from_file_location("camera_capture", BASE / "capture.py") +capture = importlib.util.module_from_spec(spec) +spec.loader.exec_module(capture) +PNG = base64.b64decode( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=" +) +CAMERA = "MacBook Pro Camera" +LISTING = """[AVFoundation indev @ 0x123] AVFoundation video devices: +[AVFoundation indev @ 0x123] [0] MacBook Pro Camera +[AVFoundation indev @ 0x123] [1] OBS Virtual Camera +[AVFoundation indev @ 0x123] [2] Capture screen 0 +[AVFoundation indev @ 0x123] AVFoundation audio devices: +[AVFoundation indev @ 0x123] [0] MacBook Pro Microphone +""" + + +class CaptureTests(unittest.TestCase): + def setUp(self): + capture.OUTPUT.mkdir(mode=0o700, exist_ok=True) + self.tmp = tempfile.TemporaryDirectory(prefix="conformance-", dir=capture.OUTPUT) + self.addCleanup(self.tmp.cleanup) + self.root = Path(self.tmp.name) + + def inventory(self, permission="authorized"): + return {"physical_cameras": [CAMERA], "avfoundation_video_devices": [CAMERA], + "eligible_cameras": [CAMERA], "permission_status": permission, + "programs": {"ffmpeg": "fixture-ffmpeg", "ffprobe": "fixture-ffprobe"}} + + def probe_result(self): + return 0, json.dumps({"streams": [{"codec_name": "png", "width": 1, "height": 1, + "nb_read_frames": "1"}]}), "" + + @contextlib.contextmanager + def working_device(self): + dispatched = [] + + def run(argv, timeout): + if argv[0] == "fixture-ffmpeg": + ledger, _, _ = capture.paths(self.root, "first") + self.assertEqual(capture.read_json(ledger)["state"], "pending") + dispatched.append(argv) + Path(argv[-1]).write_bytes(PNG) + return 0, "", "" + return self.probe_result() + + with patch.object(capture, "doctor", return_value=self.inventory()), \ + patch.object(capture, "binaries", return_value={"ffprobe": "fixture-ffprobe"}), \ + patch.object(capture, "run_process", side_effect=run): + yield dispatched + + def test_physical_inventory_rejects_virtual_and_continuity(self): + result = capture.parse_physical({"SPCameraDataType": [ + {"_name": CAMERA, "spcamera_model-id": CAMERA, "spcamera_unique-id": "private-id"}, + {"_name": "OBS Virtual Camera", "spcamera_model-id": "OBS"}, + {"_name": "Capture screen 0", "spcamera_model-id": "screen"}, + {"_name": "Phone", "spcamera_model-id": "Continuity Camera"}, + {"_name": "Incomplete device"}, + ]}) + self.assertEqual(result, [CAMERA]) + self.assertNotIn("private-id", repr(result)) + + def test_video_inventory_never_contains_microphone(self): + self.assertEqual(capture.parse_avfoundation(LISTING), + [CAMERA, "OBS Virtual Camera", "Capture screen 0"]) + + def test_doctor_exact_intersection_and_duplicate_refusal(self): + physical = {"SPCameraDataType": [{"_name": CAMERA, "spcamera_model-id": CAMERA}]} + listing = LISTING.replace("[1] OBS Virtual Camera", "[1] " + CAMERA) + with patch.object(capture, "binaries", return_value={"ffmpeg": "ffmpeg", "ffprobe": "ffprobe", + "swift": None, "system_profiler": "profiler"}), \ + patch.object(capture, "run_process", side_effect=[(0, json.dumps(physical), ""), + (1, "", listing)]): + inventory = capture.doctor(self.root) + self.assertEqual(inventory["eligible_cameras"], []) + with self.assertRaises(capture.Refusal): + capture.select_camera(CAMERA, inventory) + + def test_unobserved_virtual_screen_and_index_selection_refused(self): + for name in ("0", "OBS Virtual Camera", "Capture screen 0", "Another camera", CAMERA + ":none"): + with self.subTest(name=name), self.assertRaises(capture.Refusal): + capture.select_camera(name, self.inventory()) + + def test_permission_is_inspection_only_and_prompt_requires_opt_in(self): + for state in ("not_determined", "unknown", "unavailable"): + with self.assertRaises(capture.Refusal): + capture.select_camera(CAMERA, self.inventory(state)) + capture.select_camera(CAMERA, self.inventory(state), True) + for state in ("denied", "restricted"): + with self.assertRaises(capture.Refusal): + capture.select_camera(CAMERA, self.inventory(state), True) + source = (BASE / "permission.swift").read_text() + self.assertIn("authorizationStatus(for: .video)", source) + self.assertNotIn("requestAccess", source) + + def test_capture_persists_intent_and_uses_exact_camera_without_audio(self): + with self.working_device() as dispatched: + result = capture.capture(self.root, CAMERA, "first", 15) + repeat = capture.capture(self.root, CAMERA, "first", 15) + self.assertEqual(result["state"], "acquired") + self.assertEqual(repeat["artifact"], result["artifact"]) + self.assertEqual(len(dispatched), 1) + args = dispatched[0] + self.assertEqual(args[args.index("-i") + 1], CAMERA + ":none") + self.assertIn("-an", args) + self.assertIn("-nostdin", args) + self.assertIn("-n", args) + self.assertNotIn("-video_size", args) + self.assertEqual(args[args.index("-framerate") + 1], "30") + self.assertLess(args.index("-framerate"), args.index("-i")) + self.assertEqual(result["request"]["framerate_fps"], 30) + + def test_changed_token_arguments_refused_before_second_dispatch(self): + with self.working_device() as dispatched: + capture.capture(self.root, CAMERA, "first", 15) + for camera, timeout, prompt in (("Other", 15, False), (CAMERA, 16, False), + (CAMERA, 15, True)): + with self.assertRaises(capture.Refusal): + capture.capture(self.root, camera, "first", timeout, prompt) + ledger, _, _ = capture.paths(self.root, "first") + record = capture.read_json(ledger) + record["request"]["framerate_fps"] = 15 + capture.persist(ledger, record) + with self.assertRaises(capture.Refusal): + capture.capture(self.root, CAMERA, "first", 15) + del record["request"]["framerate_fps"] + capture.persist(ledger, record) + with self.assertRaises(capture.Refusal): + capture.capture(self.root, CAMERA, "first", 15) + self.assertEqual(len(dispatched), 1) + + def test_unknown_and_valid_unreceipted_artifact_never_replay(self): + ledger, artifact, _ = capture.paths(self.root, "first") + request = {"camera": CAMERA, "format": "png", "framerate_fps": 30, "timeout_seconds": 15, + "allow_permission_prompt": False} + capture.persist(ledger, {"token": "first", "request": request, "state": "pending"}) + artifact.write_bytes(PNG) + with patch.object(capture, "doctor") as doctor: + self.assertEqual(capture.capture(self.root, CAMERA, "first", 15)["state"], "unknown") + with self.assertRaises(capture.Refusal): + capture.capture(self.root, CAMERA, "second", 15) + doctor.assert_not_called() + + def test_corrupt_or_missing_image_invalidates_receipt_and_blocks_new_capture(self): + for mutate in (lambda p: p.write_bytes(b"changed"), lambda p: p.unlink()): + with self.subTest(mutate=mutate): + with self.working_device(): + capture.capture(self.root, CAMERA, "first", 15) + ledger, artifact, _ = capture.paths(self.root, "first") + mutate(artifact) + self.assertEqual(capture.status(self.root, "first")["state"], "unknown") + self.assertEqual(capture.status(self.root, "first")["acquisition_outcome"], "acquired") + self.assertEqual(capture.read_json(ledger)["state"], "acquired") + with self.assertRaises(capture.Refusal): + capture.capture(self.root, CAMERA, "second", 15) + for path in self.root.glob("*"): + path.unlink() + + def test_capture_timeout_retains_unknown(self): + with patch.object(capture, "doctor", return_value=self.inventory()), \ + patch.object(capture, "run_process", side_effect=capture.Refusal("timeout")): + self.assertEqual(capture.capture(self.root, CAMERA, "first", 1)["state"], "unknown") + self.assertEqual(capture.status(self.root, "first")["state"], "unknown") + + def test_lost_receipt_does_not_promote_valid_frame(self): + real_persist = capture.persist + + def fail_receipt(path, record): + if record["state"] != "pending": + raise OSError("injected persistence failure") + real_persist(path, record) + + with self.working_device(), patch.object(capture, "persist", side_effect=fail_receipt): + self.assertEqual(capture.capture(self.root, CAMERA, "first", 15)["state"], "unknown") + self.assertTrue(capture.paths(self.root, "first")[1].exists()) + self.assertEqual(capture.status(self.root, "first")["state"], "unknown") + + def test_arbitrary_or_symlinked_run_paths_and_tokens_refused(self): + with self.assertRaises(capture.Refusal): + capture.private_directory(BASE / "escape") + link = self.root / "link" + link.symlink_to(self.root, target_is_directory=True) + with self.assertRaises(capture.Refusal): + capture.private_directory(link / "child") + for token in ("../outside", "", "a" * 129): + with self.assertRaises(capture.Refusal): + capture.paths(self.root, token) + + def test_lock_blocks_another_process(self): + with capture.locked(self.root): + result = subprocess.run([sys.executable, str(BASE / "capture.py"), "status", "--run-dir", + str(self.root), "--token", "first"], capture_output=True, text=True, check=False) + self.assertEqual(result.returncode, 2) + self.assertIn("another process", result.stdout) + + def test_timeout_kills_pipe_holding_descendants(self): + marker = self.root / "survived" + grandchild = f"import pathlib,time;time.sleep(.4);pathlib.Path({str(marker)!r}).write_text('alive')" + parent = f"import subprocess,sys,time;subprocess.Popen([sys.executable,'-c',{grandchild!r}]);time.sleep(5)" + started = time.monotonic() + with self.assertRaises(capture.Refusal): + capture.run_process([sys.executable, "-c", parent], .15) + self.assertLess(time.monotonic() - started, 2) + time.sleep(.5) + self.assertFalse(marker.exists()) + + @unittest.skipUnless(shutil.which("ffprobe"), "installed ffprobe required for real fixture decoding") + def test_real_ffprobe_decodes_fixture_and_rejects_header_only_png(self): + image = self.root / "fixture.png" + image.write_bytes(PNG) + result = capture.artifact_info(image, shutil.which("ffprobe")) + self.assertEqual((result["width"], result["height"], result["codec"]), (1, 1, "png")) + image.write_bytes(PNG[:33]) + with self.assertRaises(capture.Refusal): + capture.artifact_info(image, shutil.which("ffprobe")) + + def test_cli_repeat_token_across_fresh_processes_without_camera(self): + bin_dir = self.root / "bin" + bin_dir.mkdir(mode=0o700) + marker = self.root / "dispatches" + fixture = Template("""#!/usr/bin/env python3 +import base64,json,pathlib,sys +name=pathlib.Path(sys.argv[0]).name +if name=='system_profiler': + print(json.dumps({'SPCameraDataType':[{'_name':'MacBook Pro Camera','spcamera_model-id':'MacBook Pro Camera'}]})) +elif name=='swift': print('authorized') +elif name=='ffmpeg': + if '-list_devices' in sys.argv: + print($listing,file=sys.stderr); sys.exit(1) + with open($marker,'a') as f: f.write('dispatch\\n') + pathlib.Path(sys.argv[-1]).write_bytes(base64.b64decode($png)) +elif name=='ffprobe': + print(json.dumps({'streams':[{'codec_name':'png','width':1,'height':1,'nb_read_frames':'1'}]})) +""").substitute(listing=repr(LISTING), marker=repr(str(marker)), png=repr(base64.b64encode(PNG).decode())) + for name in ("ffmpeg", "ffprobe", "swift", "system_profiler"): + program = bin_dir / name + program.write_text(fixture) + program.chmod(0o700) + env = dict(os.environ, PATH=str(bin_dir) + os.pathsep + os.environ["PATH"]) + args = [sys.executable, str(BASE / "capture.py"), "capture", "--run-dir", str(self.root / "run"), + "--camera", CAMERA, "--token", "fresh-process"] + for _ in range(2): + result = subprocess.run(args, env=env, capture_output=True, text=True, timeout=10, check=False) + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + self.assertEqual(json.loads(result.stdout)["state"], "acquired") + self.assertEqual(marker.read_text(), "dispatch\n") + status = subprocess.run([sys.executable, str(BASE / "capture.py"), "status", "--run-dir", + str(self.root / "run"), "--token", "fresh-process"], env=env, + capture_output=True, text=True, timeout=10, check=False) + self.assertEqual(status.returncode, 0, status.stdout + status.stderr) + self.assertEqual(json.loads(status.stdout)["state"], "acquired") + self.assertEqual(marker.read_text(), "dispatch\n") + + def test_public_cli_failed_or_timed_out_dispatch_reports_unknown_and_private_diagnostics(self): + for mode in ("failure", "timeout"): + with self.subTest(mode=mode): + bin_dir = self.root / mode + bin_dir.mkdir(mode=0o700) + fixture = Template("""#!/usr/bin/env python3 +import json,pathlib,sys,time +name=pathlib.Path(sys.argv[0]).name +if name=='system_profiler': + print(json.dumps({'SPCameraDataType':[{'_name':'MacBook Pro Camera','spcamera_model-id':'MacBook Pro Camera'}]})) +elif name=='swift': print('authorized') +elif name=='ffmpeg': + if '-list_devices' in sys.argv: + print($listing,file=sys.stderr); sys.exit(1) + print('PRIVATE_CAPTURE_DIAGNOSTIC',file=sys.stderr,flush=True) + if $mode == 'timeout': time.sleep(30) + sys.exit(1) +""").substitute(listing=repr(LISTING), mode=repr(mode)) + for name in ("ffmpeg", "ffprobe", "swift", "system_profiler"): + program = bin_dir / name + program.write_text(fixture) + program.chmod(0o700) + env = dict(os.environ, PATH=str(bin_dir) + os.pathsep + os.environ["PATH"]) + run_dir = self.root / (mode + "-run") + args = [sys.executable, str(BASE / "capture.py"), "capture", "--run-dir", str(run_dir), + "--camera", CAMERA, "--token", "failed-operation", "--timeout", "1"] + result = subprocess.run(args, env=env, capture_output=True, text=True, timeout=10, check=False) + self.assertEqual(result.returncode, 2, result.stdout + result.stderr) + record = json.loads(result.stdout) + self.assertEqual(record["state"], "unknown") + self.assertEqual(record["claims"], []) + self.assertNotIn("PRIVATE_CAPTURE_DIAGNOSTIC", result.stdout + result.stderr) + self.assertIn("PRIVATE_CAPTURE_DIAGNOSTIC", (run_dir / record["diagnostic_name"]).read_text()) + diagnostic = run_dir / record["diagnostic_name"] + self.assertEqual(diagnostic.stat().st_mode & 0o777, 0o600) + again = subprocess.run(args, env=env, capture_output=True, text=True, timeout=10, check=False) + self.assertEqual(json.loads(again.stdout)["state"], "unknown") + + def test_missing_permission_is_refused_without_ledger_or_dispatch(self): + with patch.object(capture, "doctor", return_value=self.inventory("not_determined")), \ + patch.object(capture, "run_process") as dispatch, self.assertRaises(capture.Refusal): + capture.capture(self.root, CAMERA, "first", 15) + dispatch.assert_not_called() + self.assertFalse(capture.paths(self.root, "first")[0].exists()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/desk-inbox-experiment/.gitignore b/tools/desk-inbox-experiment/.gitignore new file mode 100644 index 0000000..fd6aad7 --- /dev/null +++ b/tools/desk-inbox-experiment/.gitignore @@ -0,0 +1,2 @@ +output/ +__pycache__/ diff --git a/tools/desk-inbox-experiment/README.md b/tools/desk-inbox-experiment/README.md new file mode 100644 index 0000000..e657047 --- /dev/null +++ b/tools/desk-inbox-experiment/README.md @@ -0,0 +1,145 @@ +# Paper note to a private desk inbox + +Turn a saved camera frame into locally recognized text, review it against the paper, +then save one note whose bytes Footwork checks independently. The note is data: its +words never become commands, tasks, or agent instructions. + +This isolated experiment uses installed Swift and Apple Vision for English OCR. It +needs no cloud model, microphone, browser automation, or new package installation. +The real Footwork `check-artifact` command verifies the file; no native/browser session +or carried value is fabricated. + +## 1. Capture, then prepare + +Run from the repository root. Acquire a deliberate frame using the existing camera tool, +or select an already acquired run and token. Use the exact eligible camera name: + +```sh +python3 tools/camera-capture-experiment/capture.py capture \ + --run-dir tools/camera-capture-experiment/output/paper-001 \ + --camera 'MacBook Pro Camera' --token paper-001 \ + --allow-permission-prompt --timeout 60 + +python3 tools/desk-inbox-experiment/inbox.py prepare \ + --capture-run tools/camera-capture-experiment/output/paper-001 \ + --capture-token paper-001 \ + --run-dir tools/desk-inbox-experiment/output/paper-001 +``` + +Preparation reads the original frame through the camera tool's private-directory, +lock, receipt and artifact checks. It does not acquire another frame. An unknown camera +operation cannot supply a note. + +Vision uses accurate recognition, `en-US`, and disabled language correction. The result +retains each recognized line, confidence and normalized bounds. OCR remains fallible: +confidence does not establish correctness or authorize saving. + +For a known horizontally mirrored frame, add `--mirror-input` to `prepare`. This chooses +Vision's `upMirrored` orientation and mirrors the review display. It retains the original +PNG and hash. The default explicitly uses `up`. Determine direction from readable calibration +text such as `LEFT 123 RIGHT`; the tool does not classify mirror state or choose a setting +from confidence. A different orientation is a different preparation request. + +An explicit developer fixture route is available: + +```sh +python3 tools/desk-inbox-experiment/inbox.py prepare \ + --fixture-image /absolute/path/to/printed-note.png \ + --run-dir tools/desk-inbox-experiment/output/fixture-001 +``` + +That run is labeled `synthetic_fixture`, including when real Vision processes its image. +It supplies no native-camera acquisition evidence. Inputs must be saved PNGs at most 32 MiB. + +## 2. Review the image and text + +```sh +open tools/desk-inbox-experiment/output/paper-001/review.html +``` + +The local page shows the retained image, explicit orientation, original source path, +recognized text and candidate SHA256. All text and paths are HTML-escaped. It has no +scripts, upload route or automatic acceptance. Compare the text with the paper. + +The original OCR stays in `ocr.json`; `candidate.txt` joins recognized nonblank lines with +LF and adds one final LF. No text means `needs_text`, with no candidate or inbox note. +Preparation repeats reuse the existing candidate. Changed source bytes or orientation +refuse reuse. An interrupted preparation stays unknown and does not rerun OCR. + +If corrections are needed, write a separate reviewed UTF-8 file inside the private run, +then obtain its digest with `shasum -a 256`. Keep `candidate.txt` unchanged. Corrections retain +their exact bytes, including any CRLF or missing terminal newline, and are limited to 128 KiB. +Empty text and NUL bytes are refused. This experiment stores notes, rather than interpreting +their meaning or deciding what should be done. + +## 3. Accept and verify + +After review, use the exact digest shown for the candidate: + +```sh +python3 tools/desk-inbox-experiment/inbox.py accept \ + --run-dir tools/desk-inbox-experiment/output/paper-001 \ + --approved-sha256 REPLACE_WITH_THE_REVIEWED_SHA256 + +python3 tools/desk-inbox-experiment/inbox.py status \ + --run-dir tools/desk-inbox-experiment/output/paper-001 +``` + +For a corrected file, supply `--reviewed-file /absolute/path/to/reviewed.txt` and its +own approved SHA256. The manifest distinguishes corrected text from original OCR. +Digest approval is the caller's explicit review assertion; it does not authenticate a human +or prove that someone compared the paper. + +Acceptance records intent before creating `approved.txt` and `inbox/note.txt`, both private +and create-only. It calls the installed public Footwork CLI: + +```sh +jevdual/.venv/bin/footwork check-artifact DESTINATION \ + --expected-file REVIEWED_SOURCE --expected-sha256 APPROVED_DIGEST +``` + +The checker reads the source, validates its approved SHA256, compares the destination's +raw bytes and rechecks the source. It returns hashes, sizes and a verdict without note +contents. Its 1 MiB limit applies independently to both files; final file symlinks and +nonregular files are rejected. It does not authenticate ancestor directories or provenance. +The desk tool separately requires owned private run directories without symlinks. + +Repeating acceptance reads the original operation. It never overwrites or recreates a +missing or altered note. A partial write, lost receipt, interrupted acceptance or failed +checker leaves the operation unknown and blocks replay. There is no reset command. + +Status rechecks original/retained image hashes, candidate integrity, approved text and +the note. A later failure leaves the historical saved receipt intact while current state +is unknown with empty claims. Preserve source files and the original capture run so their +retained evidence remains checkable. + +The result establishes current file equality to reviewed bytes. It does not establish OCR +accuracy, physical page identity, exposure freshness, native UI completion, exactly-once +camera effects, human-review authentication, or power-loss recovery. + +## Files and checks + +Each run lives under ignored `tools/desk-inbox-experiment/output/`. Images, OCR, candidate +and approved text, HTML review, Swift cache and diagnostics remain private. Run directories +use 0700; created files use 0600. JSON responses contain paths, hashes and operation state, +but no recognized text. One nonblocking process lock permits one owner per run. + +`prepare`, `accept` and `status` return 0 for `needs_review`, `saved` or `absent`; other +states return 2. Footwork `check-artifact` returns 0 for equality and 6 for invalid/mismatched +evidence. Read JSON state as well as the exit code. + +```sh +cd jevdual +uv run --no-sync pytest tests/test_artifacts.py tests/test_cli.py tests/test_carry.py \ + ../tools/desk-inbox-experiment/test_inbox.py -q +uv run --no-sync ruff check python/jevdual/artifacts.py python/jevdual/cli.py \ + tests/test_artifacts.py ../tools/desk-inbox-experiment +``` + +Conformance tests use OCR/process fixtures and the real Footwork file checker. Real Vision +against independently generated normal/mirrored/blank images, and a human's live paper note, +are separate preregistered validations. Neither fixture conformance nor printed-fixture OCR +establishes live handwriting usefulness. + +Preregistration: `jevdual/docs/experiments/DI1-desk-paper-inbox.md`. +Notebook: `jevdual/docs/notebook/2026-10-10-desk-paper-inbox.md`. diff --git a/tools/desk-inbox-experiment/inbox.py b/tools/desk-inbox-experiment/inbox.py new file mode 100644 index 0000000..f5e8058 --- /dev/null +++ b/tools/desk-inbox-experiment/inbox.py @@ -0,0 +1,337 @@ +"""Local saved-frame OCR, explicit review, and Footwork-checked private desk notes.""" + +import argparse +import contextlib +import hashlib +import html +import importlib.util +import json +import os +import re +import shutil +import stat +import sys +import time +from pathlib import Path + +BASE = Path(__file__).resolve().parent +PROJECT = BASE.parents[1] +OUTPUT = BASE / "output" +FOOTWORK = PROJECT / "jevdual" / ".venv" / "bin" / "footwork" +SPEC = importlib.util.spec_from_file_location("camera_capture", BASE.parent / "camera-capture-experiment" / "capture.py") +camera = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(camera) +Refusal = camera.Refusal +MAX_IMAGE_BYTES = 32 * 1024 * 1024 +MAX_NOTE_BYTES = 128 * 1024 +STATES = {"preparing", "needs_text", "needs_review", "accepting", "saved", "unknown"} +DIGEST = re.compile(r"[0-9a-f]{64}\Z") + + +def digest(data): + return hashlib.sha256(data).hexdigest() + + +def private_directory(path): + raw = Path(os.path.abspath(os.fspath(path))) + if raw == OUTPUT or OUTPUT not in raw.parents: + raise Refusal("run directory must be a subdirectory of the desk experiment's output directory") + current = OUTPUT + for part in (None, *raw.relative_to(OUTPUT).parts): + if part is not None: + current = current / part + try: + current.mkdir(mode=0o700) + except FileExistsError: + pass + info = current.lstat() + if not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid() or info.st_mode & 0o077: + raise Refusal("run directories must be owned, private and free of symlinks") + return raw + + +def read_regular(path, limit): + try: + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK) + with os.fdopen(fd, "rb") as source: + before = os.fstat(source.fileno()) + if not stat.S_ISREG(before.st_mode) or before.st_size > limit: + raise Refusal("input must be a regular file within the declared size limit") + data = source.read(limit + 1) + after = os.fstat(source.fileno()) + if len(data) > limit or (before.st_size, before.st_mtime_ns, before.st_ctime_ns) != ( + after.st_size, after.st_mtime_ns, after.st_ctime_ns + ): + raise Refusal("input exceeds its limit or changed during read") + return data + except OSError as exc: + raise Refusal("input is missing, symlinked or unreadable") from exc + + +def create_file(path, data): + fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) + with os.fdopen(fd, "wb") as sink: + sink.write(data) + sink.flush() + os.fsync(sink.fileno()) + + +def load(root): + try: + record = json.loads(read_regular(root / "manifest.json", MAX_NOTE_BYTES)) + except ValueError as exc: + raise Refusal("manifest is not valid JSON") from exc + if not isinstance(record, dict) or record.get("state") not in STATES: + raise Refusal("manifest state is invalid; inspect the run") + return record + + +def persist(root, record): + camera.persist(root / "manifest.json", record) + + +def source_request(capture_run=None, capture_token=None, fixture_image=None, mirror_input=False): + if fixture_image: + if capture_run or capture_token: + raise Refusal("fixture and native capture sources cannot be combined") + path = Path(os.path.abspath(fixture_image)) + request = {"source_kind": "synthetic_fixture", "source_path": str(path), "mirror_input": mirror_input} + else: + if not capture_run or not capture_token: + raise Refusal("prepare requires a capture run and token, or an explicit PNG fixture") + capture_root = camera.private_directory(capture_run) + with camera.locked(capture_root): + retained = camera.status(capture_root, capture_token) + if retained.get("state") != "acquired": + raise Refusal("source camera operation has no currently verifiable acquired frame") + path = camera.paths(capture_root, capture_token)[1] + request = {"source_kind": "native_camera_frame", "source_path": str(path), + "capture_run": str(capture_root), "capture_token": capture_token, + "mirror_input": mirror_input} + data = read_regular(path, MAX_IMAGE_BYTES) + if not data.startswith(b"\x89PNG\r\n\x1a\n"): + raise Refusal("source must be a saved PNG") + request["source_sha256"] = digest(data) + return request, data + + +def recognize(root, mirror_input): + swift = shutil.which("swift") + if not swift: + raise Refusal("installed Swift and Apple Vision are required; nothing will be installed") + cache = root / "swift-module-cache" + if cache.exists() or cache.is_symlink(): + raise Refusal("a prior OCR cache indicates an interrupted preparation; inspect the run") + cache.mkdir(mode=0o700) + orientation = "up_mirrored" if mirror_input else "up" + rc, stdout, stderr = camera.run_process([swift, "-module-cache-path", str(cache), + str(BASE / "recognize.swift"), str(root / "source.png"), + orientation], 45) + create_file(root / "vision.stderr.log", stderr.encode(errors="replace")[:65536]) + if rc: + raise Refusal("local Vision recognition failed; private diagnostics retained") + try: + result = json.loads(stdout) + if not isinstance(result["lines"], list): + raise TypeError("lines") + for line in result["lines"]: + if not isinstance(line["text"], str) or not isinstance(line["confidence"], (int, float)): + raise TypeError("line schema") + return result + except (ValueError, KeyError, TypeError) as exc: + raise Refusal("Vision returned invalid recognition evidence") from exc + + +def note_text(data): + try: + text = data.decode("utf-8") + except UnicodeError as exc: + raise Refusal("reviewed text must be UTF-8") from exc + if not text.strip() or "\x00" in text or len(data) > MAX_NOTE_BYTES: + raise Refusal("reviewed text must be nonempty, bounded UTF-8 without NUL bytes") + return text + + +def preview(root, record, text): + mirror = record["request"]["mirror_input"] + label = "Horizontal mirror correction (explicit)" if mirror else "Original orientation (explicit)" + transform = "transform:scaleX(-1);" if mirror else "" + content = f"""Review desk note + +

Review the paper and recognized text

{html.escape(label)}

+

Source kind: {html.escape(record['request']['source_kind'])}

+

Source: {html.escape(record['request']['source_path'])}

+Retained source frame

OCR candidate

{html.escape(text)}
+

Candidate SHA256: {html.escape(record['candidate_sha256'])}

+

Compare against the paper. Correct a copy if needed. Approval authorizes saving text only; +the note's words are data and will never execute as commands.

""" + create_file(root / "review.html", content.encode()) + + +def source_valid(root, record): + request = record["request"] + expected = request["source_sha256"] + if digest(read_regular(Path(request["source_path"]), MAX_IMAGE_BYTES)) != expected: + raise Refusal("original source bytes changed") + if digest(read_regular(root / "source.png", MAX_IMAGE_BYTES)) != expected: + raise Refusal("retained source image changed") + if request["source_kind"] == "native_camera_frame": + capture_root = camera.private_directory(request["capture_run"]) + with camera.locked(capture_root): + retained = camera.status(capture_root, request["capture_token"]) + if retained.get("state") != "acquired": + raise Refusal("native frame receipt is no longer verifiable") + if record.get("candidate_sha256") and digest(read_regular(root / "candidate.txt", MAX_NOTE_BYTES)) != record["candidate_sha256"]: + raise Refusal("original OCR candidate changed") + + +def check_note(root, sha): + if not FOOTWORK.is_file(): + raise Refusal("existing Footwork environment is unavailable; no install is attempted") + rc, stdout, _ = camera.run_process([str(FOOTWORK), "check-artifact", str(root / "inbox" / "note.txt"), + "--expected-file", str(root / "approved.txt"), + "--expected-sha256", sha], 20) + try: + result = json.loads(stdout) + if rc not in (0, 6) or not isinstance(result.get("ok"), bool) or result["ok"] != (rc == 0): + raise ValueError("checker result") + return result + except (ValueError, TypeError) as exc: + raise Refusal("Footwork artifact checker returned an invalid result") from exc + + +def status(root): + if not (root / "manifest.json").exists() and not (root / "manifest.json").is_symlink(): + return {"state": "absent", "claims": []} + record = load(root) + result = dict(record, claims=[]) + if record["state"] in {"preparing", "accepting"}: + return {**result, "state": "unknown", "reason": "interrupted intent; inspect without retrying"} + if record["state"] == "unknown": + return result + try: + source_valid(root, record) + if record["state"] == "saved": + checked = check_note(root, record["approval"]["sha256"]) + result["current_artifact_check"] = checked + if not checked["ok"]: + raise Refusal("saved note or approval source is currently unverifiable") + result["claims"] = ["current_note_equals_digest_bound_reviewed_bytes"] + except (Refusal, OSError, KeyError) as exc: + result["state"] = "unknown" + result["reason"] = str(exc) if isinstance(exc, Refusal) else "retained evidence is incomplete" + return result + + +def prepare(root, capture_run=None, capture_token=None, fixture_image=None, mirror_input=False): + request, image = source_request(capture_run, capture_token, fixture_image, mirror_input) + if (root / "manifest.json").exists() or (root / "manifest.json").is_symlink(): + record = load(root) + if record["request"] != request: + raise Refusal("run already belongs to different source bytes or orientation") + return status(root) + record = {"version": 1, "state": "preparing", "request": request, "prepare_time_unix": time.time(), + "ocr_helper_sha256": digest((BASE / "recognize.swift").read_bytes())} + persist(root, record) + try: + create_file(root / "source.png", image) + ocr = recognize(root, mirror_input) + if digest(read_regular(Path(request["source_path"]), MAX_IMAGE_BYTES)) != request["source_sha256"]: + raise Refusal("original source changed during recognition") + if digest(read_regular(root / "source.png", MAX_IMAGE_BYTES)) != request["source_sha256"]: + raise Refusal("retained source changed during recognition") + create_file(root / "ocr.json", (json.dumps(ocr, indent=2, ensure_ascii=False) + "\n").encode()) + text = "\n".join(line["text"] for line in ocr["lines"] if line["text"].strip()) + if not text.strip(): + record["state"] = "needs_text" + record["reason"] = "Vision found no text; no candidate or note was saved" + else: + candidate = (text + "\n").encode("utf-8") + note_text(candidate) + create_file(root / "candidate.txt", candidate) + record.update(state="needs_review", candidate_sha256=digest(candidate), + candidate_bytes=len(candidate), preview_name="review.html") + preview(root, record, text) + persist(root, record) + except (Exception, KeyboardInterrupt): # noqa: BLE001 - never replay interrupted preparation + record.update(state="unknown", reason="preparation did not complete; inspect private artifacts") + with contextlib.suppress(OSError): + persist(root, record) + return status(root) + + +def accept(root, approved_sha256, reviewed_file=None): + if not DIGEST.fullmatch(approved_sha256): + raise Refusal("approval must be an exact lowercase SHA256 digest") + record = load(root) + approved = {"sha256": approved_sha256, + "reviewed_input_path": str(Path(os.path.abspath(reviewed_file))) if reviewed_file else None} + if record["state"] in {"saved", "accepting", "unknown"}: + if record.get("approval") != approved: + raise Refusal("run already has a different or incomplete approval") + return status(root) + if record["state"] != "needs_review": + raise Refusal("no reviewed-text candidate is available") + source_valid(root, record) + data = read_regular(Path(approved["reviewed_input_path"]) if reviewed_file else root / "candidate.txt", MAX_NOTE_BYTES) + note_text(data) + if digest(data) != approved_sha256: + raise Refusal("reviewed text no longer matches the explicitly approved digest") + if reviewed_file is None and approved_sha256 != record["candidate_sha256"]: + raise Refusal("candidate digest changed") + record.update(state="accepting", approval=approved, review_kind="corrected_text" if reviewed_file else "original_ocr", + approval_time_unix=time.time()) + persist(root, record) + try: + create_file(root / "approved.txt", data) + (root / "inbox").mkdir(mode=0o700) + create_file(root / "inbox" / "note.txt", data) + checked = check_note(root, approved_sha256) + if not checked["ok"]: + raise Refusal("saved note did not match approved bytes") + record.update(state="saved", historical_save_outcome="saved", saved_time_unix=time.time(), + saved_artifact_check=checked, note_name="inbox/note.txt") + persist(root, record) + except (Exception, KeyboardInterrupt): # noqa: BLE001 - a partial publication is never retried + record.update(state="unknown", historical_save_outcome="unknown", + reason="publication or receipt did not complete; inspect without replay") + with contextlib.suppress(OSError): + persist(root, record) + return status(root) + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + sub = parser.add_subparsers(dest="command", required=True) + for command in ("prepare", "accept", "status"): + child = sub.add_parser(command) + child.add_argument("--run-dir", required=True) + if command == "prepare": + child.add_argument("--capture-run") + child.add_argument("--capture-token") + child.add_argument("--fixture-image") + child.add_argument("--mirror-input", action="store_true") + elif command == "accept": + child.add_argument("--approved-sha256", required=True) + child.add_argument("--reviewed-file") + args = parser.parse_args(argv) + try: + root = private_directory(args.run_dir) + with camera.locked(root): + if args.command == "prepare": + result = prepare(root, args.capture_run, args.capture_token, args.fixture_image, args.mirror_input) + elif args.command == "accept": + result = accept(root, args.approved_sha256, args.reviewed_file) + else: + result = status(root) + print(json.dumps(result, sort_keys=True, indent=2)) + return 0 if result["state"] in {"needs_review", "saved", "absent"} else 2 + except (Refusal, OSError, KeyError, KeyboardInterrupt) as exc: + reason = str(exc) if isinstance(exc, Refusal) else "operation interrupted or local evidence unavailable" + print(json.dumps({"state": "refused", "reason": reason, "claims": []})) + return 2 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/desk-inbox-experiment/recognize.swift b/tools/desk-inbox-experiment/recognize.swift new file mode 100644 index 0000000..d69e1a8 --- /dev/null +++ b/tools/desk-inbox-experiment/recognize.swift @@ -0,0 +1,44 @@ +import Foundation +import ImageIO +import Vision + +guard CommandLine.arguments.count == 3, + ["up", "up_mirrored"].contains(CommandLine.arguments[2]) else { + fputs("expected saved image path and explicit orientation\n", stderr) + exit(2) +} +let url = URL(fileURLWithPath: CommandLine.arguments[1]) +guard let source = CGImageSourceCreateWithURL(url as CFURL, nil), + let image = CGImageSourceCreateImageAtIndex(source, 0, nil) else { + fputs("saved image could not be decoded\n", stderr) + exit(2) +} +let request = VNRecognizeTextRequest() +request.recognitionLevel = .accurate +request.recognitionLanguages = ["en-US"] +request.usesLanguageCorrection = false +do { + let orientation: CGImagePropertyOrientation = CommandLine.arguments[2] == "up_mirrored" ? .upMirrored : .up + try VNImageRequestHandler(cgImage: image, orientation: orientation, options: [:]).perform([request]) + let observations = (request.results ?? []).sorted { + if $0.boundingBox.midY == $1.boundingBox.midY { + return $0.boundingBox.minX < $1.boundingBox.minX + } + return $0.boundingBox.midY > $1.boundingBox.midY + } + let lines: [[String: Any]] = observations.compactMap { observation in + guard let candidate = observation.topCandidates(1).first else { return nil } + let box = observation.boundingBox + return ["text": candidate.string, "confidence": Double(candidate.confidence), + "bounds": ["x": box.minX, "y": box.minY, "width": box.width, "height": box.height]] + } + let result: [String: Any] = ["engine": "Apple Vision", "recognition_level": "accurate", + "languages": ["en-US"], "language_correction": false, "lines": lines, + "request_revision": request.revision, "orientation": CommandLine.arguments[2]] + let data = try JSONSerialization.data(withJSONObject: result, options: [.sortedKeys]) + FileHandle.standardOutput.write(data) + FileHandle.standardOutput.write(Data([10])) +} catch { + fputs("local Vision recognition failed\n", stderr) + exit(2) +} diff --git a/tools/desk-inbox-experiment/test_inbox.py b/tools/desk-inbox-experiment/test_inbox.py new file mode 100644 index 0000000..f34e9a6 --- /dev/null +++ b/tools/desk-inbox-experiment/test_inbox.py @@ -0,0 +1,272 @@ +"""Desk conformance at file and process boundaries; real Vision is a separate run.""" + +import base64 +import importlib.util +import json +import os +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +BASE = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("desk_inbox", BASE / "inbox.py") +inbox = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(inbox) +PNG = base64.b64decode( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=" +) +OCR = {"engine": "fixture", "orientation": "up", "lines": [ + {"text": "First note", "confidence": .8, "bounds": {"x": .1, "y": .8, "width": .5, "height": .1}}, + {"text": "Second note", "confidence": .7, "bounds": {"x": .1, "y": .6, "width": .5, "height": .1}}, +]} + + +class InboxTests(unittest.TestCase): + def setUp(self): + inbox.OUTPUT.mkdir(mode=0o700, exist_ok=True) + self.tmp = tempfile.TemporaryDirectory(prefix="conformance-", dir=inbox.OUTPUT) + self.addCleanup(self.tmp.cleanup) + self.work = Path(self.tmp.name) + self.root = inbox.private_directory(self.work / "run") + self.image = self.work / "printed<&>.png" + self.image.write_bytes(PNG) + + def prepare(self, result=OCR, mirror=False): + with patch.object(inbox, "recognize", return_value=result) as recognize: + prepared = inbox.prepare(self.root, fixture_image=str(self.image), mirror_input=mirror) + return prepared, recognize + + def test_prepare_and_accept_use_real_footwork_checker_without_text_output(self): + prepared, _ = self.prepare() + self.assertEqual(prepared["state"], "needs_review") + self.assertEqual(prepared["request"]["source_kind"], "synthetic_fixture") + self.assertEqual((self.root / "candidate.txt").read_bytes(), b"First note\nSecond note\n") + result = inbox.accept(self.root, prepared["candidate_sha256"]) + self.assertEqual(result["state"], "saved") + self.assertTrue(result["current_artifact_check"]["ok"]) + self.assertNotIn("First note", json.dumps(result)) + self.assertEqual((self.root / "inbox" / "note.txt").read_bytes(), (self.root / "candidate.txt").read_bytes()) + self.assertFalse((self.root / "session.json").exists()) + self.assertFalse((self.root / "carries.json").exists()) + + def test_repeated_prepare_and_accept_do_not_rewrite(self): + prepared, _ = self.prepare() + before = (self.root / "manifest.json").read_bytes() + with patch.object(inbox, "recognize") as recognize: + again = inbox.prepare(self.root, fixture_image=str(self.image)) + recognize.assert_not_called() + self.assertEqual(again["candidate_sha256"], prepared["candidate_sha256"]) + self.assertEqual((self.root / "manifest.json").read_bytes(), before) + inbox.accept(self.root, prepared["candidate_sha256"]) + before = (self.root / "manifest.json").read_bytes() + note_before = (self.root / "inbox" / "note.txt").stat() + again = inbox.accept(self.root, prepared["candidate_sha256"]) + self.assertEqual(again["state"], "saved") + self.assertEqual((self.root / "manifest.json").read_bytes(), before) + self.assertEqual((self.root / "inbox" / "note.txt").stat().st_mtime_ns, note_before.st_mtime_ns) + self.assertEqual(len(list((self.root / "inbox").iterdir())), 1) + + def test_mirror_is_bound_explicit_and_preview_escapes_text(self): + malicious = {"lines": [{"text": " & commands", "confidence": .9}]} + prepared, recognize = self.prepare(malicious, mirror=True) + self.assertTrue(prepared["request"]["mirror_input"]) + recognize.assert_called_once_with(self.root, True) + page = (self.root / "review.html").read_text() + self.assertNotIn(" + + diff --git a/tools/physical-workcell/bench_operator.py b/tools/physical-workcell/bench_operator.py new file mode 100644 index 0000000..71aa8d5 --- /dev/null +++ b/tools/physical-workcell/bench_operator.py @@ -0,0 +1,148 @@ +"""Loopback browser product for the guarded simulated bench operator.""" +import argparse +import json +import signal +import threading +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from urllib.parse import urlsplit + +import operator_runtime as runtime +import order_protocol as protocol + +BASE = Path(__file__).resolve().parent + + +class Server(ThreadingHTTPServer): + daemon_threads = True + allow_reuse_address = True + + def __init__(self, address, operator): + super().__init__(address, Handler) + self.operator = operator + self.origin = f"http://127.0.0.1:{self.server_address[1]}" + self.host = f"127.0.0.1:{self.server_address[1]}" + + +class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def log_message(self, *_): + pass + + def reply(self, code, body, content_type="application/json"): + if isinstance(body, dict): + body = json.dumps(body, allow_nan=False).encode() + self.send_response(code) + self.send_header("Content-Type", content_type) + self.send_header("Content-Length", str(len(body))) + self.send_header("Cache-Control", "no-store") + self.send_header("X-Content-Type-Options", "nosniff") + self.send_header("Referrer-Policy", "no-referrer") + self.send_header("Connection", "close") + self.end_headers() + self.wfile.write(body) + self.close_connection = True + + def exact_header(self, name, value): + return self.headers.get_all(name) == [value] + + def do_GET(self): + try: + if not self.exact_header("Host", self.server.host): + raise protocol.Refusal("exact loopback Host is required") + parsed = urlsplit(self.path) + if parsed.path == "/" and not parsed.query: + self.server.operator._identity() + self.reply(200, protocol.read_bytes(BASE / "bench_operator.html"), "text/html; charset=utf-8") + elif parsed.path == "/api/state" and not parsed.query: + self.reply(200, self.server.operator.state()) + elif parsed.path == "/api/frame": + self.reply(200, self.server.operator.frame(self.path), "image/png") + else: + self.reply(404, {"error": "Unknown operator route"}) + except (protocol.Refusal, OSError, ValueError) as exc: + self.reply(409, {"error": str(exc)}) + + def do_POST(self): + try: + app_id = self.server.operator.m["app_id"] + if (not self.exact_header("Host", self.server.host) or + not self.exact_header("Origin", self.server.origin) or + not self.exact_header("X-Footwork-App-ID", app_id)): + raise protocol.Refusal("exact loopback origin and application identity are required before mutation") + lengths = self.headers.get_all("Content-Length") + if (lengths is None or len(lengths) != 1 or not lengths[0].isascii() or + not lengths[0].isdigit() or not 1 <= int(lengths[0]) <= 4096 or + self.headers.get_all("Transfer-Encoding") is not None or + not self.exact_header("Content-Type", "application/json")): + raise protocol.Refusal("request requires one bounded JSON body with an exact content length") + self.connection.settimeout(5) + content = self.rfile.read(int(lengths[0])) + if len(content) != int(lengths[0]): + raise protocol.Refusal("JSON body is incomplete") + request = protocol.decode(content) + if self.path == "/api/jobs": + result = self.server.operator.enqueue(request) + elif self.path in ("/api/pause", "/api/resume"): + protocol.fields(request, (), "operator control") + result = getattr(self.server.operator, self.path.rsplit("/", 1)[1])() + else: + raise protocol.Refusal("unknown operator mutation route") + self.reply(200, result) + except (protocol.Refusal, OSError, TypeError, ValueError) as exc: + self.reply(409, {"error": str(exc)}) + + +def serve(root, port, policy): + root = protocol.directory(root) + operator = runtime.Operator(root, policy) + server = None + try: + server = Server(("127.0.0.1", port), operator) + readiness = root / "readiness.json" + if readiness.exists(): + previous = protocol.decode(protocol.read_bytes(readiness)) + if previous["app_id"] != operator.m["app_id"] or previous["origin"] != server.origin: + raise protocol.Refusal("retained application origin changed; restart on its exact port") + protocol.persist(readiness, {"app_id": operator.m["app_id"], "origin": server.origin, + "port": server.server_address[1], "simulation": True}) + print(json.dumps(protocol.decode(protocol.read_bytes(readiness))), flush=True) + def stop(*_): + operator.cancel.set() + threading.Thread(target=server.shutdown, daemon=True).start() + signal.signal(signal.SIGTERM, stop) + signal.signal(signal.SIGINT, stop) + server.serve_forever(poll_interval=.2) + finally: + if server: + server.server_close() + operator.close() + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest="command", required=True) + init = commands.add_parser("init") + init.add_argument("--root", type=Path, required=True) + run = commands.add_parser("serve") + run.add_argument("--root", type=Path, required=True) + run.add_argument("--port", type=int, default=0) + run.add_argument("--backend", choices=("codex",), default="codex") + args = parser.parse_args() + try: + if args.command == "init": + print(json.dumps({"app_id": runtime.initialize(args.root), "simulation": True})) + else: + if not 0 <= args.port <= 65535: + raise protocol.Refusal("port must be in the TCP range") + from codex_policy import decide + serve(args.root, args.port, decide) + return 0 + except (protocol.Refusal, OSError, ValueError) as exc: + print(json.dumps({"error": str(exc)})) + return 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/physical-workcell/bench_runner.py b/tools/physical-workcell/bench_runner.py new file mode 100644 index 0000000..a06558d --- /dev/null +++ b/tools/physical-workcell/bench_runner.py @@ -0,0 +1,170 @@ +"""Inspectable RGB-only host for successive simulated bench experiments. + +This is a deterministic diagnostic policy, not a language-model robotics trial. +All guidance comes from exact public PNGs; private native state belongs to scoring. +""" + +import argparse +import importlib.util +import math +from pathlib import Path + +BASE = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("persistent_bench", BASE / "bench.py") +bench = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(bench) +body, vision = bench.body, bench.vision +ZONE = {"x_min": .035, "x_max": .105, "y_min": -.025, "y_max": .025} +HOME_X = -.12 +MAX_ACTIONS = 64 + + +class RGBHost: + def __init__(self, root, journal): + self.root = body.directory(root) + self.journal = body.directory(journal, create=True) + self.events = [] + self.sequence = 0 + self.job_id = None + + def api(self): + # Each call reloads the same retained body; never initializes an episode. + return bench.Bench(self.root) + + def record(self, kind, **fields): + event = {"index": len(self.events), "kind": kind, **fields} + body.create_file(self.journal / f"event-{len(self.events):04d}.json", body.encoded(event)) + self.events.append(event) + return event + + def observe(self): + packet = self.api().observe() + content = body.read_bytes(Path(packet["image_path"])) + if body.sha(content) != packet["image_sha256"]: + raise body.Refusal("public image bytes changed") + located = vision.locate(vision.decode_png(content)) + block = vision.image_world_center(located["block"]["box"]) + pusher = vision.image_world_center(located["pusher"]["box"]) + self.record("rgb_observation", packet=packet, rgb_block_xy=block, rgb_pusher_xy=pusher) + return packet, block, pusher + + def move(self, packet, pusher, target, purpose): + delta = [target[i] - pusher[i] for i in (0, 1)] + distance = math.hypot(*delta) + if distance > .11: + target = [pusher[i] + delta[i] * .11 / distance for i in (0, 1)] + self.sequence += 1 + if self.sequence > MAX_ACTIONS: + raise body.Refusal("RGB host motion budget exhausted") + operation = f"{self.job_id}-motion-{self.sequence:03d}" + receipt = self.api().move(operation, packet, *target) + self.record("motion", purpose=purpose, target_xy=target, receipt=receipt) + return receipt + + def position(self, center_x, purpose): + for _ in range(MAX_ACTIONS): + packet, block, pusher = self.observe() + if abs(block[1]) > .018: + raise body.Refusal("RGB block is outside the supported axial recovery corridor") + if abs(block[0] - center_x) <= .012: + return packet + direction = 1 if center_x > block[0] else -1 + side = block[0] - direction * .065 + wrong_side = direction * (block[0] - pusher[0]) < .050 + if wrong_side: + target = [pusher[0], .135] if pusher[1] < .125 else [side, .135] + elif abs(pusher[1]) > .006: + # Stay clear of the cube before descending from the bypass lane. + target = [side, pusher[1]] if abs(pusher[0] - side) > .010 else [pusher[0], 0] + else: + gap = max(0, direction * (block[0] - pusher[0]) - .055) + advance = min(.09, abs(center_x - block[0]) + gap) + target = [pusher[0] + direction * advance, 0] + if not -.32 <= target[0] <= .18: + raise body.Refusal("RGB recovery requires a pusher target outside the workspace") + self.move(packet, pusher, target, purpose) + raise body.Refusal("RGB placement did not converge") + + def park(self, purpose): + for _ in range(MAX_ACTIONS): + packet, block, pusher = self.observe() + if abs(pusher[0] + .3) <= .004 and abs(pusher[1]) <= .004: + return packet + if pusher[0] > block[0] - .065 and pusher[1] < .125: + target = [pusher[0], .135] + elif pusher[0] > -.29: + target = [-.3, pusher[1]] + else: + target = [-.3, 0] + self.move(packet, pusher, target, purpose) + raise body.Refusal("RGB pusher parking did not converge") + + def run_job(self, job_id, *, disturbance=False): + self.job_id, self.sequence = job_id, 0 + started = self.api().start_job(job_id, ZONE) + self.record("job_started", status=started) + self.position(.07, "prepare") + packet = self.park("clear_measurement_area") + recovery = None + if disturbance: + changed = self.api().disturb(f"{job_id}-external-force", -1.5, 80) + self.record("diagnostic_external_force", receipt=changed) + refused = [] + for command in ("move", "measure"): + before = self.api().status() + try: + if command == "move": + self.api().move(f"{job_id}-stale-motion", packet, -.28, 0) + else: + self.api().measure(f"{job_id}-stale-measurement", packet) + except body.Refusal as exc: + after = self.api().status() + if before != after: + raise AssertionError("stale refusal changed durable public state") from exc + refused.append(command) + self.record("stale_effect_refused", command=command, reason=str(exc)) + else: + raise AssertionError(f"stale {command} was accepted") + fresh, changed_block, _ = self.observe() + previous = vision.image_world_center(vision.locate(vision.decode_png( + body.read_bytes(Path(packet["image_path"]))))["block"]["box"]) + displacement = abs(changed_block[0] - previous[0]) + if displacement < .02: + raise AssertionError("diagnostic force did not create a meaningful observed displacement") + recovery = {"stale_refused": refused, "rgb_displacement_m": displacement, + "before_generation": packet["generation"], "after_generation": fresh["generation"]} + self.position(.07, "recover_unexpected_displacement") + packet = self.park("clear_recovered_measurement_area") + measurement = self.api().measure(f"{job_id}-measurement", packet) + self.record("computer_measurement", receipt=measurement) + repeat = self.api().measure(f"{job_id}-measurement", packet) + if repeat != measurement: + raise AssertionError("measurement repeat differs from exact historical receipt") + self.record("measurement_reply_reconciled", receipt=repeat) + self.record("reset_started", status=self.api().begin_reset(job_id)) + self.position(HOME_X, "contact_reset") + packet = self.park("return_pusher_home") + finished = self.api().finish_reset(f"{job_id}-reset", packet) + self.record("reset_verified", receipt=finished) + return {"job_id": job_id, "measurement": measurement, "reset": finished, + "recovery": recovery, "motion_count": self.sequence} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--bench-dir", required=True) + parser.add_argument("--journal-dir", required=True) + parser.add_argument("--job-id", required=True) + parser.add_argument("--disturb", action="store_true") + args = parser.parse_args() + host = RGBHost(Path(args.bench_dir), Path(args.journal_dir)) + try: + result = host.run_job(args.job_id, disturbance=args.disturb) + except Exception as exc: + host.record("attempt_failed", error_type=type(exc).__name__, error=str(exc)) + raise + print(body.encoded(result).decode(), end="") + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/body_client.py b/tools/physical-workcell/body_client.py new file mode 100644 index 0000000..b97a443 --- /dev/null +++ b/tools/physical-workcell/body_client.py @@ -0,0 +1,98 @@ +"""Simulated bench vocabulary on the reusable durable single-send client. + +Existing callers keep BodyClient. Other adapters use PersistentBodyClient with +an explicit operation contract; they do not inherit block or PNG semantics. +""" +import order_protocol as protocol +from bench_body_contract import BenchOperations, challenge_packet +from persistent_body_client import ( + ENVELOPE_KEYS, + IDENTITY_KEYS, + MAX_CLIENT_JOURNAL, + PROTOCOL, + PersistentBodyClient, + Refusal, + Uncertain, + endpoint, + http_call, + identity, + pinned_headers, + read_journal, +) + +__all__ = [ + "ENVELOPE_KEYS", + "IDENTITY_KEYS", + "MAX_CLIENT_JOURNAL", + "PROTOCOL", + "BodyClient", + "PersistentBodyClient", + "Refusal", + "Uncertain", + "endpoint", + "http_call", + "identity", + "pinned_headers", + "read_journal", +] + +MAX_IMAGE = 8 * 1024 * 1024 + + +class BodyClient(PersistentBodyClient): + def __init__(self, root, origin, timeout=15): + super().__init__(root, origin, BenchOperations(), timeout=timeout) + + def _challenge(self, m, challenge): + return challenge_packet(m["identity"], challenge) + + def _download(self, m, challenge): + if challenge is None: + return None + packet = self._challenge(m, challenge) + status, content = http_call(self.origin, "GET", "/images/" + challenge["challenge_id"], pinned_headers(m["identity"]), + timeout=self.timeout, maximum=MAX_IMAGE) + if status != 200 or protocol.sha(content) != packet["image_sha256"] or not content.startswith(b"\x89PNG\r\n\x1a\n"): + raise Refusal("downloaded body image failed its exact PNG digest check") + path = self.root / "images" / (challenge["challenge_id"] + "-" + packet["image_sha256"] + ".png") + if path.exists(): + if protocol.read_bytes(path, maximum=MAX_IMAGE) != content: + raise Refusal("client image changed") + else: + protocol.create_file(path, content) + return {**challenge, "image_path": str(path)} + + def _result(self, m, operation, result): + if operation == "observe": + return self._download(m, result) + return result + + def start_job(self, job_id, zone): + protocol.identity(job_id) + protocol.validate_zone(zone) + return self._command("start_job", {"job_id": job_id, "target_zone": zone}) + + def observe(self): + return self._command("observe", {}) + + def propose(self, challenge_id, action, intent, target_x=None, target_y=None): + protocol.identity(challenge_id) + if action not in {"move", "measure", "begin_reset", "finish_reset"} or not isinstance(intent, str) or not 1 <= len(intent.strip()) <= 2000: + raise Refusal("proposal needs a supported action and bounded intent") + if action == "move": + target_x, target_y = protocol.finite(target_x), protocol.finite(target_y) + elif target_x is not None or target_y is not None: + raise Refusal("only movement proposals accept targets") + return self._command("propose", {"challenge_id": challenge_id, "action": action, "intent": intent, + "target_x": target_x, "target_y": target_y}) + + def dispatch(self, proposal_id): + protocol.identity(proposal_id) + return self._command("dispatch", {"proposal_id": proposal_id}) + + def disturb(self, operation_id, force_x_n, steps): + protocol.identity(operation_id) + force = protocol.finite(force_x_n) + if type(steps) is not int: + raise Refusal("diagnostic steps must be an integer") + return self._command("disturb", {"operation_id": operation_id, "force_x_n": force, "steps": steps}) diff --git a/tools/physical-workcell/body_contract.py b/tools/physical-workcell/body_contract.py new file mode 100644 index 0000000..089f71b --- /dev/null +++ b/tools/physical-workcell/body_contract.py @@ -0,0 +1,106 @@ +"""Device-independent bounded identity, lease, command and receipt schemas. + +Operation arguments and lower-proof semantics belong to an explicit adapter +contract. Nothing here imports a simulator or grants physical authority. +""" +import order_protocol as storage + +PROTOCOL = "footwork.body/1" +MAX_COMMANDS = 256 +Refusal = storage.Refusal +decode, encoded, sha = storage.decode, storage.encoded, storage.sha +fields, identity, finite = storage.fields, storage.identity, storage.finite +PIN_KEYS = {"protocol", "adapter_id", "body_id", "source_sha256"} +IDENTITY_FIELDS = PIN_KEYS | {"epoch", "simulation"} +COMMAND_FIELDS = PIN_KEYS | {"client_id", "lease_id", "epoch", "command_id", "operation", "arguments"} +ENVELOPE_FIELDS = PIN_KEYS | {"command_id", "request_sha256", "state", "result", "reason", "receipt_scope", "recorded_epoch"} +RECEIPT_STATES = {"completed", "refused", "unknown", "not_seen", "not_accepted"} +TERMINAL_STATES = {"completed", "refused", "not_accepted"} +RECEIPT_SCOPE = "retained_command_result" + + +def epoch(value): + if type(value) is not int or not 1 <= value <= 2147483647: + raise Refusal("boot epoch must be a positive bounded integer") + return value + + +def digest(value): + if not isinstance(value, str) or not storage.DIGEST.fullmatch(value): + raise Refusal("SHA256 must be an exact lowercase digest") + return value + + +def validate_identity(value): + fields(value, IDENTITY_FIELDS, "body identity") + if value["protocol"] != PROTOCOL or value["simulation"] is not True: + raise Refusal("unsupported body protocol or simulation identity") + for key in ("adapter_id", "body_id"): + identity(value[key]) + digest(value["source_sha256"]) + epoch(value["epoch"]) + return value + + +def command_digest(request): + return sha(encoded(request)) + + +def validate_command(request): + fields(request, COMMAND_FIELDS, "body command") + if request["protocol"] != PROTOCOL: + raise Refusal("unsupported body protocol") + for key in ("adapter_id", "body_id", "client_id", "lease_id", "command_id", "operation"): + identity(request[key]) + digest(request["source_sha256"]) + epoch(request["epoch"]) + if not isinstance(request["arguments"], dict): + raise Refusal("operation arguments must be an object") + return request + + +def validate_lease_request(value): + fields(value, ("client_id", "expected_epoch", "duration_s"), "lease request") + identity(value["client_id"]) + epoch(value["expected_epoch"]) + duration = finite(value["duration_s"]) + if not .05 <= duration <= 30: + raise Refusal("lease duration must be between 0.05 and 30 seconds") + return duration + + +def validate_reconcile(value): + fields(value, ("client_id", "lease_id", "epoch", "request_sha256"), "reconcile query") + for key in ("client_id", "lease_id"): + identity(value[key]) + epoch(value["epoch"]) + digest(value["request_sha256"]) + return value + + +def validate_envelope(value, request, request_sha256): + fields(value, ENVELOPE_FIELDS, "command result envelope") + for key in PIN_KEYS | {"command_id"}: + if value[key] != request[key]: + raise Refusal("command result identity changed") + digest(value["request_sha256"]) + if (value["request_sha256"] != request_sha256 or value["receipt_scope"] != RECEIPT_SCOPE or + not isinstance(value["state"], str) or value["state"] not in RECEIPT_STATES): + raise Refusal("command result digest, scope or state is invalid") + epoch(value["recorded_epoch"]) + if value["recorded_epoch"] != request["epoch"]: + raise Refusal("historical command epoch changed") + if value["reason"] is not None and not isinstance(value["reason"], str): + raise Refusal("command reason must be text or null") + if value["result"] is not None and not isinstance(value["result"], dict): + raise Refusal("command result must be an object or null") + if value["state"] == "completed" and not isinstance(value["result"], dict): + raise Refusal("completed command needs its typed result") + return value + + +def terminal(value, lower_unknown): + """A lower unresolved effect cannot be cleared by a completed outer receipt.""" + if type(lower_unknown) is not bool: + raise Refusal("operation contract must classify unresolved effects explicitly") + return value["state"] in TERMINAL_STATES and not lower_unknown diff --git a/tools/physical-workcell/body_network.py b/tools/physical-workcell/body_network.py new file mode 100644 index 0000000..c69cf19 --- /dev/null +++ b/tools/physical-workcell/body_network.py @@ -0,0 +1,429 @@ +"""Durable loopback body server; uncertainty never causes an effect replay.""" +import argparse +import os +import signal +import threading +import time +import uuid +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from urllib.parse import parse_qs, urlsplit + +import agent_bench as agent +import bench +import body_contract as contract +import body_protocol as wire +import order_protocol as storage + +BASE = Path(__file__).resolve().parent + + +def sources(): + return {**agent.sources(), **{name: storage.sha(storage.read_bytes(BASE / name)) + for name in ("body_contract.py", "bench_body_contract.py", "body_protocol.py", "body_network.py")}} + + +def source_sha(): + return storage.sha(storage.encoded(sources())) + + +def initialize(root, diagnostic_enabled=False): + root = storage.directory(root, create=True) + with storage.locked(root): + if any(p.name != "app.lock" for p in root.iterdir()): + raise wire.Refusal("body initialization requires an empty owned directory") + native = bench.initialize(root / "bench", diagnostic_enabled=diagnostic_enabled) + agent.initialize(root / "agent", root / "bench") + (root / "commands").mkdir(mode=0o700) + identity = {"protocol": wire.PROTOCOL, "adapter_id": uuid.uuid4().hex, + "body_id": native["bench_id"], "source_sha256": source_sha(), "simulation": True} + storage.create_file(root / "identity.json", storage.encoded(identity)) + storage.persist(root / "service.json", {"epoch": 0, "identity_sha256": storage.sha(storage.encoded(identity))}) + return identity + + +class BodyService: + def __init__(self, root, host="127.0.0.1", port=0, barrier=None): + if host != "127.0.0.1" or type(port) is not int or not 0 <= port <= 65535: + raise wire.Refusal("body service must bind literal IPv4 loopback") + if barrier not in (None, "before_dispatch", "after_dispatch"): + raise wire.Refusal("unknown diagnostic barrier") + self.root = storage.directory(root) + self.lock = threading.RLock() + self.owner = storage.locked(self.root) + self.owner.__enter__() + self.closed = False + self.lease = None + self.barrier = barrier + self.thread = None + try: + self.pinned = storage.decode(storage.read_bytes(self.root / "identity.json")) + storage.fields(self.pinned, ("protocol", "adapter_id", "body_id", "source_sha256", "simulation"), "adapter identity") + m = storage.decode(storage.read_bytes(self.root / "service.json")) + storage.fields(m, ("epoch", "identity_sha256"), "service boot record") + if m["identity_sha256"] != storage.sha(storage.encoded(self.pinned)) or type(m["epoch"]) is not int: + raise wire.Refusal("adapter identity or boot record changed") + self.epoch = wire.epoch(m["epoch"] + 1) + self.body = bench.Bench(self.root / "bench") + self.agent = agent.AgentBench(self.root / "agent") + self._identity_guard() + storage.persist(self.root / "service.json", {"epoch": self.epoch, "identity_sha256": m["identity_sha256"]}) + self.records = {} + self._restore() + self.http = _Server((host, port), self) + self.origin = f"http://127.0.0.1:{self.http.server_address[1]}" + except Exception: + self.owner.__exit__(None, None, None) + raise + + def _identity_guard(self): + if (self.pinned["protocol"] != wire.PROTOCOL or self.pinned["simulation"] is not True or + self.pinned["source_sha256"] != source_sha() or + storage.decode(storage.read_bytes(self.root / "identity.json")) != self.pinned or + self.body.status()["bench_id"] != self.pinned["body_id"]): + raise wire.Refusal("pinned adapter, body, or measured source changed") + + def identity(self): + with self.lock: + self._identity_guard() + return {**self.pinned, "epoch": self.epoch} + + def start(self): + if self.thread is not None: + raise wire.Refusal("body service has already started") + self.thread = threading.Thread(target=self.http.serve_forever, kwargs={"poll_interval": .1}, daemon=True) + self.thread.start() + return self + + def close(self): + self.closed = True + if self.thread: + self.http.shutdown() + self.thread.join(5) + self.http.server_close() + with self.lock: + self.lease = None + self.owner.__exit__(None, None, None) + + def _directory(self, command_id): + wire.identity(command_id) + return self.root / "commands" / storage.sha(command_id.encode()) + + def _envelope(self, request, state, result=None, reason=None): + return {**{k: self.pinned[k] for k in ("protocol", "adapter_id", "body_id", "source_sha256")}, "command_id": request["command_id"], "request_sha256": wire.command_digest(request), + "state": state, "result": result, "reason": reason, "receipt_scope": "retained_command_result", + "recorded_epoch": request["epoch"]} + + def _reply(self, request, state, result=None, reason=None): + value = self._envelope(request, state, result, reason) + return value + + def _read_record(self, directory): + raw = storage.read_bytes(directory / "intent.json") + record = storage.decode(raw) + storage.fields(record, ("request", "request_sha256", "result_sha256"), "command journal") + request = wire.validate_command(record["request"]) + if (record["request_sha256"] != wire.command_digest(request) or directory != self._directory(request["command_id"]) or + any(request[key] != self.pinned[key] for key in ("protocol", "adapter_id", "body_id", "source_sha256"))): + raise wire.Refusal("retained command request or identity changed") + if record["result_sha256"] is not None: + wire.digest(record["result_sha256"]) + content = storage.read_bytes(directory / "result.json") + result = storage.decode(content) + storage.fields(result, wire.ENVELOPE_FIELDS, "command result") + if result["state"] not in {"completed", "refused", "unknown"}: + raise wire.Refusal("retained result state is invalid") + if storage.sha(content) != record["result_sha256"] or any(result[k] != self._reply(request, result["state"])[k] + for k in ("protocol", "adapter_id", "body_id", "source_sha256", "command_id", "request_sha256", "recorded_epoch", "receipt_scope")): + raise wire.Refusal("retained terminal result changed") + return record + + def _terminal(self, directory, record, envelope): + content = storage.encoded(envelope) + path = directory / "result.json" + # An orphan transport result is never an authority source; retain it separately. + if path.exists(): + path.rename(directory / ("orphan-result-" + uuid.uuid4().hex + ".json")) + storage.create_file(path, content) + terminal = {**record, "result_sha256": storage.sha(content)} + storage.persist(directory / "intent.json", terminal) + record.update(terminal) + + def _restore(self): + entries = list((self.root / "commands").iterdir()) + if len(entries) > wire.MAX_COMMANDS: + raise wire.Refusal("retained command budget exceeded") + for directory in entries: + if directory.is_symlink() or not directory.is_dir(): + raise wire.Refusal("command journal must contain owned directories") + if not (directory / "intent.json").exists(): + # No accepted intent implies no effect; admission has not happened. + continue + record = self._read_record(directory) + request = record["request"] + self.records[request["command_id"]] = record + if record["result_sha256"] is None and request["operation"] == "dispatch": + # Recover only indexed, SHA-verified historical lower proof. Never dispatch. + try: + with storage.locked(self.root / "agent"): + manifest, _ = self.agent._load() + proposal_id = request["arguments"]["proposal_id"] + self.agent._read(manifest, "proposals", proposal_id) + self.agent._read(manifest, "intents", proposal_id) + result = self.agent._read(manifest, "results", proposal_id) + if result["proposal_id"] != proposal_id: + raise wire.Refusal("lower dispatch identity differs") + outer = "unknown" if result["outcome"] == "unknown" else "completed" + self._terminal(directory, record, self._reply(request, outer, agent.public_response(result), + "read-only lower receipt recovered after restart")) + except Exception as exc: # noqa: BLE001 - absent lower proof must remain unresolved. + storage.persist(directory / f"recovery-{self.epoch}.json", {"reason": str(exc), "state": "unknown"}) + + def _unresolved(self): + unknown = [] + for key in self.records: + record = self._read_record(self._directory(key)) + if record["result_sha256"] is None or storage.decode(storage.read_bytes( + self._directory(key) / "result.json"))["state"] == "unknown": + unknown.append(key) + return unknown + + def _known_body(self): + status = self.agent.status() + if status["bench"]["state"] != "ready" or status["unresolved_proposals"] or self._unresolved(): + raise wire.Refusal("accepted outcome is unknown; new commands are blocked") + return status["bench"] + + def state(self): + with self.lock: + self._identity_guard() + native = self.agent.status() + unresolved = self._unresolved() + lease = None + if self.lease: + lease = {k: self.lease[k] for k in ("client_id", "lease_id", "epoch")} + lease["expires_in_s"] = max(0, self.lease["expires_at"] - time.monotonic()) + return {"identity": {**self.pinned, "epoch": self.epoch}, "state": "unknown" if unresolved or + native["unresolved_proposals"] or native["bench"]["state"] != "ready" else "ready", + "bench": agent.public_response(native["bench"]), "unresolved_commands": unresolved, "lease": lease} + + def _lease_valid(self, client_id, lease_id, epoch): + return bool(self.lease and epoch == self.epoch == self.lease["epoch"] and + client_id == self.lease["client_id"] and lease_id == self.lease["lease_id"] and + time.monotonic() < self.lease["expires_at"]) + + def acquire(self, value): + duration = contract.validate_lease_request(value) + with self.lock: + self._identity_guard() + if self.closed or value["expected_epoch"] != self.epoch: + raise wire.Refusal("requested boot epoch is no longer current") + if self.lease and time.monotonic() < self.lease["expires_at"] and self.lease["client_id"] != value["client_id"]: + raise wire.Refusal("another client owns the unexpired body lease") + self.lease = {"client_id": value["client_id"], "lease_id": uuid.uuid4().hex, "epoch": self.epoch, + "expires_at": time.monotonic() + duration} + return {k: self.lease[k] for k in ("client_id", "lease_id", "epoch")} | {"duration_s": duration} + + def reconcile(self, command_id, value): + wire.identity(command_id) + contract.validate_reconcile(value) + with self.lock: + self._identity_guard() + record = self.records.get(command_id) + if record: + record = self._read_record(self._directory(command_id)) + request = record["request"] + if wire.command_digest(request) != value["request_sha256"] or any( + request[k] != value[k] for k in ("client_id", "lease_id", "epoch")): + raise wire.Refusal("reconcile identity or digest conflicts with retained command") + if record["result_sha256"]: + return storage.decode(storage.read_bytes(self._directory(command_id) / "result.json")) + return self._reply(request, "unknown", reason="accepted intent has no verified terminal proof") + return {**{k: self.pinned[k] for k in ("protocol", "adapter_id", "body_id", "source_sha256")}, + "command_id": command_id, "request_sha256": value["request_sha256"], "state": "not_seen" if + self._lease_valid(value["client_id"], value["lease_id"], value["epoch"]) else "not_accepted", + "result": None, "reason": "no accepted command in retained journal", "receipt_scope": "retained_command_result", + "recorded_epoch": value["epoch"]} + + def _barrier(self, where, request): + if (self.barrier == where and request["operation"] == "dispatch" and + self.body.status()["diagnostic_enabled"]): + storage.persist(self.root / "barrier.json", {"barrier": where, "command_id": request["command_id"], "pid": os.getpid()}) + os.kill(os.getpid(), signal.SIGSTOP) + + def command(self, request): + wire.validate_command(request) + with self.lock: + self._identity_guard() + if any(request[k] != self.pinned[k] for k in ("protocol", "adapter_id", "body_id", "source_sha256")): + return self._reply(request, "not_accepted", reason="pinned command identity changed") + existing = self.records.get(request["command_id"]) + if existing: + existing = self._read_record(self._directory(request["command_id"])) + if existing["request_sha256"] != wire.command_digest(request): + return self._reply(request, "refused", reason="command identity conflicts with its retained digest") + if existing["result_sha256"]: + return storage.decode(storage.read_bytes(self._directory(request["command_id"]) / "result.json")) + return self._reply(request, "unknown", reason="accepted intent has no verified terminal proof") + if self.closed or not self._lease_valid(request["client_id"], request["lease_id"], request["epoch"]): + return self._reply(request, "not_accepted", reason="body lease expired or was revoked before admission") + try: + self._known_body() + except (wire.Refusal, bench.Refusal) as exc: + return self._reply(request, "not_accepted", reason=str(exc)) + if len(self.records) >= wire.MAX_COMMANDS: + return self._reply(request, "not_accepted", reason="body command journal budget exhausted") + args, operation = request["arguments"], request["operation"] + if operation == "disturb" and not self.body.status()["diagnostic_enabled"]: + return self._reply(request, "not_accepted", reason="diagnostic disturbance is disabled") + if not self._lease_valid(request["client_id"], request["lease_id"], request["epoch"]): + return self._reply(request, "not_accepted", reason="body lease expired during admission validation") + directory = self._directory(request["command_id"]) + directory.mkdir(mode=0o700, exist_ok=True) + storage.directory(directory) + record = {"request": request, "request_sha256": wire.command_digest(request), "result_sha256": None} + storage.persist(directory / "intent.json", record) + self.records[request["command_id"]] = record + result, state, reason = None, "unknown", None + try: + self._barrier("before_dispatch", request) + if operation == "start_job": + result = self.body.start_job(**args) + elif operation == "observe": + result = self.agent.observe() + elif operation == "propose": + result = self.agent.propose(**args) + elif operation == "dispatch": + result = self.agent.dispatch(**args) + else: + result = self.body.disturb(**args) + self._barrier("after_dispatch", request) + state = "unknown" if operation == "dispatch" and result["outcome"] == "unknown" else "completed" + except Exception as exc: # noqa: BLE001 - accepted command may have partial effects. + reason = str(exc) + try: + core = self.agent.status() + if isinstance(exc, (wire.Refusal, bench.Refusal)) and core["bench"]["state"] == "ready" and not core["unresolved_proposals"]: + state = "refused" + except Exception as status_error: # noqa: BLE001 - missing native readback cannot resolve uncertainty. + reason += "; native status unresolved: " + str(status_error) + envelope = self._reply(request, state, agent.public_response(result), reason) + try: + self._terminal(directory, record, envelope) + except OSError: + return self._reply(request, "unknown", reason="accepted effect has no durable transport result") + return envelope + + def image(self, challenge_id): + with self.lock: + self._identity_guard() + with storage.locked(self.root / "agent"): + manifest, _ = self.agent._load() + challenge = self.agent._challenge(manifest, challenge_id) + return storage.read_bytes(Path(challenge["image_path"]), maximum=wire.MAX_MEDIA) + + +class _Server(ThreadingHTTPServer): + daemon_threads = True + def __init__(self, address, service): + super().__init__(address, _Handler) + self.service = service + self.host = f"127.0.0.1:{self.server_address[1]}" + + +class _Handler(BaseHTTPRequestHandler): + def log_message(self, *_): + pass + + def reply(self, code, value, mime="application/json"): + content = value if isinstance(value, bytes) else storage.encoded(value) + self.send_response(code) + self.send_header("Content-Type", mime) + self.send_header("Content-Length", str(len(content))) + self.send_header("Cache-Control", "no-store") + self.send_header("Connection", "close") + self.end_headers() + self.wfile.write(content) + self.close_connection = True + + def _headers(self, pinned=True): + if (self.headers.get_all("Host") != [self.server.host] or self.headers.get_all("Origin") is not None): + raise wire.Refusal("exact loopback Host and absence of browser Origin are required") + if pinned: + for name, key in (("X-Body-Id", "body_id"), ("X-Adapter-Id", "adapter_id"), ("X-Source-Sha256", "source_sha256")): + if self.headers.get_all(name) != [self.server.service.pinned[key]]: + raise wire.Refusal("pinned body request headers are required") + + def do_GET(self): + try: + parsed = urlsplit(self.path) + self._headers(pinned=parsed.path != "/identity") + if parsed.path == "/identity" and not parsed.query: + result = self.server.service.identity() + elif parsed.path == "/state" and not parsed.query: + result = self.server.service.state() + elif parsed.path.startswith("/images/") and not parsed.query: + result = self.server.service.image(parsed.path.removeprefix("/images/")) + self.reply(200, result, "image/png") + return + elif parsed.path.startswith("/commands/"): + query = parse_qs(parsed.query, keep_blank_values=True, strict_parsing=True) + if any(len(value) != 1 for value in query.values()): + raise wire.Refusal("reconcile query has duplicate fields") + value = {key: values[0] for key, values in query.items()} + if "epoch" in value and value["epoch"].isascii() and value["epoch"].isdigit(): + value["epoch"] = int(value["epoch"]) + result = self.server.service.reconcile(parsed.path.removeprefix("/commands/"), value) + else: + raise wire.Refusal("unknown body read route") + self.reply(200, result) + except (wire.Refusal, bench.Refusal, OSError, ValueError, TypeError, KeyError) as exc: + self.reply(409, {"state": "not_accepted", "reason": str(exc)}) + + def do_POST(self): + try: + self._headers() + lengths = self.headers.get_all("Content-Length") + if (lengths is None or len(lengths) != 1 or not lengths[0].isascii() or not lengths[0].isdigit() or + not 1 <= int(lengths[0]) <= storage.MAX_BYTES or self.headers.get_all("Transfer-Encoding") or + self.headers.get_all("Content-Type") != ["application/json"]): + raise wire.Refusal("one bounded exact JSON body is required") + self.connection.settimeout(5) + content = self.rfile.read(int(lengths[0])) + if len(content) != int(lengths[0]): + raise wire.Refusal("body request is truncated") + request = storage.decode(content) + if self.path == "/lease": + result = self.server.service.acquire(request) + elif self.path == "/commands": + result = self.server.service.command(request) + else: + raise wire.Refusal("unknown body mutation route") + self.reply(200, result) + except (wire.Refusal, bench.Refusal, OSError, ValueError, TypeError, KeyError) as exc: + self.reply(409, {"state": "not_accepted", "reason": str(exc)}) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--port", type=int, default=0) + parser.add_argument("--initialize", action="store_true") + parser.add_argument("--diagnostic", action="store_true") + parser.add_argument("--barrier", choices=("before_dispatch", "after_dispatch")) + args = parser.parse_args() + if args.initialize: + initialize(args.root, args.diagnostic) + service = BodyService(args.root, port=args.port, barrier=args.barrier).start() + print(storage.encoded({"origin": service.origin, "identity": service.identity()}).decode(), flush=True) + stopped = threading.Event() + signal.signal(signal.SIGTERM, lambda *_: stopped.set()) + signal.signal(signal.SIGINT, lambda *_: stopped.set()) + try: + stopped.wait() + finally: + service.close() + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/body_protocol.py b/tools/physical-workcell/body_protocol.py new file mode 100644 index 0000000..ea7cef1 --- /dev/null +++ b/tools/physical-workcell/body_protocol.py @@ -0,0 +1,33 @@ +"""Compatibility facade for the strict simulated bench body protocol. + +New adapters use body_contract plus their explicit operation schemas; this +facade retains the existing bench vocabulary and bounds. +""" +import body_contract as contract +from bench_body_contract import MAX_MEDIA, validate_arguments +from body_contract import ( + COMMAND_FIELDS, + ENVELOPE_FIELDS, + MAX_COMMANDS, + PROTOCOL, + Refusal, + command_digest, + decode, + digest, + encoded, + epoch, + fields, + finite, + identity, + sha, +) + +__all__ = ["COMMAND_FIELDS", "ENVELOPE_FIELDS", "MAX_COMMANDS", "MAX_MEDIA", "PROTOCOL", "Refusal", + "command_digest", "decode", "digest", "encoded", "epoch", "fields", "finite", "identity", "sha", + "validate_command"] + + +def validate_command(request): + contract.validate_command(request) + validate_arguments(request["operation"], request["arguments"]) + return request diff --git a/tools/physical-workcell/cases/camera-shift.json b/tools/physical-workcell/cases/camera-shift.json new file mode 100644 index 0000000..fd5a42e --- /dev/null +++ b/tools/physical-workcell/cases/camera-shift.json @@ -0,0 +1,29 @@ +{ + "case_id": "camera-shift", + "max_actions": 8, + "scene": { + "block_xy": [ + -0.145, + 0 + ], + "camera_offset_x_m": 0.05, + "illumination_scale": 1, + "occlusion_fraction": 0 + }, + "version": 1, + "work_order": { + "order_id": "sim3-camera-shift", + "part_id": "block", + "project": { + "namespace": "codex_app", + "project_id": "abe2a667-dedc-4f3f-9863-62f3d80188bd" + }, + "target_zone": { + "x_max": 0.22, + "x_min": 0.14, + "y_max": 0.04, + "y_min": -0.04 + }, + "version": 1 + } +} diff --git a/tools/physical-workcell/cases/low-light.json b/tools/physical-workcell/cases/low-light.json new file mode 100644 index 0000000..09a5c97 --- /dev/null +++ b/tools/physical-workcell/cases/low-light.json @@ -0,0 +1,29 @@ +{ + "case_id": "low-light", + "max_actions": 8, + "scene": { + "block_xy": [ + -0.135, + 0 + ], + "camera_offset_x_m": 0, + "illumination_scale": 0.03, + "occlusion_fraction": 0 + }, + "version": 1, + "work_order": { + "order_id": "sim3-low-light", + "part_id": "block", + "project": { + "namespace": "codex_app", + "project_id": "abe2a667-dedc-4f3f-9863-62f3d80188bd" + }, + "target_zone": { + "x_max": 0.22, + "x_min": 0.14, + "y_max": 0.04, + "y_min": -0.04 + }, + "version": 1 + } +} diff --git a/tools/physical-workcell/cases/occlusion.json b/tools/physical-workcell/cases/occlusion.json new file mode 100644 index 0000000..34bd8cc --- /dev/null +++ b/tools/physical-workcell/cases/occlusion.json @@ -0,0 +1,29 @@ +{ + "case_id": "occlusion", + "max_actions": 8, + "scene": { + "block_xy": [ + -0.11, + 0 + ], + "camera_offset_x_m": 0, + "illumination_scale": 1, + "occlusion_fraction": 0.65 + }, + "version": 1, + "work_order": { + "order_id": "sim3-occlusion", + "part_id": "block", + "project": { + "namespace": "codex_app", + "project_id": "abe2a667-dedc-4f3f-9863-62f3d80188bd" + }, + "target_zone": { + "x_max": 0.22, + "x_min": 0.14, + "y_max": 0.04, + "y_min": -0.04 + }, + "version": 1 + } +} diff --git a/tools/physical-workcell/cases/ordinary-a.json b/tools/physical-workcell/cases/ordinary-a.json new file mode 100644 index 0000000..0e3832d --- /dev/null +++ b/tools/physical-workcell/cases/ordinary-a.json @@ -0,0 +1,29 @@ +{ + "case_id": "ordinary-a", + "max_actions": 8, + "scene": { + "block_xy": [ + -0.17, + 0 + ], + "camera_offset_x_m": 0, + "illumination_scale": 1, + "occlusion_fraction": 0 + }, + "version": 1, + "work_order": { + "order_id": "sim3-ordinary-a", + "part_id": "block", + "project": { + "namespace": "codex_app", + "project_id": "abe2a667-dedc-4f3f-9863-62f3d80188bd" + }, + "target_zone": { + "x_max": 0.16, + "x_min": 0.08, + "y_max": 0.04, + "y_min": -0.04 + }, + "version": 1 + } +} diff --git a/tools/physical-workcell/cases/ordinary-b.json b/tools/physical-workcell/cases/ordinary-b.json new file mode 100644 index 0000000..88e7f36 --- /dev/null +++ b/tools/physical-workcell/cases/ordinary-b.json @@ -0,0 +1,29 @@ +{ + "case_id": "ordinary-b", + "max_actions": 8, + "scene": { + "block_xy": [ + -0.095, + 0.006 + ], + "camera_offset_x_m": 0, + "illumination_scale": 1, + "occlusion_fraction": 0 + }, + "version": 1, + "work_order": { + "order_id": "sim3-ordinary-b", + "part_id": "block", + "project": { + "namespace": "codex_app", + "project_id": "abe2a667-dedc-4f3f-9863-62f3d80188bd" + }, + "target_zone": { + "x_max": 0.22, + "x_min": 0.14, + "y_max": 0.04, + "y_min": -0.04 + }, + "version": 1 + } +} diff --git a/tools/physical-workcell/cases/ordinary-c.json b/tools/physical-workcell/cases/ordinary-c.json new file mode 100644 index 0000000..21f3d1f --- /dev/null +++ b/tools/physical-workcell/cases/ordinary-c.json @@ -0,0 +1,29 @@ +{ + "case_id": "ordinary-c", + "max_actions": 8, + "scene": { + "block_xy": [ + -0.04, + -0.006 + ], + "camera_offset_x_m": 0, + "illumination_scale": 1, + "occlusion_fraction": 0 + }, + "version": 1, + "work_order": { + "order_id": "sim3-ordinary-c", + "part_id": "block", + "project": { + "namespace": "codex_app", + "project_id": "abe2a667-dedc-4f3f-9863-62f3d80188bd" + }, + "target_zone": { + "x_max": 0.26, + "x_min": 0.18, + "y_max": 0.04, + "y_min": -0.04 + }, + "version": 1 + } +} diff --git a/tools/physical-workcell/cases/sim3-suite.json b/tools/physical-workcell/cases/sim3-suite.json new file mode 100644 index 0000000..53cd57d --- /dev/null +++ b/tools/physical-workcell/cases/sim3-suite.json @@ -0,0 +1,35 @@ +{ + "version": 1, + "cases": [ + { + "case_id": "ordinary-a", + "group": "ordinary", + "file": "ordinary-a.json" + }, + { + "case_id": "ordinary-b", + "group": "ordinary", + "file": "ordinary-b.json" + }, + { + "case_id": "ordinary-c", + "group": "ordinary", + "file": "ordinary-c.json" + }, + { + "case_id": "low-light", + "group": "challenge", + "file": "low-light.json" + }, + { + "case_id": "occlusion", + "group": "challenge", + "file": "occlusion.json" + }, + { + "case_id": "camera-shift", + "group": "challenge", + "file": "camera-shift.json" + } + ] +} diff --git a/tools/physical-workcell/codex-action.schema.json b/tools/physical-workcell/codex-action.schema.json new file mode 100644 index 0000000..a242e24 --- /dev/null +++ b/tools/physical-workcell/codex-action.schema.json @@ -0,0 +1,11 @@ +{ + "type": "object", + "properties": { + "action": {"type": "string", "enum": ["move", "measure", "begin_reset", "finish_reset", "stop"]}, + "target_x": {"type": ["number", "null"]}, + "target_y": {"type": ["number", "null"]}, + "intent": {"type": "string"} + }, + "required": ["action", "target_x", "target_y", "intent"], + "additionalProperties": false +} diff --git a/tools/physical-workcell/codex-policy.md b/tools/physical-workcell/codex-policy.md new file mode 100644 index 0000000..8982224 --- /dev/null +++ b/tools/physical-workcell/codex-policy.md @@ -0,0 +1,11 @@ +You are the visual policy for a simulated experiment bench. Choose exactly one next action from the attached current image and task metadata. Return only the requested JSON shape. You have no tools and must not request tools, inspect files, run code, or claim success without controller verification. + +The orange object is a free block. The blue object is an XY pusher; move targets position the pusher, not the block. The green rectangle is the measurement region. Dark shapes are shadows. The camera is orthographic, 640 by 360 pixels, with its world origin at pixel (320,180). Use the supplied calibration: increasing image x is increasing world x, increasing image y is decreasing world y. Infer current object positions from the image yourself. The block footprint is approximately 0.07 by 0.07 meters; the pusher is 0.04 by 0.14 meters. Native contact moves the block; you cannot pick it up or teleport it. + +During prepare, put the block CENTER inside the supplied target zone. Its entire footprint need not fit. Push from its left to move it right, or from its right to move it left. Clear the pusher away from contact before measuring. When the fresh image shows the center in the region and the pusher clear, choose measure. A rejected stale decision is feedback: inspect the new image and choose the necessary correction; do not blindly retry measurement. + +After a verified measurement, choose begin_reset. During resetting, move the block CENTER into the home zone and return the pusher to world (-0.30,0). To push the block left, approach from the right. When crossing from one side of the block to the other, first enter a clear lateral lane, cross horizontally, then return to y=0. Avoid diagonal paths through the block. Clearance depends on both footprints; y around +0.135 meters is a usable bypass lane for a block near y=0. Keep all pusher targets within the supplied bounds. When releasing right-side contact after a leftward push, retreat slightly right before entering the bypass lane so you do not drag the block. + +In resetting, when the current image shows both bodies home, choose finish_reset and let the controller verify it. Never use finish_reset or measure to manufacture success. If the situation cannot be resolved within the task or lacks usable evidence, choose stop and briefly explain why. + +For move, provide finite target_x and target_y in meters. For all other actions, both targets must be null. Intent must be one short sentence describing the next action's purpose. Every call is bounded; use purposeful moves rather than many tiny increments. History describes earlier actions, not current placement. The current attached image is the basis for your next decision. diff --git a/tools/physical-workcell/codex_policy.py b/tools/physical-workcell/codex_policy.py new file mode 100644 index 0000000..27250b1 --- /dev/null +++ b/tools/physical-workcell/codex_policy.py @@ -0,0 +1,202 @@ +"""Bounded image-only Codex CLI policy; it never dispatches bench actions.""" + +import contextlib +import fcntl +import hashlib +import json +import os +import shutil +import signal +import subprocess +import sys +import time +import uuid +from pathlib import Path + +import agent_bench +import order_protocol as protocol +import planner_guard + +BASE = Path(__file__).resolve().parent +TIMEOUT_S = 100 +# Leave room for the guardian's bounded group retirement and descendant proof. +TERMINATE_GRACE_S = 15 +MAX_TRACE_BYTES = 1024 * 1024 +MAX_RESULT_BYTES = 8192 +EXPECTED_CLI = "codex-cli 0.147.0" + + +class PolicyUnavailable(RuntimeError): + """Safe failure code; provider output and credentials stay private.""" + + +class PolicyCancelled(PolicyUnavailable): + pass + + +def preflight(): + executable = shutil.which("codex") + if not executable: + return {"available": False, "reason": "Codex CLI is not installed", "provider": "codex_cli"} + try: + version = subprocess.run([executable, "--version"], capture_output=True, text=True, timeout=5, check=False) + auth = subprocess.run([executable, "login", "status"], capture_output=True, text=True, timeout=5, check=False) + cli_version = version.stdout.strip() + available = version.returncode == auth.returncode == 0 and cli_version == EXPECTED_CLI + return {"available": available, "provider": "codex_cli", "cli_version": cli_version, + "auth_mode": "saved_cli_authentication", "reason": None if available else "CLI version or sign-in is unavailable"} + except (OSError, subprocess.TimeoutExpired): + return {"available": False, "reason": "Codex CLI readiness could not be checked", "provider": "codex_cli"} + + +def validate_action(value): + protocol.fields(value, ("action", "target_x", "target_y", "intent"), "model action") + action = value["action"] + if action not in agent_bench.ACTIONS | {"stop"} or not isinstance(value["intent"], str) or not 1 <= len(value["intent"].strip()) <= 500: + raise PolicyUnavailable("model_action_schema") + if action == "move": + x, y = protocol.finite(value["target_x"]), protocol.finite(value["target_y"]) + if not -.32 <= x <= .18 or not -.15 <= y <= .15: + raise PolicyUnavailable("model_target_outside_bounds") + elif value["target_x"] is not None or value["target_y"] is not None: + raise PolicyUnavailable("nonmotion_targets_must_be_null") + return value + + +def terminate(process): + if process.poll() is not None: + return + with contextlib.suppress(ProcessLookupError): + os.killpg(process.pid, signal.SIGTERM) + try: + process.wait(timeout=TERMINATE_GRACE_S) + except subprocess.TimeoutExpired: + with contextlib.suppress(ProcessLookupError): + os.killpg(process.pid, signal.SIGKILL) + process.wait(timeout=3) + + +def usage_from_events(content): + reported = None + for line in content.decode("utf-8", errors="replace").splitlines(): + try: + event = json.loads(line) + except (ValueError, TypeError): + continue + if not isinstance(event, dict): + continue + item = event.get("item", {}) + if isinstance(item, dict) and item.get("type") == "error": + message = item.get("message", "") + known = ("Code Mode is unavailable because code-mode host is disabled.", + "Skill descriptions were shortened to fit the skills context budget.") + if not isinstance(message, str) or not message.startswith(known): + raise PolicyUnavailable("codex_reported_error") + elif isinstance(item, dict) and item.get("type") not in {None, "agent_message", "reasoning"}: + raise PolicyUnavailable("unexpected_model_tool_use") + if event.get("type") == "turn.completed": + data = event.get("usage", {}) + if isinstance(data, dict) and all(type(data.get(k)) is int and 0 <= data[k] <= 1000000 for k in ("input_tokens", "output_tokens")): + reported = {k: data[k] for k in ("input_tokens", "output_tokens", "cached_input_tokens") if type(data.get(k)) is int} + return reported + + +def decide(challenge, history, call_dir, cancel_event): + root = protocol.directory(call_dir, create=True) + if any(path.name != "reservation.json" for path in root.iterdir()): + raise PolicyUnavailable("model_call_directory_already_used") + packet = challenge["observation"] + image = protocol.read_bytes(Path(challenge["image_path"]), maximum=1024 * 1024) + if protocol.sha(image) != packet["image_sha256"]: + raise PolicyUnavailable("model_image_digest_changed") + readiness = preflight() + if not readiness["available"]: + raise PolicyUnavailable("codex_cli_unavailable") + if cancel_event.is_set(): + raise PolicyCancelled("operator_pause_requested") + data = agent_bench.public_response({"current": {"job_id": packet["job_id"], "phase": packet["phase"], + "generation": packet["generation"], "calibration": packet["calibration"], "task": challenge["task"]}, + "recent_actions": history[-12:]}) + prompt = "Choose one next action from this current attached image and task data.\n" + protocol.encoded(data).decode() + instructions = protocol.read_bytes(BASE / "codex-policy.md") + schema = protocol.read_bytes(BASE / "codex-action.schema.json") + for name, content in (("input.png", image), ("instructions.md", instructions), ("schema.json", schema), ("prompt.txt", prompt.encode())): + agent_bench.bench.body.create_file(root / name, content) + request_sha = hashlib.sha256(prompt.encode() + image + instructions + schema).hexdigest() + flags = ["--ignore-user-config", "--ignore-rules", "--ephemeral", "--skip-git-repo-check", "--sandbox", "read-only", + "--disable", "shell_tool", "--disable", "shell_snapshot", "--disable", "unified_exec", "--disable", "hooks", "--disable", "multi_agent", + "--disable", "apps", "--disable", "image_generation", "--disable", "view_image", "--disable", "code_mode_host", + "-c", "tools.view_image=false", "-c", 'web_search="disabled"', "-c", "project_doc_max_bytes=0", + "-c", "approval_policy=\"never\"", "-c", 'model_reasoning_effort="low"', + "-c", "model_instructions_file=" + json.dumps(str(root / "instructions.md")), + "--cd", str(root), "--image", str(root / "input.png"), "--output-schema", str(root / "schema.json"), + "--output-last-message", str(root / "result.json"), "--json", "-"] + executable = shutil.which("codex") + environment = {k: v for k, v in os.environ.items() if not any(word in k.upper() for word in ("API_KEY", "TOKEN", "SECRET", "PASSWORD"))} + metadata = {"provider": "codex_cli", "cli_version": readiness["cli_version"], "request_sha256": request_sha, + "image_sha256": packet["image_sha256"], "started_at": time.time(), "usage": None} + agent_bench.bench.body.create_file(root / "request.json", protocol.encoded({**metadata, "flags": flags, "tools_enabled": False})) + reservation = protocol.decode(protocol.read_bytes(root / "reservation.json")) if (root / "reservation.json").exists() else {} + owner = reservation.get("planner_owner", uuid.uuid4().hex) + protocol.create_file(root / "planner-intent.json", protocol.encoded({"version": 1, "host_pid": os.getpid(), + "owner": owner, "request_sha256": request_sha})) + started = time.monotonic() + with (root / "events.jsonl").open("xb") as stdout, (root / "stderr.log").open("xb") as stderr: + lock_fd = planner_guard.lock_call(root) + try: + fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + lifetime_read, lifetime_write = os.pipe() + except BaseException: + os.close(lock_fd) + raise + try: + process = subprocess.Popen([sys.executable, str(BASE / "planner_guard.py"), "--root", str(root), + "--executable", executable, "--lifetime-fd", str(lifetime_read), "--lock-fd", str(lock_fd)], + stdin=subprocess.DEVNULL, stdout=stdout, stderr=stderr, env=environment, + start_new_session=True, close_fds=True, pass_fds=(lifetime_read, lock_fd)) + except BaseException as exc: + for fd in (lifetime_read, lifetime_write, lock_fd): + os.close(fd) + if not isinstance(exc, OSError): + raise + raise PolicyUnavailable("codex_cli_launch_failed") from exc + os.close(lifetime_read) + os.close(lock_fd) + metadata["guardian_pid"] = process.pid + try: + while process.poll() is None: + if cancel_event.wait(.15): + raise PolicyCancelled("operator_pause_requested") + if time.monotonic() - started > TIMEOUT_S: + raise PolicyUnavailable("model_call_timeout") + if max((root / "events.jsonl").stat().st_size, (root / "stderr.log").stat().st_size) > MAX_TRACE_BYTES: + raise PolicyUnavailable("model_trace_exceeds_bound") + if cancel_event.is_set(): + raise PolicyCancelled("operator_pause_requested") + if process.returncode != 0: + raise PolicyUnavailable("codex_cli_exit_" + str(process.returncode)) + content = protocol.read_bytes(root / "result.json", maximum=MAX_RESULT_BYTES) + value = validate_action(protocol.decode(content)) + metadata["usage"] = usage_from_events(protocol.read_bytes(root / "events.jsonl", maximum=MAX_TRACE_BYTES)) + except (PolicyUnavailable, OSError, ValueError, TypeError, KeyError) as exc: + terminate(process) + metadata.update(outcome="cancelled" if isinstance(exc, PolicyCancelled) else "unavailable", + reason=str(exc) if isinstance(exc, PolicyUnavailable) else "invalid_or_unavailable_model_result") + if isinstance(exc, PolicyUnavailable): + raise + raise PolicyUnavailable("invalid_or_unavailable_model_result") from exc + finally: + os.close(lifetime_write) + terminate(process) + metadata.update(elapsed_s=time.monotonic() - started, process_exit_observed=process.poll() is not None, + exit_code=process.poll()) + closure = planner_guard.recover(root) + if closure is None or closure.get("group_closed") is not True: + raise PolicyUnavailable("planner_group_cleanup_unproven") + metadata["planner_cleanup"] = closure + startup = protocol.decode(protocol.read_bytes(root / "planner-process.json")) + metadata["pid"] = metadata["planner_pid"] = startup.get("planner_pid") + protocol.persist(root / "outcome.json", metadata) + metadata["outcome"] = "decision" + protocol.persist(root / "outcome.json", metadata) + return value, metadata diff --git a/tools/physical-workcell/controller.py b/tools/physical-workcell/controller.py new file mode 100644 index 0000000..0a7fee0 --- /dev/null +++ b/tools/physical-workcell/controller.py @@ -0,0 +1,503 @@ +"""Independent, private Unix-socket controller for an initialized simulated episode.""" + +import argparse +import contextlib +import importlib.util +import json +import os +import signal +import socket +import stat +import sys +import threading +import time +import uuid +from pathlib import Path + +BASE = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("controller_episode", BASE / "episode_runner.py") +episode = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(episode) +vision, body, Refusal = episode.vision, episode.body, episode.Refusal +MAX_RPC = 8192 +TERMINAL = {"executed", "staged", "cancelled", "refused", "unknown"} +# Do not use TMPDIR: macOS's per-session path (or a long checkout) can exhaust +# sockaddr_un. The OS temporary directory has a stable, short canonical path. +SOCKET_PARENT = Path("/private/tmp" if sys.platform == "darwin" else "/tmp") + + +def operation_path(control, operation_id): + body.op_path(control, operation_id) # Shared exact safe-ID validation, no reservation. + return control / f"goal-{body.sha(operation_id.encode())}.json" + + +def request_path(control, operation_id): + operation_path(control, operation_id) + return control / f"goal-request-{body.sha(operation_id.encode())}.json" + + +def socket_path(control, create=False): + # A shared ancestor must be trusted and sticky; our own namespace must be + # private. Never follow a socket-directory symlink or chmod an existing path. + for parent in (*reversed(SOCKET_PARENT.parents), SOCKET_PARENT): + info = parent.lstat() + if (not stat.S_ISDIR(info.st_mode) or info.st_uid not in {0, os.getuid()} or + (info.st_mode & 0o022 and not info.st_mode & stat.S_ISVTX)): + raise Refusal("Unix socket ancestors must be trusted directories without symlinks") + directory = SOCKET_PARENT / f"fw-c-{os.getuid()}" + if create: + try: + directory.mkdir(mode=0o700) + except FileExistsError: + pass + info = directory.lstat() + if not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid() or info.st_mode & 0o077: + raise Refusal("Unix socket directory must be owned, private and free of symlinks") + path = directory / (body.sha(str(control).encode()) + ".sock") + if len(os.fsencode(path)) >= 104: + raise Refusal("owned Unix socket path exceeds the platform bound") + return path + + +def source_binding(root, control): + record = episode.lifecycle(root) + return {"version": 1, "source_kind": body.SOURCE_KIND, "run_dir": str(root), + "controller_dir": str(control), "episode_id": record["episode_id"], + "case_sha256": record["case_sha256"], "episode_fingerprint": record["fingerprint"], + "controller_sha256": body.sha(body.read_bytes(Path(__file__))), "socket_path": str(socket_path(control))} + + +def receive(connection): + connection.settimeout(2) + content = b"" + while b"\n" not in content: + chunk = connection.recv(min(4096, MAX_RPC + 1 - len(content))) + if not chunk: + raise Refusal("RPC ended before its newline-delimited request") + content += chunk + if len(content) > MAX_RPC: + raise Refusal("RPC request exceeds its bounded size") + line, extra = content.split(b"\n", 1) + if extra: + raise Refusal("one RPC connection accepts exactly one request") + value = body.decoded(line) + if not isinstance(value, dict): + raise Refusal("RPC request must be an object") + return value + + +class Controller: + def __init__(self, root, control, dispatch_delay_s=0, pace_s=0): + self.root, self.control = root, control + self.dispatch_delay_s, self.pace_s = body.number(dispatch_delay_s), body.number(pace_s) + if not 0 <= self.dispatch_delay_s <= 5 or not 0 <= self.pace_s <= .003: + raise Refusal("dispatch delay or native pacing exceeds the diagnostic bound") + if root == control or root in control.parents or control in root.parents: + raise Refusal("controller directory must be separate from the episode directory") + socket_path(control, create=True) + self.binding = source_binding(root, control) + binding_file = control / "controller-binding.json" + if binding_file.exists() or binding_file.is_symlink(): + if body.decoded(body.read_bytes(binding_file)) != self.binding: + raise Refusal("controller source, case or run binding changed") + else: + if any(path.name != "lock" for path in control.iterdir()): + raise Refusal("unreceipted controller files exist; no implicit reset") + body.create_file(binding_file, body.encoded(self.binding)) + self.incarnation = uuid.uuid4().hex + self.mutex = threading.RLock() + self.shutdown = threading.Event() + self.cancel_event = threading.Event() + self.worker = None + self.active_id = None + self.blocked = False + self.current_problem = None + self.goals = {} + for path in sorted(control.glob("goal-*.json")): + if path.name.startswith("goal-request-"): + continue + goal = self.load_goal(path) + if goal["phase"] in {"accepted", "running", "unknown"}: + self.reconcile(goal) + self.goals[goal["operation_id"]] = goal + self.blocked |= goal["phase"] == "unknown" + try: + loaded = body.load(root) + self.blocked |= body.unresolved(root, loaded[0]) or vision.wrapper_unresolved(root) + except (Refusal, OSError, KeyError, TypeError, ValueError): + self.current_problem = "current state or lower evidence is unavailable; historical goals remain retained" + self.incarnation_record = {"version": 1, "current_incarnation": self.incarnation, + "pid": os.getpid(), "started_at": time.time(), "phase": "serving", + "binding": self.binding, "dispatch_delay_s": self.dispatch_delay_s, + "pace_s": self.pace_s} + body.create_file(control / f"incarnation-{self.incarnation}.json", body.encoded(self.incarnation_record)) + body.utils.persist(control / "controller-current.json", self.incarnation_record) + + def validate_current(self): + if (source_binding(self.root, self.control) != self.binding or + body.decoded(body.read_bytes(self.control / "controller-binding.json")) != self.binding): + raise Refusal("controller source, episode or case binding changed") + + def load_goal(self, path): + goal = body.decoded(body.read_bytes(path)) + if (not isinstance(goal, dict) or goal.get("version") != 1 or + goal.get("binding") != self.binding or goal.get("phase") not in TERMINAL | {"accepted", "running"} or + operation_path(self.control, goal.get("operation_id")) != path): + raise Refusal("controller goal identity, phase or binding is invalid") + content = body.read_bytes(request_path(self.control, goal["operation_id"])) + if (body.sha(content) != goal.get("proposal_sha256") or body.decoded(content) != goal.get("proposal") or + goal["proposal"] != vision.schema(goal["proposal"], self.root)): + raise Refusal("controller accepted proposal bytes changed") + return goal + + def persist_goal(self, goal): + body.utils.persist(operation_path(self.control, goal["operation_id"]), goal) + + def terminal_receipt(self, goal): + path = vision.proposal_path(self.root, goal["proposal"]["token"]) + receipt = vision.load_proposal_record(path) + if (receipt["proposal"] != goal["proposal"] or receipt["proposal_sha256"] != goal["proposal_sha256"] or + receipt["phase"] not in {"executed", "staged"}): + raise Refusal("lower receipt is not an exact terminal result for this accepted goal") + # load_proposal_record checks native request, source/case/frame and retained state identity. + native = body.load_op(body.op_path(self.root, "vision-" + body.sha(goal["proposal"]["token"].encode()))) + generation = native.get("after_generation", goal["proposal"]["generation"]) + state_sha = native.get("after_state_sha256", native.get("state_sha256")) + order_bytes = body.read_bytes(self.root / "work-order.json") + if (body.sha(order_bytes) != receipt["binding"]["work_order_sha256"] or + body.sha(body.read_bytes(self.root / "model.xml")) != body.fingerprints()["model_sha256"]): + raise Refusal("historical work-order or model source changed") + order = body.order_schema(body.decoded(order_bytes)) + episode.generation_predicate(self.root, order, generation, state_sha) + return receipt + + def set_terminal(self, goal, receipt): + public = vision.public_receipt(receipt) + stats = receipt.get("contact_statistics", {}) + if stats.get("cancellation_requested"): + if stats.get("stop_verified") is not True: + raise Refusal("cancelled motion lacks verified lower-layer stop evidence") + goal.update(phase="cancelled", cancellation_stage="during_motion", + stop_evidence={"stop_verified": True, "state_retained": True, + "method": "native_velocity_after_held_command_settle", + "retained_generation": receipt["after_generation"], + "retained_state_sha256": receipt["after_state_sha256"], **stats["stop_evidence"]}) + else: + goal["phase"] = receipt["phase"] + if "reason" in goal: + goal["prior_reason"] = goal.pop("reason") + goal.update(receipt=public, finished_at=time.time()) + + def reconcile(self, goal): + try: + receipt = self.terminal_receipt(goal) + self.set_terminal(goal, receipt) + goal["reconciled"] = True + except (Refusal, OSError, KeyError, TypeError, ValueError): + goal.update(phase="unknown", reason="no exact terminal image/native receipt; unfinished work will not replay", + finished_at=time.time(), reconciled=False) + self.persist_goal(goal) + + def health(self): + self.validate_current() + health = ("stopping" if self.shutdown.is_set() else "unknown" if self.current_problem else + "blocked" if self.blocked else "busy" if self.active_id else "ready") + current = {} + if health == "ready": + try: + loaded = body.load(self.root) + manifest = loaded[0] + if body.unresolved(self.root, manifest) or vision.wrapper_unresolved(self.root): + health = "blocked" + elif manifest["phase"] == "completed": + current["current_software_artifact_check"] = body.artifact_check(self.root, manifest["result_sha256"]) + if not current["current_software_artifact_check"]["ok"]: + health = "blocked" + except (Refusal, OSError, KeyError, TypeError, ValueError): + self.current_problem = "current state or lower evidence is unavailable; historical goals remain retained" + health = "unknown" + if self.current_problem: + current["current_health_reason"] = self.current_problem + return {"source_kind": body.SOURCE_KIND, "phase": health, "current_health": health, + "current_incarnation": self.incarnation, "active_operation_id": self.active_id, + "run_dir": str(self.root), "controller_dir": str(self.control), "pid": os.getpid(), **current} + + def public_goal(self, goal): + result = self.health() + result.update(phase=goal["phase"], operation_id=goal["operation_id"], + accepted_incarnation=goal["accepted_incarnation"], cancel_requested=goal["cancel_requested"], + historical_outcome=goal["phase"] if goal["phase"] in TERMINAL else None, + accepted_at=goal["accepted_at"], proposal_sha256=goal["proposal_sha256"], + image_generation=goal["proposal"]["generation"], replayed=False) + for key in ("receipt", "reason", "prior_reason", "finished_at", "stop_evidence", "cancellation_stage", "reconciled"): + if key in goal: + result[key] = goal[key] + return result + + def require_incarnation(self, request): + if request.get("expected_incarnation") != self.incarnation: + raise Refusal("expected controller incarnation is stale or missing; read health before an effect request") + + def submit(self, request): + self.require_incarnation(request) + operation_id = request["operation_id"] + operation_path(self.control, operation_id) + proposal_sha = request["proposal_sha256"] + if not isinstance(proposal_sha, str) or not body.DIGEST.fullmatch(proposal_sha): + raise Refusal("proposal SHA256 must be exact lowercase hexadecimal") + content = body.read_bytes(Path(request["proposal_file"])) + if body.sha(content) != proposal_sha: + raise Refusal("proposal bytes differ from their bound digest") + proposal = vision.schema(body.decoded(content), self.root) + if operation_id in self.goals: + goal = self.goals[operation_id] + if goal["proposal"] != proposal or goal["proposal_sha256"] != proposal_sha: + raise Refusal("operation ID belongs to different accepted proposal bytes") + return self.public_goal(goal) + if self.health()["current_health"] != "ready": + raise Refusal("controller has active, unresolved or stopping work; no new goal admitted") + # One controller ID owns a lower token, even when a goal was cancelled before dispatch. + if any(goal["proposal"]["token"] == proposal["token"] for goal in self.goals.values()): + raise Refusal("proposal token already belongs to another controller operation") + if vision.proposal_path(self.root, proposal["token"]).exists(): + raise Refusal("proposal token has pre-existing lower history; choose a new operation") + retained = request_path(self.control, operation_id) + body.create_file(retained, content) + goal = {"version": 1, "operation_id": operation_id, "proposal": proposal, "proposal_sha256": proposal_sha, + "binding": self.binding, "accepted_incarnation": self.incarnation, "accepted_at": time.time(), + "phase": "accepted", "cancel_requested": False} + body.create_file(operation_path(self.control, operation_id), body.encoded(goal)) + # Persist uses file fsync + directory fsync, covering the request and goal directory + # entries before acceptance is acknowledged. This is not power-loss qualification. + self.persist_goal(goal) + self.goals[operation_id] = goal + self.active_id = operation_id + self.cancel_event.clear() + reply = self.public_goal(goal) + self.worker = threading.Thread(target=self.execute, args=(goal,), name="native-controller-operation", daemon=False) + self.worker.start() + return reply + + def no_dispatch_cancel(self, goal): + goal.update(phase="cancelled", finished_at=time.time(), cancellation_stage="before_dispatch", + stop_evidence={"stop_verified": True, "state_retained": True, "method": "no_dispatch"}) + self.persist_goal(goal) + + def execute(self, goal): + try: + self.cancel_event.wait(self.dispatch_delay_s) + with self.mutex: + if self.cancel_event.is_set(): + self.no_dispatch_cancel(goal) + return + self.validate_current() + goal.update(phase="running", dispatch_intent_at=time.time()) + self.persist_goal(goal) + # The service owns the episode lock. Native work runs here; renderer remains main-thread only. + vision.apply(self.root, str(request_path(self.control, goal["operation_id"])), goal["proposal_sha256"], + motion_control={"cancel_requested": self.cancel_event.is_set, "pace_s": self.pace_s}) + receipt = self.terminal_receipt(goal) + with self.mutex: + self.set_terminal(goal, receipt) + self.persist_goal(goal) + except (Exception, KeyboardInterrupt) as exc: # noqa: BLE001 - durable dispatched uncertainty cannot be replayed + with self.mutex: + lower = vision.proposal_path(self.root, goal["proposal"]["token"]) + native = body.op_path(self.root, "vision-" + body.sha(goal["proposal"]["token"].encode())) + dispatched = lower.exists() or native.exists() + goal.update(phase="unknown" if dispatched or not isinstance(exc, Refusal) else "refused", + reason="lower effect is unresolved" if dispatched else "proposal refused before a lower effect", + finished_at=time.time()) + self.blocked |= goal["phase"] == "unknown" + self.persist_goal(goal) + finally: + with self.mutex: + self.active_id = None + + def cancel(self, request): + self.require_incarnation(request) + goal = self.goals.get(request["operation_id"]) + if goal is None: + raise Refusal("controller operation ID is unknown") + if goal["phase"] not in TERMINAL: + goal["cancel_requested"] = True + goal["cancel_requested_at"] = time.time() + self.persist_goal(goal) + self.cancel_event.set() + return self.public_goal(goal) + + def handle(self, request): + with self.mutex: + self.validate_current() + command = request.get("command") + fields = {"health": ("command",), "observe": ("command",), + "status": ("command", "operation_id"), + "cancel": ("command", "operation_id", "expected_incarnation"), + "submit": ("command", "operation_id", "proposal_file", "proposal_sha256", "expected_incarnation")} + if command not in fields: + raise Refusal("unknown controller command") + body.fields(request, fields[command], "controller RPC") + if command == "health": + return self.health() + if command == "observe": + reply = self.health() + if reply["current_health"] != "ready": + return reply + reply.update(phase="ready", observation=vision.view(self.root)) + return reply + if command == "submit": + return self.submit(request) + operation_path(self.control, request["operation_id"]) + if command == "cancel": + return self.cancel(request) + goal = self.goals.get(request["operation_id"]) + if goal is None: + raise Refusal("controller operation ID is unknown") + return self.public_goal(goal) + + def request_shutdown(self, *_): + self.shutdown.set() + with self.mutex: + if self.active_id: + goal = self.goals[self.active_id] + if goal["phase"] not in TERMINAL: + goal["cancel_requested"] = True + goal["cancel_requested_at"] = time.time() + self.persist_goal(goal) + self.cancel_event.set() + + def abort_unfinished_worker(self): + # Do not unwind either ownership scope while a live worker can still write. + # Existing accepted/running intent is already durable if this best-effort update fails. + acquired = self.mutex.acquire(timeout=.1) + try: + if acquired: + with contextlib.suppress(OSError, Refusal): + if self.active_id: + goal = self.goals[self.active_id] + if goal["phase"] not in TERMINAL: + goal.update(phase="unknown", reason="controller shutdown deadline exceeded; result is unresolved", + finished_at=time.time()) + self.persist_goal(goal) + self.incarnation_record.update(phase="unknown", shutdown_timeout_at=time.time(), + reason="native worker exceeded bounded shutdown; process abort") + body.utils.persist(self.control / "controller-current.json", self.incarnation_record) + body.utils.persist(self.control / f"incarnation-{self.incarnation}.json", self.incarnation_record) + finally: + if acquired: + self.mutex.release() + # Whole-process exit ends the worker before the kernel releases the episode/control + # locks. No stopped claim or socket cleanup is made; restart reconciles exact receipts. + os._exit(2) + + def serve(self): + path = socket_path(self.control) + if path.exists() or path.is_symlink(): + info = path.lstat() + if not stat.S_ISSOCK(info.st_mode) or info.st_uid != os.getuid(): + raise Refusal("owned socket path is not an owned Unix socket") + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as probe: + probe.settimeout(.2) + try: + probe.connect(str(path)) + except (ConnectionRefusedError, FileNotFoundError): + path.unlink() + else: + raise Refusal("another live controller occupies the owned socket path") + prior = {sig: signal.signal(sig, self.request_shutdown) for sig in (signal.SIGTERM, signal.SIGINT)} + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as server: + server.bind(str(path)) + os.chmod(path, 0o600) + server.listen(8) + server.settimeout(.1) + try: + while not self.shutdown.is_set(): + try: + connection, _ = server.accept() + except TimeoutError: + continue + with connection: + try: + reply = self.handle(receive(connection)) + except (Refusal, OSError, KeyError, ValueError, TypeError) as exc: + reply = {"source_kind": body.SOURCE_KIND, "phase": "refused", + "reason": str(exc) if isinstance(exc, Refusal) else + "controller request or bound evidence is invalid or unavailable", + "current_incarnation": self.incarnation} + with contextlib.suppress(OSError): + connection.sendall(body.encoded(reply).replace(b"\n", b" ") + b"\n") + finally: + self.request_shutdown() + if self.worker: + self.worker.join(15) + if self.worker.is_alive(): + self.abort_unfinished_worker() + path.unlink(missing_ok=True) + self.incarnation_record.update(phase="stopped", stopped_at=time.time()) + body.utils.persist(self.control / "controller-current.json", self.incarnation_record) + body.utils.persist(self.control / f"incarnation-{self.incarnation}.json", self.incarnation_record) + for sig, handler in prior.items(): + signal.signal(sig, handler) + + +def client(control, request): + binding = body.decoded(body.read_bytes(control / "controller-binding.json")) + path = socket_path(control) + if binding.get("controller_dir") != str(control) or binding.get("socket_path") != str(path): + raise Refusal("client controller/socket binding is invalid") + info = path.lstat() + if not stat.S_ISSOCK(info.st_mode) or info.st_uid != os.getuid() or info.st_mode & 0o077: + raise Refusal("controller socket must be owned and private") + content = json.dumps(request, sort_keys=True, allow_nan=False).encode() + b"\n" + if len(content) > MAX_RPC: + raise Refusal("client request exceeds its bounded size") + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as connection: + connection.settimeout(5) + connection.connect(str(path)) + connection.sendall(content) + response = b"" + while b"\n" not in response: + chunk = connection.recv(4096) + if not chunk or len(response) + len(chunk) > body.MAX_FILE: + raise Refusal("controller reply missing or outside its bounded size") + response += chunk + return body.decoded(response.split(b"\n", 1)[0]) + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest="command", required=True) + for command in ("serve", "health", "observe", "submit", "status", "cancel"): + child = commands.add_parser(command) + child.add_argument("--controller-dir", required=True) + if command == "serve": + child.add_argument("--run-dir", required=True) + child.add_argument("--dispatch-delay-s", type=float, default=0) + child.add_argument("--pace-s", type=float, default=0) + if command in {"submit", "status", "cancel"}: + child.add_argument("--operation-id", required=True) + if command in {"submit", "cancel"}: + child.add_argument("--expected-incarnation", required=True) + if command == "submit": + child.add_argument("--proposal-file", required=True) + child.add_argument("--proposal-sha256", required=True) + args = parser.parse_args(argv) + try: + control = body.directory(args.controller_dir, create=args.command == "serve") + if args.command == "serve": + root = body.directory(args.run_dir) + with body.utils.locked(control), body.utils.locked(root): + Controller(root, control, args.dispatch_delay_s, args.pace_s).serve() + return 0 + request = {key: value for key, value in vars(args).items() if key != "controller_dir"} + result = client(control, request) + print(json.dumps(result, sort_keys=True, indent=2, allow_nan=False)) + return 2 if result.get("phase") in {"refused", "unknown", "blocked"} else 0 + except (Refusal, OSError, ValueError, TypeError, KeyError, KeyboardInterrupt) as exc: + reason = str(exc) if isinstance(exc, Refusal) else "controller or its retained binding is unavailable" + print(json.dumps({"source_kind": body.SOURCE_KIND, "phase": "refused", "reason": reason})) + return 2 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/physical-workcell/crash_body.py b/tools/physical-workcell/crash_body.py new file mode 100644 index 0000000..8fca6d9 --- /dev/null +++ b/tools/physical-workcell/crash_body.py @@ -0,0 +1,115 @@ +"""Owned diagnostic body launcher for process-crash qualification, never completion. + +after_native stops once after a move's native state save and before its Bench +receipt commit. The ordinary body server is used for restart and reconciliation. +This exposes a simulation persistence gap, not a physical hardware stop guarantee. +""" +import argparse +import copy +import os +import signal +import threading +from pathlib import Path + +import bench +import body_network +import body_protocol as wire +import order_protocol as storage + +GATES = {"after_native", "before_dispatch", "after_dispatch"} + + +class CrashBody(body_network.BodyService): + def __init__(self, root, *, port=0, gate="after_native"): + if gate not in GATES: + raise wire.Refusal("unsupported diagnostic crash gate") + root = storage.directory(root) + if not bench.Bench(root / "bench").status()["diagnostic_enabled"]: + raise wire.Refusal("crash gates require a diagnostic body") + if (root / "barrier.json").exists(): + raise wire.Refusal("a crash gate requires an unused retained barrier identity") + self.gate, self.fired = gate, False + self.active_request = None + super().__init__(root, port=port, barrier=gate if gate != "after_native" else None) + + def _native_gate(self, native_bench, manifest, operation_id, request, receipt): + active = self.active_request + if self.fired or not active or active["operation"] != "dispatch" or receipt.get("kind") != "move": + return + if native_bench.root != self.root / "bench": + raise wire.Refusal("diagnostic move belongs to a foreign bench") + accepted = self._read_record(self._directory(active["command_id"])) + if accepted["request"] != active or accepted["result_sha256"] is not None: + raise wire.Refusal("diagnostic gate is not bound to an accepted unresolved command") + agent_manifest = storage.decode(storage.read_bytes(self.root / "agent" / "agent.json")) + proposal_id = active["arguments"]["proposal_id"] + proposal = self.agent._read(agent_manifest, "proposals", proposal_id) + if (proposal["action"] != "move" or proposal["operation_id"] != operation_id or + proposal["observation_sha256"] != storage.sha(storage.encoded(request["observation"])) or + (proposal["target_x"], proposal["target_y"]) != (request["target_x"], request["target_y"])): + raise wire.Refusal("native crash gate does not match its validated proposal") + retained = storage.decode(storage.read_bytes(native_bench.root / "bench.json")) + busy = {"operation_id": operation_id, "kind": "move", "generation": retained["generation"]} + state_sha = bench.body.sha(bench.body.read_bytes(bench.body.state_path(native_bench.root, manifest["generation"]))) + if (retained["busy"] != busy or retained["diagnostic_enabled"] is not True or + manifest["generation"] != retained["generation"] + 1 or state_sha != manifest["state_sha256"] or + manifest["bench_id"] != self.pinned["body_id"] or receipt["operation_id"] != operation_id or + receipt["generation"] != manifest["generation"] or receipt["state_sha256"] != state_sha or + receipt["native_time_s"] <= retained["native_time_s"] or receipt["native_steps"] <= 0): + raise wire.Refusal("native crash evidence is not the exact advanced unresolved move") + marker = {"version": 1, "barrier": "after_native", "pid": os.getpid(), + "command_id": active["command_id"], "request_sha256": wire.command_digest(active), + "proposal_id": proposal_id, "operation_id": operation_id, "bench_id": manifest["bench_id"], + "manifest_generation": retained["generation"], "native_generation": manifest["generation"], + "native_state_sha256": state_sha, "native_time_s": receipt["native_time_s"], + "native_steps": receipt["native_steps"], + "pusher_block_contact_samples": receipt["pusher_block_contact_samples"], + "diagnostic_source_sha256": storage.sha(storage.read_bytes(Path(__file__)))} + self.fired = True + storage.persist(self.root / "barrier.json", marker) + os.kill(os.getpid(), signal.SIGSTOP) + + def command(self, request): + wire.validate_command(request) + if self.gate != "after_native": + return super().command(request) + with self.lock: + self.active_request = copy.deepcopy(request) + original = bench.Bench._finish + owner = self + + def finish(native_bench, manifest, operation_id, operation_request, receipt): + owner._native_gate(native_bench, manifest, operation_id, operation_request, receipt) + return original(native_bench, manifest, operation_id, operation_request, receipt) + + bench.Bench._finish = finish + try: + return super().command(request) + finally: + bench.Bench._finish = original + self.active_request = None + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--port", type=int, default=0) + parser.add_argument("--initialize", action="store_true") + parser.add_argument("--diagnostic", action="store_true") + parser.add_argument("--gate", choices=sorted(GATES), default="after_native") + args = parser.parse_args(argv) + if args.initialize: + body_network.initialize(args.root, diagnostic_enabled=args.diagnostic) + service = CrashBody(args.root, port=args.port, gate=args.gate).start() + print(storage.encoded({"origin": service.origin, "identity": service.identity()}).decode(), flush=True) + stopped = threading.Event() + signal.signal(signal.SIGTERM, lambda *_: stopped.set()) + signal.signal(signal.SIGINT, lambda *_: stopped.set()) + try: + stopped.wait() + finally: + service.close() + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/crash_operator.py b/tools/physical-workcell/crash_operator.py new file mode 100644 index 0000000..07ea1fd --- /dev/null +++ b/tools/physical-workcell/crash_operator.py @@ -0,0 +1,106 @@ +"""Diagnostic-only host barriers; external trial owner sends SIGKILL.""" +import argparse +import os +import signal +import threading +import time +from pathlib import Path + +import body_client +import network_bench_operator as app +import order_protocol as protocol + + +def publication_gate(root, pid, path, content): + if path.parent != root / "events": + return None + event = protocol.decode(content) + if event.get("kind") != "rpc_applied" or not event.get("outcome", "").startswith("dispatch: "): + return None + token = event["outcome"].split(": ", 1)[1] + host = body_client.read_journal(root / "operator.json") + client = body_client.read_journal(root / "client/client.json") + stage = host["stage"] + if not stage or stage["operation"] != "dispatch" or stage["command_id"] != token or client["commands"][token]["state"] != "completed": + raise protocol.Refusal("publication gate requires the original completed dispatch and retained host stage") + if stage["context"]["action"]["action"] != "move": + return None + digest = client["commands"][token]["response_sha256"] + reply = protocol.read_bytes(root / "client/responses" / (token + "-" + digest + ".json")) + envelope = protocol.decode(reply) + if protocol.sha(reply) != digest or envelope["command_id"] != token or envelope["request_sha256"] != client["commands"][token]["request_sha256"]: + raise protocol.Refusal("publication gate reply differs from exact client digest") + if envelope["result"]["outcome"] != "executed": + return None + if event["seq"] != host["sequence"] + 1: + raise protocol.Refusal("publication gate is not immediately ahead of the retained manifest") + return {"version": 1, "barrier": "rpc_applied_event_before_manifest", "pid": pid, + "command_id": token, "request_sha256": client["commands"][token]["request_sha256"], + "event_sequence": event["seq"], "event_sha256": protocol.sha(content), + "manifest_sequence": host["sequence"], "manifest_sha256": protocol.sha(protocol.read_bytes(root / "operator.json")), + "diagnostic_sha256": protocol.sha(protocol.read_bytes(Path(__file__).resolve())), "time": time.time()} + + +def install_publication(root): + original = protocol.create_file + used = False + + def create(path, content): + nonlocal used + original(path, content) + if not used: + marker = publication_gate(root, os.getpid(), Path(path), content) + if marker: + used = True + original(root / "barrier.json", protocol.encoded(marker)) + os.kill(os.getpid(), signal.SIGSTOP) + protocol.create_file = create + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--port", type=int, required=True) + parser.add_argument("--gate", choices=("publication", "planning"), required=True) + args = parser.parse_args() + root = protocol.directory(args.root) + if (root / "barrier.json").exists(): + raise protocol.Refusal("diagnostic barrier requires an unused operator root") + if args.gate == "publication": + install_publication(root) + else: + install_planning(root) + from codex_policy import decide + app.serve(root, args.port, decide) + + +def install_planning(root): + def watch(): + until = time.monotonic() + 1200 + while time.monotonic() < until: + for path in sorted((root / "calls").glob("*/planner-process.json")): + live = body_client.read_journal(path) + if live.get("host_pid") != os.getpid() or live.get("process_exit_observed") or not live.get("planner_pid"): + continue + if (path.parent / "planner-closure.json").exists(): + continue + trace = path.parent / "events.jsonl" + if not trace.exists() or b'"thread.started"' not in protocol.read_bytes(trace, maximum=16 * 1024 * 1024): + continue + try: + os.kill(live["guardian_pid"], 0) + os.kill(live["planner_pid"], 0) + except ProcessLookupError: + continue + marker = {"version": 1, "barrier": "actual_codex_planning_with_live_guardian", "pid": os.getpid(), + "command_id": None, "request_sha256": live["request_sha256"], + "call_directory": path.parent.name, "planner": live, "diagnostic_sha256": protocol.sha(protocol.read_bytes(Path(__file__).resolve())), "time": time.time()} + protocol.create_file(root / "barrier.json", protocol.encoded(marker)) + os.kill(os.getpid(), signal.SIGSTOP) + return + time.sleep(.025) + threading.Thread(target=watch, daemon=True, name="diagnostic-planner-barrier").start() + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/episode_cases.py b/tools/physical-workcell/episode_cases.py new file mode 100644 index 0000000..183cb0b --- /dev/null +++ b/tools/physical-workcell/episode_cases.py @@ -0,0 +1,117 @@ +"""Strict, bounded case inputs; no qualification-suite discovery or evaluation.""" + +import hashlib +import json +import math +import os +import re +import stat +from pathlib import Path + +MAX_CASE_BYTES = 64 * 1024 +ID = re.compile(r"[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}\Z") +SHA = re.compile(r"[0-9a-f]{64}\Z") + + +class CaseError(ValueError): + pass + + +def fields(value, keys, label): + if not isinstance(value, dict) or set(value) != set(keys): + raise CaseError(f"{label} has invalid fields") + + +def finite(value): + if type(value) not in (int, float) or not math.isfinite(value): + raise CaseError("case coordinates/settings must be finite numbers") + return float(value) + + +def validate(case): + fields(case, ("version", "case_id", "scene", "max_actions", "work_order"), "episode case") + if type(case["version"]) is not int or case["version"] != 1: + raise CaseError("case must be version 1") + if not isinstance(case["case_id"], str) or not ID.fullmatch(case["case_id"]): + raise CaseError("case ID must be an exact bounded identifier") + if type(case["max_actions"]) is not int or not 1 <= case["max_actions"] <= 8: + raise CaseError("case action budget must be 1-8 admitted actions") + scene = case["scene"] + fields(scene, ("block_xy", "illumination_scale", "occlusion_fraction", "camera_offset_x_m"), "case scene") + if not isinstance(scene["block_xy"], list) or len(scene["block_xy"]) != 2: + raise CaseError("initial block position must contain two coordinates") + x, y = (finite(value) for value in scene["block_xy"]) + if not -.24 <= x <= .2 or not -.10 <= y <= .10: + raise CaseError("initial block must be within the bounded tabletop") + if not 0 <= finite(scene["illumination_scale"]) <= 2: + raise CaseError("illumination scale must be 0-2") + if not 0 <= finite(scene["occlusion_fraction"]) <= 1: + raise CaseError("occlusion fraction must be 0-1") + if not -.10 <= finite(scene["camera_offset_x_m"]) <= .10: + raise CaseError("camera offset exceeds the declared scene envelope") + order = case["work_order"] + fields(order, ("version", "order_id", "project", "part_id", "target_zone"), "case work order") + if type(order["version"]) is not int or order["version"] != 1 or order["part_id"] != "block": + raise CaseError("case must contain a version 1 block work order") + fields(order["project"], ("namespace", "project_id"), "case project") + for value in (order["order_id"], *order["project"].values()): + if not isinstance(value, str) or not ID.fullmatch(value): + raise CaseError("case work-order identities are invalid") + zone = order["target_zone"] + fields(zone, ("x_min", "x_max", "y_min", "y_max"), "case target zone") + for value in zone.values(): + finite(value) + if not (-.35 <= zone["x_min"] < zone["x_max"] <= .35 and -.2 <= zone["y_min"] < zone["y_max"] <= .2): + raise CaseError("case target zone must be a bounded nonempty rectangle") + return case + + +def read_bytes(path): + try: + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK) + with os.fdopen(fd, "rb") as source: + before = os.fstat(source.fileno()) + if not stat.S_ISREG(before.st_mode) or before.st_size > MAX_CASE_BYTES: + raise CaseError("case input is not a bounded regular file") + data = source.read(MAX_CASE_BYTES + 1) + after = os.fstat(source.fileno()) + if len(data) > MAX_CASE_BYTES or (before.st_size, before.st_mtime_ns, before.st_ctime_ns) != ( + after.st_size, after.st_mtime_ns, after.st_ctime_ns + ): + raise CaseError("case changed during read or exceeds its size limit") + return data + except OSError as exc: + raise CaseError("case source is missing, symlinked or unreadable") from exc + + +def decode(data): + def unique(pairs): + output = {} + for key, value in pairs: + if key in output: + raise CaseError("duplicate case JSON key") + output[key] = value + return output + + def nonfinite(value): + raise CaseError("case JSON contains nonfinite values") + + try: + return validate(json.loads(data, object_pairs_hook=unique, parse_constant=nonfinite)) + except (UnicodeError, json.JSONDecodeError) as exc: + raise CaseError("case source is not UTF-8 JSON") from exc + + +def load(path, expected_sha256): + if not isinstance(expected_sha256, str) or not SHA.fullmatch(expected_sha256): + raise CaseError("case SHA256 must be exact lowercase hexadecimal") + path = Path(os.path.abspath(os.fspath(path))) + try: + if path != path.resolve(strict=True) or path.is_symlink(): + raise CaseError("case source must have a canonical path without symlinks") + except OSError as exc: + raise CaseError("case source is unavailable") from exc + content = read_bytes(path) + if hashlib.sha256(content).hexdigest() != expected_sha256: + raise CaseError("case source does not match its bound digest") + return decode(content), path, content diff --git a/tools/physical-workcell/episode_runner.py b/tools/physical-workcell/episode_runner.py new file mode 100644 index 0000000..72acd50 --- /dev/null +++ b/tools/physical-workcell/episode_runner.py @@ -0,0 +1,398 @@ +"""SIM3 participant lifecycle, deterministic RGB comparator and private evaluator. + +The evaluator is a separate parent-only role; its native evidence is never planner feedback. +""" + +import argparse +import importlib.util +import json +import os +import sys +import time +import uuid +from pathlib import Path + +import numpy as np + +BASE = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("episode_vision", BASE / "image_guided.py") +vision = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(vision) +body = vision.body +Refusal = body.Refusal +OUTCOMES = {"completed", "unresolved", "failed"} + + +def fingerprint(): + return {"runner_sha256": body.sha(body.read_bytes(Path(__file__))), "vision": vision.fingerprint()} + + +def lifecycle(root): + record = body.decoded(body.read_bytes(root / "episode.json")) + if (not isinstance(record, dict) or record.get("version") != 1 or + record.get("run_dir") != str(root) or record.get("fingerprint") != fingerprint()): + raise Refusal("episode source or exact run binding changed") + if (record.get("phase") not in {"initializing", "active", "setup_failed", "finished"} or + record.get("participant") not in {"fresh_host", "deterministic_rgb"} or + body.number(record.get("started_time_unix")) <= 0 or + not isinstance(record.get("case_sha256"), str) or not body.DIGEST.fullmatch(record["case_sha256"])): + raise Refusal("episode lifecycle record is malformed") + source = body.read_bytes(Path(record["case_path"])) + if body.sha(source) != record.get("case_sha256"): + raise Refusal("episode case changed; implicit reset is forbidden") + return record + + +def public_history(root): + """Declared receipt history only: no native-state scoring or current checker call.""" + result = [] + for path in sorted(root.glob("vision-op-*.json")): + record = body.decoded(body.read_bytes(path)) + proposal = record.get("proposal", {}) + result.append({"token": proposal.get("token"), "decision": proposal.get("decision"), + "generation": proposal.get("generation"), "phase": record.get("phase")}) + return result + + +def public_episode(root, record): + result = {key: record[key] for key in ("episode_id", "participant", "phase", "case_sha256", + "started_time_unix")} + result.update(source_kind=body.SOURCE_KIND, run_dir=str(root)) + if "finish" in record: + result["declaration"] = record["finish"] + result["history"] = public_history(root) + return result + + +def initialize(root, case_file, case_sha256, participant="fresh_host"): + if participant not in {"fresh_host", "deterministic_rgb"}: + raise Refusal("participant must be fresh_host or deterministic_rgb") + if not isinstance(case_sha256, str) or not body.DIGEST.fullmatch(case_sha256): + raise Refusal("case SHA256 must be exact lowercase hexadecimal") + source = body.read_bytes(Path(case_file)) + if body.sha(source) != case_sha256: + raise Refusal("case bytes do not match the bound digest") + case_path = str(Path(os.path.abspath(case_file))) + episode_path = root / "episode.json" + if episode_path.exists() or episode_path.is_symlink(): + record = lifecycle(root) + if (record["case_path"], record["case_sha256"], record["participant"]) != ( + case_path, case_sha256, participant): + raise Refusal("run already belongs to another case or participant") + if record["phase"] != "active": + return public_episode(root, record) + else: + if any(path.name != "lock" for path in root.iterdir()): + raise Refusal("unreceipted episode files exist; no implicit reset") + record = {"version": 1, "episode_id": uuid.uuid4().hex, "run_dir": str(root), + "case_path": case_path, "case_sha256": case_sha256, "participant": participant, + "started_time_unix": time.time(), "phase": "initializing", "fingerprint": fingerprint()} + body.create_file(episode_path, body.encoded(record)) + try: + body.initialize_episode(root, case_path, case_sha256) + except (Exception, KeyboardInterrupt): # noqa: BLE001 - any partial setup stays counted + record.update(phase="setup_failed", setup_failure="bound episode initialization failed") + body.utils.persist(episode_path, record) + return public_episode(root, record) + record["phase"] = "active" + body.utils.persist(episode_path, record) + # Native observation is deliberately discarded. Only task intent and opaque identity leave init. + manifest, order, _, _ = body.load(root) + result = public_episode(root, record) + result.update(generation=manifest["generation"], state_sha256=manifest["state_sha256"], + task_intent={key: order[key] for key in ("order_id", "project", "part_id", "target_zone")}, + calibration_valid=body.case_for(root)["scene"]["camera_offset_x_m"] == 0) + return result + + +def finish(root, outcome, explanation_file, explanation_sha256, interventions=None): + if outcome not in OUTCOMES or (interventions is not None and (type(interventions) is not int or interventions < 0)): + raise Refusal("outcome or declared intervention count is invalid") + if not isinstance(explanation_sha256, str) or not body.DIGEST.fullmatch(explanation_sha256): + raise Refusal("explanation SHA256 must be exact lowercase hexadecimal") + content = body.read_bytes(Path(explanation_file)) + if body.sha(content) != explanation_sha256: + raise Refusal("explanation bytes do not match the bound digest") + try: + explanation = content.decode("utf-8") + except UnicodeError as exc: + raise Refusal("explanation must be UTF-8 text") from exc + if not explanation.strip(): + raise Refusal("participant explanation must be nonempty") + record = lifecycle(root) + declaration = {"outcome": outcome, "explanation_path": str(Path(os.path.abspath(explanation_file))), + "explanation_sha256": explanation_sha256, "explanation": explanation, + "interventions_declared": interventions} + if "finish" in record: + retained = {key: value for key, value in record["finish"].items() if key != "finished_time_unix"} + if retained != declaration: + raise Refusal("episode already has a different finish declaration") + if body.read_bytes(root / "participant-explanation.txt") != content: + raise Refusal("retained participant explanation changed") + return public_episode(root, record) + body.create_file(root / "participant-explanation.txt", content) + declaration["finished_time_unix"] = time.time() + record.update(phase="finished", finish=declaration) + body.utils.persist(root / "episode.json", record) + return public_episode(root, record) + + +def doctor(): + checks = {"native_runtime_pinned": body.mujoco.__version__ == body.PINNED_VERSION, + "numpy_available": True, "renderer": False, "footwork_checker": False} + try: + model = body.mujoco.MjModel.from_xml_path(str(body.SCENE)) + data = body.mujoco.MjData(model) + body.mujoco.mj_forward(model, data) + with body.mujoco.Renderer(model, height=360, width=640) as renderer: + renderer.update_scene(data, camera="overview") + checks["renderer"] = renderer.render().shape == (360, 640, 3) + except Exception: # noqa: BLE001 - readiness reports unavailable dependencies without native details + checks["renderer"] = False + try: + rc, _, _ = body.utils.run_process([str(body.FOOTWORK), "check-artifact", "--help"], 20) + checks["footwork_checker"] = rc == 0 + except Exception: # noqa: BLE001 + checks["footwork_checker"] = False + return {"source_kind": body.SOURCE_KIND, "phase": "ready" if all(checks.values()) else "unavailable", + "checks": checks, "mujoco_version": body.mujoco.__version__, + "scope": "dependency readiness only; no episode, persisted state, action or camera acquisition"} + + +def baseline(root, case_file, case_sha256): + initial = initialize(root, case_file, case_sha256, "deterministic_rgb") + record = lifecycle(root) + if "finish" in record: + return initial + outcome, explanation = "unresolved", "RGB feedback action budget exhausted." + if initial["phase"] == "setup_failed": + outcome, explanation = "failed", "Bound episode setup failed." + else: + # Budget is public controller policy, not private case geometry or native pose feedback. + for index in range(8): + try: + packet = vision.view(root) + if packet.get("calibration", {}).get("valid") is not True: + explanation = "Acquired RGB image has invalid calibration; no motion admitted." + break + perception = packet.get("perception", {}) + if not isinstance(perception, dict) or perception.get("state") != "recognized": + explanation = "Acquired RGB image has unavailable or ambiguous part recognition." + break + block_box = perception["block_box"] + block = [body.number(value) for value in perception["block_center_xy"]] + pusher = [body.number(value) for value in perception["pusher_center_xy"]] + zone, margin = packet["task_intent"]["target_zone"], vision.SETTINGS["completion_margin_m"] + complete = (zone["x_min"] + margin <= block[0] <= zone["x_max"] - margin and + zone["y_min"] + margin <= block[1] <= zone["y_max"] - margin) + proposal = {"version": 1, "run_dir": str(root), "generation": packet["generation"], + "image_sha256": packet["image_sha256"], "token": f"rgb-{index:03d}", + "block_box": block_box, "decision": "complete" if complete else "move"} + if not complete: + remaining = packet.get("action_budget", {}).get("remaining") + if type(remaining) is not int or index == 7 or remaining <= 1: + break + # Aim toward the region center using current RGB displacement. The initial + # gap is also RGB-derived; no prerecorded target sequence is used. + displacement = (zone["x_min"] + zone["x_max"]) / 2 - block[0] + gap = max(0.0, block[0] - pusher[0] - .055) + forward = min(vision.SETTINGS["max_forward_m"] - .005, displacement + gap) + x = min(body.CONTROLLER["x_max"] - .005, pusher[0] + forward) + if (x - pusher[0] < vision.SETTINGS["min_forward_m"] or + abs(block[1]) > max(abs(zone["y_min"]), abs(zone["y_max"])) or displacement <= 0): + explanation = "RGB placement requires a motion outside the bounded forward controller." + break + proposal["target"] = {"x": x, "y": 0.0} + content = body.encoded(proposal) + path = root / f"baseline-proposal-{index:03d}.json" + body.create_file(path, content) + result = vision.apply(root, str(path), body.sha(content)) + if result["phase"] == "staged": + outcome, explanation = "completed", "Fresh RGB completion proposal received a staged software receipt." + break + if result["phase"] != "executed": + explanation = "RGB proposal has an unresolved effect receipt." + break + except (Refusal, OSError, ValueError, KeyError, TypeError): + explanation = "RGB view or bounded proposal was refused; retained evidence requires review." + break + except (Exception, KeyboardInterrupt): # noqa: BLE001 - retain unexpected driver failures as attempted episodes + outcome, explanation = "failed", "RGB episode driver failed before supported completion." + record = lifecycle(root) + record["driver_failure"] = True + body.utils.persist(root / "episode.json", record) + break + content = (explanation + "\n").encode() + path = root / "baseline-explanation.txt" + body.create_file(path, content) + return finish(root, outcome, str(path), body.sha(content), interventions=0) + + +def generation_predicate(root, order, generation, expected_sha): + """Restore a verified historical integration vector in memory, never in the manifest.""" + if type(generation) is not int or not 0 <= generation <= 999999: + raise Refusal("historical generation is invalid") + content = body.read_bytes(body.state_path(root, generation)) + if body.sha(content) != expected_sha: + raise Refusal("historical verdict state digest changed") + state = body.decoded(content) + body.fields(state, ("source_kind", "state_spec", "values"), "historical integration state") + model = body.model_for(root, order) + spec = body.mujoco.mjtState.mjSTATE_INTEGRATION + if (state["source_kind"] != body.SOURCE_KIND or state["state_spec"] != int(spec) or + not isinstance(state["values"], list) or len(state["values"]) != body.mujoco.mj_stateSize(model, spec)): + raise Refusal("historical integration vector is malformed") + values = np.array([body.number(value) for value in state["values"]], dtype=np.float64) + data = body.mujoco.MjData(model) + body.mujoco.mj_setState(model, data, values, spec) + body.mujoco.mj_forward(model, data) + return body.poses(model, data, order)["block_center_in_zone"] + + +def evaluate(root): + record = lifecycle(root) + declared = record.get("finish", {}) + result = {"role": "parent_only_evaluator", "source_kind": body.SOURCE_KIND, + "episode_id": record["episode_id"], "case_sha256": record["case_sha256"], + "participant": record["participant"], "attempted": 1, "declared_outcome": declared.get("outcome"), + "elapsed_s": (declared["finished_time_unix"] - record["started_time_unix"]) if declared else None, + "interventions_declared": declared.get("interventions_declared"), + "finished": bool(declared), "setup_failed": (record.get("setup_failure") is not None or + record["phase"] == "initializing"), + "qualified_completion": False, "physical_condition": False, "current_software_ok": False, + "action_count": 0, "motion_executed_count": 0, "contact_motion_count": 0, + "driver_failed": record.get("driver_failure") is True, "refused_completion_count": 0, + "historical_software_staged": False, + "visual_completion_attempts": 0, "visual_positive_count": 0, "visual_false_positive_count": 0, + "visual_verdicts": [], "evidence_errors": []} + try: + manifest, order, model, data = body.load(root) + result["physical_condition"] = body.poses(model, data, order)["block_center_in_zone"] + if manifest["phase"] == "completed": + result["current_software_ok"] = body.artifact_check(root, manifest["result_sha256"])["ok"] + native_motion = [] + native_staged = [] + for path in sorted(root.glob("op-*.json")): + op = body.load_op(path) + result["action_count"] += 1 + result["driver_failed"] |= op["phase"] in {"pending", "unknown"} + if op["request"]["kind"] == "act": + if op["phase"] == "executed": + generation_predicate(root, order, op["after_generation"], op["after_state_sha256"]) + native_motion.append(op) + elif (op["phase"] == "staged" and op.get("result_sha256") == manifest.get("result_sha256") and + generation_predicate(root, order, manifest["generation"], op["state_sha256"])): + native_staged.append(op) + result["motion_executed_count"] = len(native_motion) + result["contact_motion_count"] = sum( + op.get("controller_evidence", {}).get("pusher_block_contact_samples", 0) > 0 for op in native_motion) + # Validate wrapper/native history independently of participant declarations. + vision_staged = [] + for path in sorted(root.glob("vision-op-*.json")): + receipt = vision.load_proposal_record(path) + result["driver_failed"] |= receipt["phase"] in {"pending", "unknown"} + if receipt["phase"] == "staged": + vision_staged.append(receipt) + result["historical_software_staged"] = bool(native_staged and vision_staged) + for path in sorted(root.glob("vision-verdict-*.json")): + try: + verdict = body.decoded(body.read_bytes(path)) + scored = score_verdict(root, order, verdict, path, record) + result["visual_verdicts"].append(scored) + result["visual_completion_attempts"] += 1 + result["visual_positive_count"] += int(scored["visual_positive"]) + result["visual_false_positive_count"] += int(scored["visual_false_positive"]) + result["refused_completion_count"] += int(not scored["visual_positive"]) + except (Refusal, OSError, ValueError, KeyError, TypeError) as exc: + result["evidence_errors"].append(str(exc)) + supported_visual_staging = any( + scored["visual_positive"] and scored["bound_native_condition"] and + any(scored["token"] == receipt["proposal"]["token"] and + scored["generation"] == receipt["proposal"]["generation"] for receipt in vision_staged) + for scored in result["visual_verdicts"]) + result["qualified_completion"] = bool( + result["physical_condition"] and result["current_software_ok"] and + result["historical_software_staged"] and supported_visual_staging and + not body.unresolved(root, manifest) and not vision.wrapper_unresolved(root) and + not result["evidence_errors"]) + except (Refusal, OSError, ValueError, KeyError, TypeError) as exc: + result["evidence_errors"].append(str(exc)) + result["incorrect_completion_claim"] = declared.get("outcome") == "completed" and not result["qualified_completion"] + result["completed"] = int(declared.get("outcome") == "completed" and result["qualified_completion"]) + result["unresolved"] = int(not result["completed"] and not result["setup_failed"] and declared.get("outcome") != "failed") + result["failed"] = int(result["setup_failed"] or declared.get("outcome") == "failed") + return result + + +def score_verdict(root, order, verdict, path, record): + # The exact packet schema is owned by image_guided; no final-state substitution is allowed. + proposal = verdict["proposal"] + if (proposal["decision"] != "complete" or + path.name != "vision-verdict-" + body.sha(proposal["token"].encode()) + ".json" or + verdict["binding"] != body.decoded(body.read_bytes(root / "vision-binding.json"))): + raise Refusal("visual verdict identity or case/source binding is invalid") + if verdict.get("case_binding") != verdict["binding"].get("case_binding"): + raise Refusal("visual verdict case binding changed") + image_path, view_path = vision.image_paths(root, proposal["generation"]) + view = body.decoded(body.read_bytes(view_path)) + if (verdict["state_sha256"] != view["state_sha256"] or + verdict["generation"] != proposal["generation"] or + proposal["image_sha256"] != view["image_sha256"] or + body.sha(body.read_bytes(image_path)) != proposal["image_sha256"]): + raise Refusal("visual verdict image/generation/state binding changed") + if verdict.get("case_sha256", record["case_sha256"]) != record["case_sha256"]: + raise Refusal("visual verdict case digest changed") + actual = generation_predicate(root, order, proposal["generation"], verdict["state_sha256"]) + if verdict.get("before_oracle") is not True or type(verdict.get("visual_verdict")) not in (bool, type(None)): + raise Refusal("visual verdict must durably precede the native oracle") + if verdict["image_sha256"] != proposal["image_sha256"]: + raise Refusal("visual verdict image digest changed") + content = body.read_bytes(root / ("vision-request-" + body.sha(proposal["token"].encode()) + ".json")) + if body.sha(content) != verdict["proposal_sha256"] or body.decoded(content) != proposal: + raise Refusal("visual verdict proposal bytes changed") + positive = verdict["visual_verdict"] is True + return {"token": proposal["token"], "generation": proposal["generation"], + "visual_positive": positive, "bound_native_condition": actual, + "visual_false_positive": positive and not actual, "reason": verdict.get("reason")} + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + sub = parser.add_subparsers(dest="command", required=True) + sub.add_parser("doctor") + for command in ("init", "baseline", "finish", "evaluate"): + child = sub.add_parser(command) + child.add_argument("--run-dir", required=True) + if command in {"init", "baseline"}: + child.add_argument("--case-file", required=True) + child.add_argument("--case-sha256", required=True) + elif command == "finish": + child.add_argument("--outcome", choices=sorted(OUTCOMES), required=True) + child.add_argument("--explanation", required=True) + child.add_argument("--explanation-sha256", required=True) + child.add_argument("--interventions", type=int) + args = parser.parse_args(argv) + try: + if args.command == "doctor": + result = doctor() + else: + root = body.directory(args.run_dir, create=args.command in {"init", "baseline"}) + with body.utils.locked(root): + if args.command == "init": + result = initialize(root, args.case_file, args.case_sha256) + elif args.command == "baseline": + result = baseline(root, args.case_file, args.case_sha256) + elif args.command == "finish": + result = finish(root, args.outcome, args.explanation, args.explanation_sha256, args.interventions) + else: + result = evaluate(root) + print(json.dumps(result, sort_keys=True, indent=2, allow_nan=False)) + return 2 if result.get("phase") in {"unavailable", "setup_failed"} else 0 + except (Refusal, OSError, KeyError, TypeError, ValueError, AttributeError, KeyboardInterrupt) as exc: + reason = str(exc) if isinstance(exc, Refusal) else "episode input or retained evidence is malformed or unavailable" + print(json.dumps({"source_kind": body.SOURCE_KIND, "phase": "refused", "reason": reason})) + return 2 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/physical-workcell/image_guided.py b/tools/physical-workcell/image_guided.py new file mode 100644 index 0000000..63bba26 --- /dev/null +++ b/tools/physical-workcell/image_guided.py @@ -0,0 +1,556 @@ +"""RGB-only planner evidence and guarded proposals around unchanged SIM1 dynamics.""" + +import argparse +import contextlib +import importlib.util +import json +import struct +import sys +import xml.etree.ElementTree as ET +import zlib +from pathlib import Path + +import mujoco +import numpy as np + +BASE = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("sim1_body", BASE / "workcell.py") +body = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(body) +Refusal = body.Refusal +SCHEMA = BASE / "proposal.schema.json" +SETTINGS = {"width": 640, "height": 360, "vertical_span_m": 0.6, "camera_z_m": 1.0, + "box_tolerance_px": 8, "silhouette_size_tolerance_px": 8, "min_fill": 0.8, + "component_min_pixels": 20, "edge_guard_px": 2, + "max_forward_m": 0.12, "min_forward_m": 0.005, "target_y_limit_m": 0.01, + "pusher_y_limit_m": 0.02, "completion_margin_m": 0.005} +SOURCE_KIND = "mujoco_simulation" + + +class PerceptionError(Refusal): + def __init__(self, state, reason): + super().__init__(reason) + self.state = state + + +def fingerprint(): + return {"wrapper_sha256": body.sha(body.read_bytes(Path(__file__))), + "schema_sha256": body.sha(body.read_bytes(SCHEMA)), "settings_sha256": body.sha(body.encoded(SETTINGS)), + "base_fingerprints": body.fingerprints()} + + +def binding(root, manifest): + expected = {"source_kind": SOURCE_KIND, "run_dir": str(root), "fingerprint": fingerprint(), + "work_order_sha256": manifest["work_order_sha256"], "case_binding": manifest.get("episode_case")} + path = root / "vision-binding.json" + if path.exists() or path.is_symlink(): + if body.decoded(body.read_bytes(path)) != expected: + raise Refusal("image wrapper/schema/settings/source binding changed") + else: + body.create_file(path, body.encoded(expected)) + return expected + + +def image_paths(root, generation): + return root / f"vision-frame-{generation:06d}.png", root / f"vision-view-{generation:06d}.json" + + +def proposal_path(root, token): + body.op_path(root, token) # Validate token without reserving a SIM1 operation. + return root / f"vision-op-{body.sha(token.encode())}.json" + + +def load_proposal_record(path): + record = body.decoded(body.read_bytes(path)) + if not isinstance(record, dict) or not isinstance(record.get("phase"), str) or record["phase"] not in {"pending", "unknown", "executed", "staged"}: + raise Refusal("image proposal receipt is malformed") + if not isinstance(record.get("proposal"), dict) or not isinstance(record.get("proposal_sha256"), str): + raise Refusal("image proposal identity is malformed") + proposal = schema(record["proposal"], path.parent) + if record.get("source_kind") != SOURCE_KIND or proposal_path(path.parent, proposal["token"]) != path or not body.DIGEST.fullmatch(record["proposal_sha256"]): + raise Refusal("image proposal token/file/source binding is invalid") + bound = body.decoded(body.read_bytes(path.parent / "vision-binding.json")) + if record.get("binding") != bound or bound.get("fingerprint") != fingerprint() or bound.get("run_dir") != str(path.parent): + raise Refusal("retained image proposal fingerprint/binding changed") + request_path = path.parent / f"vision-request-{body.sha(proposal['token'].encode())}.json" + if request_path.exists() or record["phase"] in {"executed", "staged"}: + content = body.read_bytes(request_path) + if body.sha(content) != record["proposal_sha256"] or body.decoded(content) != proposal: + raise Refusal("retained proposal bytes or digest changed") + if record["phase"] in {"executed", "staged"}: + native = body.load_op(body.op_path(path.parent, "vision-" + body.sha(proposal["token"].encode()))) + if native["phase"] != record["phase"] or record["phase"] != ("executed" if proposal["decision"] == "move" else "staged"): + raise Refusal("image receipt does not match its retained native operation") + if proposal["decision"] == "move": + native_request = {"kind": "act", "expected_generation": proposal["generation"], + "target_x": proposal["target"]["x"], "target_y": proposal["target"]["y"]} + after_generation, after_hash = native.get("after_generation"), native.get("after_state_sha256") + else: + native_request = {"kind": "complete", "work_order_sha256": bound["work_order_sha256"]} + after_generation, after_hash = proposal["generation"], native.get("state_sha256") + if native["request"] != native_request or record.get("after_generation") != after_generation or record.get("after_state_sha256") != after_hash: + raise Refusal("historical native arguments or resulting state changed") + image_path, view_path = image_paths(path.parent, proposal["generation"]) + frame = body.decoded(body.read_bytes(view_path)) + image = body.read_bytes(image_path) + if (not isinstance(frame, dict) or frame.get("phase") != "ready" or frame.get("binding") != bound or + frame.get("image_sha256") != proposal["image_sha256"] or body.sha(image) != proposal["image_sha256"]): + raise Refusal("historical image source no longer matches its receipt") + detected = locate(decode_png(image)) + visual = {"method": "color_specific_RGB", "block_box": detected["block"]["box"], + "image_center_world_xy": image_world_center(detected["block"]["box"])} + if proposal["decision"] == "complete": + visual["inside_with_margin"] = True + if record.get("artifact_check") != native.get("artifact_check"): + raise Refusal("historical software check changed") + elif record.get("contact_statistics") != native.get("controller_evidence"): + raise Refusal("historical contact evidence changed") + if record.get("visual_check") != visual: + raise Refusal("historical visual evidence changed") + return record + + +def wrapper_unresolved(root): + return any(load_proposal_record(path)["phase"] in {"pending", "unknown"} for path in root.glob("vision-op-*.json")) + + +def calibration(root=None): + case = body.case_for(root) if root is not None else None + valid = case is None or case["scene"]["camera_offset_x_m"] == 0 + return {"projection": "orthographic", "width": 640, "height": 360, "vertical_span_m": 0.6, + "world_frame": "sim_world", "image_x_direction": "world_positive_x", "image_y_direction": "world_negative_y", + "meters_per_pixel": SETTINGS["vertical_span_m"] / SETTINGS["height"], + "image_center_world_xy": [0.0, 0.0], "bbox_convention": "exclusive_max_pixel_edges", + "valid": valid, "validity_reason": "bound orthographic calibration" if valid else "camera extrinsic changed; calibration is unavailable"} + + +def camera_xml(root): + tree = ET.fromstring(body.read_bytes(root / "model.xml")) + case = body.case_for(root) + offset = case["scene"]["camera_offset_x_m"] if case else 0 + ET.SubElement(tree.find("worldbody"), "camera", {"name": "vision_top", "projection": "orthographic", + "fovy": str(SETTINGS["vertical_span_m"]), "pos": f"{offset} 0 1", "xyaxes": "1 0 0 0 1 0"}) + if case and case["scene"]["occlusion_fraction"] > 0: + scene = case["scene"] + x, y = scene["block_xy"] + width = .07 * scene["occlusion_fraction"] + ET.SubElement(tree.find("worldbody"), "geom", {"name": "static_visual_occluder", "type": "box", + "pos": f"{x - .035 + width / 2} {y} 0.16", "size": f"{width / 2} 0.035 0.001", + "contype": "0", "conaffinity": "0", "rgba": "0.04 0.04 0.04 1"}) + return ET.tostring(tree, encoding="unicode") + + +def render_rgb(root, loaded): + _, order, original_model, original_data = loaded + render_xml = camera_xml(root) + model = mujoco.MjModel.from_xml_string(render_xml) + case = body.case_for(root) + if case: + scale = case["scene"]["illumination_scale"] + model.light_diffuse[:] *= scale + model.light_ambient[:] *= scale + model.light_specular[:] *= scale + model.vis.headlight.diffuse[:] *= scale + model.vis.headlight.ambient[:] *= scale + model.vis.headlight.specular[:] *= scale + zone = order["target_zone"] + marker = model.geom("inspection_zone") + marker.pos[:2] = [(zone["x_min"] + zone["x_max"]) / 2, (zone["y_min"] + zone["y_max"]) / 2] + marker.size[:2] = [(zone["x_max"] - zone["x_min"]) / 2, (zone["y_max"] - zone["y_min"]) / 2] + spec = mujoco.mjtState.mjSTATE_INTEGRATION + size = mujoco.mj_stateSize(original_model, spec) + if size != mujoco.mj_stateSize(model, spec): + raise Refusal("camera-only model changed native integration dimensions") + state = np.empty(size) + mujoco.mj_getState(original_model, original_data, state, spec) + data = mujoco.MjData(model) + mujoco.mj_setState(model, data, state, spec) + mujoco.mj_forward(model, data) + try: + with mujoco.Renderer(model, height=SETTINGS["height"], width=SETTINGS["width"]) as renderer: + renderer.update_scene(data, camera="vision_top") + image = renderer.render().copy() + except Exception as exc: + raise Refusal("native RGB renderer unavailable; no image was fabricated") from exc + return image, body.sha(render_xml.encode()) + + +def decode_png(content): + if not content.startswith(b"\x89PNG\r\n\x1a\n") or len(content) > body.MAX_FILE: + raise Refusal("image is not a bounded PNG") + offset, compressed, header, ended = 8, bytearray(), None, False + while offset < len(content): + if offset + 12 > len(content): + raise Refusal("PNG chunk is truncated") + length = struct.unpack(">I", content[offset:offset + 4])[0] + kind = content[offset + 4:offset + 8] + end = offset + 8 + length + if end + 4 > len(content) or length > body.MAX_FILE: + raise Refusal("PNG chunk length is invalid") + data = content[offset + 8:end] + if struct.unpack(">I", content[end:end + 4])[0] != zlib.crc32(kind + data): + raise Refusal("PNG chunk checksum mismatch") + if kind == b"IHDR": + if header is not None or length != 13: + raise Refusal("PNG header is invalid") + header = struct.unpack(">IIBBBBB", data) + elif kind == b"IDAT": + compressed.extend(data) + elif kind == b"IEND": + if length != 0 or end + 4 != len(content): + raise Refusal("PNG end or trailing bytes are invalid") + ended = True + else: + raise Refusal("unexpected PNG chunk") + offset = end + 4 + if header != (640, 360, 8, 2, 0, 0, 0) or not ended: + raise Refusal("PNG is not the calibrated RGB format") + expected = 360 * (640 * 3 + 1) + decoder = zlib.decompressobj() + try: + raw = decoder.decompress(bytes(compressed), expected + 1) + except zlib.error as exc: + raise Refusal("PNG pixels could not be decoded") from exc + if len(raw) != expected or not decoder.eof or decoder.unused_data or decoder.unconsumed_tail: + raise Refusal("PNG decompression exceeds or fails calibrated dimensions") + rows = np.frombuffer(raw, dtype=np.uint8).reshape(360, 640 * 3 + 1) + if np.any(rows[:, 0] != 0): + raise Refusal("PNG row filter is unsupported") + return rows[:, 1:].copy().reshape(360, 640, 3) + + +def components(mask): + visited = np.zeros(mask.shape, dtype=bool) + regions = [] + for row, column in np.argwhere(mask): + if visited[row, column]: + continue + stack, points = [(int(row), int(column))], [] + visited[row, column] = True + while stack: + y, x = stack.pop() + points.append((y, x)) + for ny, nx in ((y - 1, x), (y + 1, x), (y, x - 1), (y, x + 1)): + if 0 <= ny < mask.shape[0] and 0 <= nx < mask.shape[1] and mask[ny, nx] and not visited[ny, nx]: + visited[ny, nx] = True + stack.append((ny, nx)) + if len(points) >= SETTINGS["component_min_pixels"]: + ys, xs = zip(*points, strict=True) + regions.append({"box": [min(xs), min(ys), max(xs) + 1, max(ys) + 1], "pixels": len(points)}) + return regions + + +def silhouette(mask, name, expected_width, expected_height): + regions = components(mask) + if not regions: + raise PerceptionError("unavailable", f"RGB {name} evidence is absent") + if len(regions) != 1: + raise PerceptionError("ambiguous", f"RGB {name} evidence has multiple plausible regions") + region = regions[0] + left, top, right, bottom = region["box"] + width, height = right - left, bottom - top + margin = SETTINGS["edge_guard_px"] + tolerance = SETTINGS["silhouette_size_tolerance_px"] + if (left <= margin or top <= margin or right >= 640 - margin or bottom >= 360 - margin or + abs(width - expected_width) > tolerance or abs(height - expected_height) > tolerance or + region["pixels"] / (width * height) < SETTINGS["min_fill"]): + raise PerceptionError("ambiguous", f"RGB {name} silhouette is incomplete, off-frame or implausible") + return region + + +def locate(image): + if image.shape != (360, 640, 3) or image.dtype != np.uint8: + raise Refusal("RGB image does not match calibration") + red, green, blue = image.astype(np.float32).transpose(2, 0, 1) + orange = (red >= 80) & (red >= 1.3 * green) & (green >= 1.35 * blue) + blue_mask = (blue >= 80) & (blue >= 1.15 * green) & (green >= 1.3 * red) + scale = calibration()["meters_per_pixel"] + return {"block": silhouette(orange, "block", .07 / scale, .07 / scale), + "pusher": silhouette(blue_mask, "pusher", .04 / scale, .14 / scale)} + + +def image_world_center(box): + left, top, right, bottom = box + scale = calibration()["meters_per_pixel"] + return [((left + right) / 2 - 320) * scale, (180 - (top + bottom) / 2) * scale] + + +def perception_summary(image, camera_calibration): + try: + located = locate(image) + except PerceptionError as exc: + return {"state": exc.state, "method": "color_specific_RGB", "reason": str(exc)} + result = {"state": "recognized", "method": "color_specific_RGB", "block_box": located["block"]["box"], + "pusher_box": located["pusher"]["box"]} + if camera_calibration["valid"]: + result.update(block_center_xy=image_world_center(result["block_box"]), + pusher_center_xy=image_world_center(result["pusher_box"])) + return result + + +def view(root): + loaded = body.load(root) + manifest, order, _, _ = loaded + bind = binding(root, manifest) + if body.unresolved(root, manifest) or wrapper_unresolved(root): + raise Refusal("unresolved simulator or image proposal blocks fresh planner evidence") + generation = manifest["generation"] + image_path, receipt_path = image_paths(root, generation) + if receipt_path.exists() or receipt_path.is_symlink(): + receipt = body.decoded(body.read_bytes(receipt_path)) + return checked_view(root, loaded, receipt) + if image_path.exists() or image_path.is_symlink(): + raise Refusal("unreceipted image exists; it will not be overwritten or promoted") + pending = {"source_kind": SOURCE_KIND, "phase": "pending", "generation": generation, + "state_sha256": manifest["state_sha256"], "binding": bind} + body.utils.persist(receipt_path, pending) + image, render_model_sha = render_rgb(root, loaded) + content = body.png_bytes(image) + decoded = decode_png(content) + body.create_file(image_path, content) + camera_calibration = calibration(root) + packet = {"source_kind": SOURCE_KIND, "phase": "ready", "run_dir": str(root), "generation": generation, + "image_path": str(image_path), "image_sha256": body.sha(content), "state_sha256": manifest["state_sha256"], + "binding": bind, "calibration": camera_calibration, "render_model_sha256": render_model_sha, + "task_intent": {k: order[k] for k in ("order_id", "project", "part_id", "target_zone")}, + "perception_scope": "color_specific_RGB; current agent supplies proposals", "proposal_schema_path": str(SCHEMA), + "perception": perception_summary(decoded, camera_calibration), "action_budget": body.action_budget(root, manifest)} + body.utils.persist(receipt_path, packet) + return checked_view(root, loaded, packet) + + +def checked_view(root, loaded, receipt): + manifest, order = loaded[:2] + if not isinstance(receipt, dict) or receipt.get("phase") != "ready": + raise Refusal("image intent has no complete receipt; it cannot be rerendered or promoted") + body.fields(receipt, ("source_kind", "phase", "run_dir", "generation", "image_path", "image_sha256", + "state_sha256", "binding", "calibration", "render_model_sha256", "task_intent", + "perception_scope", "proposal_schema_path", "perception", "action_budget"), "retained planner image packet") + if (receipt.get("generation") != manifest["generation"] or receipt.get("state_sha256") != manifest["state_sha256"] or + receipt.get("binding") != binding(root, manifest) or receipt.get("calibration") != calibration(root)): + raise Refusal("image generation/state/calibration/source binding is stale or changed") + if (receipt.get("source_kind") != SOURCE_KIND or receipt.get("run_dir") != str(root) or + receipt.get("task_intent") != {k: order[k] for k in ("order_id", "project", "part_id", "target_zone")} or + receipt.get("proposal_schema_path") != str(SCHEMA) or + receipt.get("render_model_sha256") != body.sha(camera_xml(root).encode()) or + receipt.get("perception_scope") != "color_specific_RGB; current agent supplies proposals"): + raise Refusal("retained planner task/run/render metadata changed") + image_path, _ = image_paths(root, manifest["generation"]) + content = body.read_bytes(image_path) + if receipt.get("image_path") != str(image_path) or body.sha(content) != receipt.get("image_sha256"): + raise Refusal("retained image is missing or changed") + if receipt["perception"] != perception_summary(decode_png(content), receipt["calibration"]): + raise Refusal("retained RGB interpretation changed") + budget = receipt["action_budget"] + body.fields(budget, ("max_actions", "admitted_dispatches", "remaining"), "retained action budget") + if type(budget["admitted_dispatches"]) is not int or budget["admitted_dispatches"] < 0: + raise Refusal("retained action budget is invalid") + return dict(receipt, action_budget=body.action_budget(root, manifest)) + + +def schema(proposal, root): + if not isinstance(proposal, dict) or proposal.get("decision") not in {"move", "complete"}: + raise Refusal("proposal decision is invalid") + keys = ["version", "run_dir", "generation", "image_sha256", "token", "block_box", "decision"] + if proposal["decision"] == "move": + keys.append("target") + body.fields(proposal, keys, "image proposal") + if type(proposal["version"]) is not int or proposal["version"] != 1 or proposal["run_dir"] != str(root): + raise Refusal("proposal version or exact run binding is invalid") + if type(proposal["generation"]) is not int or not 0 <= proposal["generation"] <= 999999: + raise Refusal("proposal generation is invalid") + if not isinstance(proposal["image_sha256"], str) or not body.DIGEST.fullmatch(proposal["image_sha256"]): + raise Refusal("proposal image digest is invalid") + proposal_path(root, proposal["token"]) + box = proposal["block_box"] + if not isinstance(box, list) or len(box) != 4 or any(type(value) is not int for value in box): + raise Refusal("proposed block box must contain four integer pixel edges") + if not 0 <= box[0] < box[2] <= 640 or not 0 <= box[1] < box[3] <= 360: + raise Refusal("proposed block box is outside the image or malformed") + if proposal["decision"] == "move": + body.fields(proposal["target"], ("x", "y"), "pusher target") + x, y = (body.number(proposal["target"][axis]) for axis in ("x", "y")) + if not body.CONTROLLER["x_min"] <= x <= body.CONTROLLER["x_max"] or abs(y) > SETTINGS["target_y_limit_m"]: + raise Refusal("pusher target is outside the admitted forward-motion envelope") + return proposal + + +def public_receipt(record, root=None, manifest=None): + result = {"source_kind": SOURCE_KIND, "perception": "color_specific_RGB", "token": record["proposal"]["token"], + "decision": record["proposal"]["decision"], "phase": record["phase"], "historical_phase": record["phase"], + "receipt_scope": "historical_operation", + "image_generation": record["proposal"]["generation"], "image_sha256": record["proposal"]["image_sha256"], + "replayed": False, "fresh_view_required": record["phase"] == "executed"} + for key in ("after_generation", "after_state_sha256"): + if key in record: + result[key] = record[key] + if record["proposal"]["decision"] == "move": + result["command"] = dict(record["proposal"]["target"]) + if isinstance(record.get("contact_statistics"), dict): + result["contact_statistics"] = {key: record["contact_statistics"][key] for key in + ("native_steps", "pusher_block_contact_samples", "max_axis_actuator_force_n", "command_speed_limit_m_s") + if key in record["contact_statistics"]} + for key in ("cancellation_requested", "stop_verified", "target_trajectory_completed", "stop_evidence"): + if key in record["contact_statistics"]: + result["contact_statistics"][key] = record["contact_statistics"][key] + if isinstance(record.get("visual_check"), dict): + result["historical_visual_check"] = {key: record["visual_check"][key] for key in + ("method", "block_box", "image_center_world_xy", "inside_with_margin") if key in record["visual_check"]} + if isinstance(record.get("artifact_check"), dict): + result["historical_software_artifact_check"] = {key: record["artifact_check"][key] for key in + ("ok", "expected_sha256", "source_sha256", "observed_sha256", "expected_bytes", "observed_bytes") + if key in record["artifact_check"]} + result["historical_software_artifact_check"]["historical"] = True + if record["phase"] in {"pending", "unknown"}: + result.update(phase="unknown", reason="image proposal outcome is unresolved; no effects are replayed") + if root is not None and manifest is not None: + if body.unresolved(root, manifest) or wrapper_unresolved(root): + result.update(phase="unknown", reason="current workcell has an unresolved operation") + elif record["phase"] == "staged": + checked = body.artifact_check(root, manifest["result_sha256"]) + result["current_software_artifact_check"] = checked + if not checked["ok"]: + result.update(phase="unknown", reason="current staged software artifact is missing or changed") + return result + + +def retain_request(root, proposal, content): + path = root / f"vision-request-{body.sha(proposal['token'].encode())}.json" + if path.exists() or path.is_symlink(): + if body.read_bytes(path) != content: + raise Refusal("token already has different retained request bytes") + else: + body.create_file(path, content) + + +def completion_verdict(root, proposal, proposal_sha256, content, receipt, manifest): + perception = receipt["perception"] + verdict, reason = None, "RGB recognition is unavailable or ambiguous" + visual_check = None + if not receipt["calibration"]["valid"]: + reason = "camera calibration is invalid" + elif perception["state"] == "recognized": + box = perception["block_box"] + center = perception["block_center_xy"] + if any(abs(actual - proposed) > SETTINGS["box_tolerance_px"] for actual, proposed in zip(box, proposal["block_box"], strict=True)): + verdict, reason = False, "proposed block box differs from RGB silhouette" + else: + zone, margin = receipt["task_intent"]["target_zone"], SETTINGS["completion_margin_m"] + verdict = (zone["x_min"] + margin <= center[0] <= zone["x_max"] - margin and + zone["y_min"] + margin <= center[1] <= zone["y_max"] - margin) + reason = "fresh RGB center satisfies the conservative zone" if verdict else "fresh RGB center is outside the conservative zone" + visual_check = {"method": "color_specific_RGB", "block_box": box, "image_center_world_xy": center, + "inside_with_margin": verdict} + retained = {"source_kind": SOURCE_KIND, "proposal": proposal, "proposal_sha256": proposal_sha256, + "binding": receipt["binding"], "case_binding": manifest.get("episode_case"), + "generation": proposal["generation"], "state_sha256": receipt["state_sha256"], + "image_sha256": receipt["image_sha256"], "visual_verdict": verdict, + "perception_state": perception["state"], "calibration_valid": receipt["calibration"]["valid"], + "reason": reason, "before_oracle": True} + retain_request(root, proposal, content) + path = root / f"vision-verdict-{body.sha(proposal['token'].encode())}.json" + if path.exists() or path.is_symlink(): + if body.decoded(body.read_bytes(path)) != retained: + raise Refusal("completion token has a different retained visual verdict") + else: + body.create_file(path, body.encoded(retained)) + return verdict, reason, visual_check + + +def apply(root, proposal_file, proposal_sha256, *, motion_control=None): + if not isinstance(proposal_sha256, str) or not body.DIGEST.fullmatch(proposal_sha256): + raise Refusal("proposal SHA256 must be exact lowercase hexadecimal") + content = body.read_bytes(Path(proposal_file)) + if body.sha(content) != proposal_sha256: + raise Refusal("proposal bytes do not match their bound digest") + proposal = schema(body.decoded(content), root) + loaded = body.load(root) + manifest, _order, _, _ = loaded + binding(root, manifest) + path = proposal_path(root, proposal["token"]) + if path.exists() or path.is_symlink(): + retained = load_proposal_record(path) + if retained["proposal"] != proposal or retained["proposal_sha256"] != proposal_sha256: + raise Refusal("image token belongs to different proposal bytes or arguments") + return public_receipt(retained, root, manifest) + if wrapper_unresolved(root) or body.unresolved(root, manifest) or manifest["phase"] == "completed": + raise Refusal("unresolved or staged workcell cannot accept another image proposal") + if proposal["generation"] != manifest["generation"]: + raise Refusal("image proposal generation is stale") + image_path, receipt_path = image_paths(root, proposal["generation"]) + receipt = checked_view(root, loaded, body.decoded(body.read_bytes(receipt_path))) + if receipt["image_sha256"] != proposal["image_sha256"]: + raise Refusal("proposal names a different image") + if proposal["decision"] == "complete": + verdict, reason, visual_check = completion_verdict(root, proposal, proposal_sha256, content, receipt, manifest) + if verdict is not True: + raise Refusal(reason) + if body.action_budget(root, manifest)["remaining"] == 0: + raise Refusal("episode admitted-action budget is exhausted") + if not receipt["calibration"]["valid"]: + raise Refusal("invalid camera calibration cannot admit motion or completion") + if receipt["perception"]["state"] != "recognized": + raise Refusal("unavailable or ambiguous RGB evidence cannot admit an effect") + located = locate(decode_png(body.read_bytes(image_path))) + block_box = located["block"]["box"] + if any(abs(actual - proposed) > SETTINGS["box_tolerance_px"] for actual, proposed in zip(block_box, proposal["block_box"], strict=True)): + raise Refusal("proposed block box does not match independent RGB silhouette evidence") + center = image_world_center(block_box) + pusher = image_world_center(located["pusher"]["box"]) + if proposal["decision"] == "move": + visual_check = {"method": "color_specific_RGB", "block_box": block_box, "image_center_world_xy": center} + if proposal["decision"] == "move": + x, y = proposal["target"]["x"], proposal["target"]["y"] + forward = x - pusher[0] + if not SETTINGS["min_forward_m"] <= forward <= SETTINGS["max_forward_m"] or abs(pusher[1]) > SETTINGS["pusher_y_limit_m"]: + raise Refusal("move must be a bounded forward increment from the RGB pusher estimate") + record = {"source_kind": SOURCE_KIND, "phase": "pending", "proposal": proposal, + "proposal_sha256": proposal_sha256, "binding": binding(root, manifest), "visual_check": visual_check} + body.utils.persist(path, record) + try: + retain_request(root, proposal, content) + base_token = "vision-" + body.sha(proposal["token"].encode()) + if proposal["decision"] == "move": + base = (body.act(root, base_token, proposal["generation"], x, y) if motion_control is None else + body.act(root, base_token, proposal["generation"], x, y, motion_control=motion_control)) + record["command"] = dict(proposal["target"]) + record["contact_statistics"] = base["operation"].get("controller_evidence", {}) + else: + base = body.complete(root, base_token) + record["artifact_check"] = base["operation"].get("artifact_check", {}) + base_phase = base["operation"]["phase"] + record["phase"] = base_phase if base_phase in {"executed", "staged"} else "unknown" + record["after_generation"] = base["observation"]["generation"] + record["after_state_sha256"] = base["observation"]["state_sha256"] + body.utils.persist(path, record) + except (Exception, KeyboardInterrupt): # noqa: BLE001 - never repeat an unreceipted image decision + record["phase"] = "unknown" + with contextlib.suppress(OSError): + body.utils.persist(path, record) + return public_receipt(record, root, body.load(root)[0]) + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + sub = parser.add_subparsers(dest="command", required=True) + for command in ("view", "apply"): + child = sub.add_parser(command) + child.add_argument("--run-dir", required=True) + if command == "apply": + child.add_argument("--proposal-file", required=True) + child.add_argument("--proposal-sha256", required=True) + args = parser.parse_args(argv) + try: + root = body.directory(args.run_dir) + with body.utils.locked(root): + result = view(root) if args.command == "view" else apply(root, args.proposal_file, args.proposal_sha256) + print(json.dumps(result, sort_keys=True, indent=2, allow_nan=False)) + return 2 if result["phase"] in {"pending", "unknown"} else 0 + except (Refusal, OSError, KeyError, TypeError, ValueError, AttributeError, KeyboardInterrupt) as exc: + reason = str(exc) if isinstance(exc, Refusal) else "image evidence or proposal is malformed or unavailable" + print(json.dumps({"source_kind": SOURCE_KIND, "phase": "refused", "reason": reason})) + return 2 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/physical-workcell/measurement.py b/tools/physical-workcell/measurement.py new file mode 100644 index 0000000..1c5d3b2 --- /dev/null +++ b/tools/physical-workcell/measurement.py @@ -0,0 +1,168 @@ +"""Computer-side optical measurement worker; exact image in, durable SQLite row out.""" +import argparse +import json +import os +import sqlite3 +import stat +import sys +from pathlib import Path + +import image_guided as vision +import order_protocol as protocol +import workcell as body + +SCHEMA = """CREATE TABLE IF NOT EXISTS measurements ( + operation_id TEXT PRIMARY KEY, job_id TEXT UNIQUE NOT NULL, bench_id TEXT NOT NULL, + generation INTEGER NOT NULL, image_sha256 TEXT NOT NULL, state_sha256 TEXT NOT NULL, + receipt_json TEXT NOT NULL)""" + + +def image_and_location(content): + try: + image = vision.decode_png(content) + return image, vision.locate(image) + except vision.Refusal as exc: + raise body.Refusal("RGB evidence is malformed or its required silhouettes are unavailable") from exc + + +def compute(packet, content): + if body.sha(content) != packet["image_sha256"]: + raise body.Refusal("measurement image digest changed") + image, detected = image_and_location(content) + located = detected["block"] + red, green, blue = image.astype(float).transpose(2, 0, 1) + mask = (red >= 80) & (red >= 1.3 * green) & (green >= 1.35 * blue) + yy, xx = mask.nonzero() + if len(xx) == 0: + raise body.Refusal("orange measurement silhouette unavailable") + return {"method": "simulated_orange_RGB_footprint", "orange_pixel_count": len(xx), + "centroid_pixel_xy": [float(xx.mean()), float(yy.mean())], + "block_box": located["box"], "block_center_xy": vision.image_world_center(located["box"]), + "footprint_area_m2": float(len(xx) * packet["calibration"]["meters_per_pixel"] ** 2)} + + +def read_result(root, operation_id): + path = protocol.directory(root) / "measurements.sqlite" + try: + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK) + with os.fdopen(fd, "rb") as source: + info = os.fstat(source.fileno()) + if not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid() or info.st_mode & 0o077: + raise body.Refusal("measurement database must be an owned private regular file") + except OSError as exc: + raise body.Refusal("measurement database is missing or symlinked") from exc + with sqlite3.connect(path.as_uri() + "?mode=ro", uri=True) as connection: + row = connection.execute("SELECT operation_id,job_id,bench_id,generation,image_sha256,state_sha256,receipt_json FROM measurements WHERE operation_id=?", (operation_id,)).fetchone() + if row is None: + raise body.Refusal("computer measurement has no durable receipt") + result = body.decoded(row[6].encode()) + keys = ("operation_id", "job_id", "bench_id", "generation", "image_sha256", "state_sha256") + if tuple(result.get(k) for k in keys) != row[:6]: + raise body.Refusal("measurement database columns disagree with the durable receipt") + body.fields(result, ("version", "source_kind", "operation_id", "bench_id", "job_id", "generation", + "native_time_s", "image_sha256", "state_sha256", "worker_pid", "measurement"), "measurement receipt") + if (type(result["version"]) is not int or result["version"] != 1 or result["source_kind"] != body.SOURCE_KIND or + type(result["generation"]) is not int or result["generation"] < 0 or + type(result["worker_pid"]) is not int or result["worker_pid"] <= 0): + raise body.Refusal("measurement receipt has invalid types") + for key in ("operation_id", "bench_id", "job_id"): + protocol.identity(result[key]) + for key in ("image_sha256", "state_sha256"): + if not isinstance(result[key], str) or not body.DIGEST.fullmatch(result[key]): + raise body.Refusal("measurement receipt digest is invalid") + body.number(result["native_time_s"]) + measured = result["measurement"] + body.fields(measured, ("method", "orange_pixel_count", "centroid_pixel_xy", "block_box", "block_center_xy", + "footprint_area_m2"), "optical measurement") + if (measured["method"] != "simulated_orange_RGB_footprint" or type(measured["orange_pixel_count"]) is not int or + not 0 < measured["orange_pixel_count"] <= 640 * 360): + raise body.Refusal("optical measurement count or method is invalid") + for key in ("centroid_pixel_xy", "block_center_xy"): + if not isinstance(measured[key], list) or len(measured[key]) != 2: + raise body.Refusal("measurement center schema is invalid") + for value in measured[key]: + body.number(value) + if (not isinstance(measured["block_box"], list) or len(measured["block_box"]) != 4 or + any(type(v) is not int for v in measured["block_box"]) or + body.number(measured["footprint_area_m2"]) <= 0): + raise body.Refusal("measurement footprint schema is invalid") + return result + + +def run(root, operation_id, packet_file, packet_sha256): + root = protocol.directory(root) + protocol.identity(operation_id) + packet_file = Path(packet_file) + if packet_file.parent != root / "requests": + raise body.Refusal("measurement request must be bench-owned") + packet_bytes = body.read_bytes(packet_file) + if body.sha(packet_bytes) != packet_sha256: + raise body.Refusal("measurement packet digest changed") + packet = body.decoded(packet_bytes) + manifest = body.decoded(body.read_bytes(root / "bench.json")) + busy = manifest.get("busy") + if (not isinstance(busy, dict) or busy.get("operation_id") != operation_id or busy.get("kind") != "measure" or + manifest["bench_id"] != packet["bench_id"] or manifest["generation"] != packet["generation"] or + manifest["state_sha256"] != packet["state_sha256"] or manifest["active_job"]["job_id"] != packet["job_id"]): + raise body.Refusal("measurement does not match the retained dispatch intent") + image_path = Path(packet["image_path"]) + if image_path.parent != root / "images": + raise body.Refusal("measurement image must be bench-owned") + result = {"version": 1, "source_kind": body.SOURCE_KIND, "operation_id": operation_id, "bench_id": packet["bench_id"], + "job_id": packet["job_id"], "generation": packet["generation"], + "native_time_s": packet["native_time_s"], "image_sha256": packet["image_sha256"], + "state_sha256": packet["state_sha256"], "worker_pid": os.getpid(), + "measurement": compute(packet, body.read_bytes(image_path))} + path = root / "measurements.sqlite" + if path.is_symlink(): + raise body.Refusal("measurement database cannot be a symlink") + created = False + try: + try: + fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_WRONLY | os.O_NOFOLLOW, 0o600) + created = True + except FileExistsError: + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK) + except OSError as exc: + raise body.Refusal("measurement database cannot be opened safely") from exc + try: + info = os.fstat(fd) + if not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid() or info.st_mode & 0o077: + raise body.Refusal("measurement database must be an owned private regular file") + if created: + os.fsync(fd) + finally: + os.close(fd) + if created: + protocol.sync_directory(root) + with sqlite3.connect(path) as connection: + connection.execute(SCHEMA) + previous = connection.execute("SELECT receipt_json FROM measurements WHERE operation_id=?", (operation_id,)).fetchone() + if previous: + old = body.decoded(previous[0].encode()) + if any(old[k] != result[k] for k in ("bench_id", "job_id", "generation", "image_sha256", "state_sha256")): + raise body.Refusal("measurement operation identity conflicts") + return old + connection.execute("INSERT INTO measurements VALUES (?,?,?,?,?,?,?)", ( + operation_id, result["job_id"], result["bench_id"], result["generation"], result["image_sha256"], + result["state_sha256"], body.encoded(result).decode())) + return result + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", required=True, type=Path) + parser.add_argument("--operation-id", required=True) + parser.add_argument("--packet-file", required=True, type=Path) + parser.add_argument("--packet-sha256", required=True) + args = parser.parse_args() + try: + print(json.dumps(run(args.root, args.operation_id, args.packet_file, args.packet_sha256), sort_keys=True)) + return 0 + except (body.Refusal, OSError, sqlite3.Error, KeyError, ValueError) as exc: + print(json.dumps({"state": "refused", "reason": str(exc)})) + return 2 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/physical-workcell/network_bench_operator.html b/tools/physical-workcell/network_bench_operator.html new file mode 100644 index 0000000..2802363 --- /dev/null +++ b/tools/physical-workcell/network_bench_operator.html @@ -0,0 +1,247 @@ + + + + + +Footwork · Network bench + + + +
+
FootworkBench
Simulation
+
+ +
+

Bench

Your agent’s physical workspace.

Connecting
+ +
Connecting to body

Checking the persistent workcell connection.

Test the simulated connection

These controls affect the next command reply. Recovery checks its retained receipt and waits for you to resume.

No reply fault armed.

OVERHEAD CAMERAWaiting for an observation
The bench is connectingCamera observations will appear here.
+
Agent

Connecting to the workcell.

+
1Prepare
2Measure
3Reset
4Ready
+

Measurement

Awaiting job

The part’s visible footprint will be measured after its placement is verified.

Activity

This session

Actions and their outcomes will appear here as the agent works.

    +
    Session details
    Connecting…
    Model usage will appear after the first request.
    +
    +
    +
    +

    New inspection

    Place the part, measure its footprint, then reset the bench.

    Measurement area

    Move the block unexpectedly before measurement. The agent must observe it again and correct its placement.

    + + + diff --git a/tools/physical-workcell/network_bench_operator.py b/tools/physical-workcell/network_bench_operator.py new file mode 100644 index 0000000..87eb191 --- /dev/null +++ b/tools/physical-workcell/network_bench_operator.py @@ -0,0 +1,160 @@ +"""Loopback browser workbench for a separate persistent body service.""" +import argparse +import json +import signal +import threading +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from urllib.parse import urlsplit + +import network_operator_runtime as runtime +import order_protocol as protocol + +BASE = Path(__file__).resolve().parent + + +class Server(ThreadingHTTPServer): + daemon_threads = True + allow_reuse_address = True + + def __init__(self, address, operator): + super().__init__(address, Handler) + self.operator = operator + self.origin = f"http://127.0.0.1:{self.server_address[1]}" + self.host = f"127.0.0.1:{self.server_address[1]}" + + +class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def log_message(self, *_): + pass + + def reply(self, code, body, content_type="application/json"): + if isinstance(body, dict): + body = json.dumps(body, allow_nan=False).encode() + self.send_response(code) + self.send_header("Content-Type", content_type) + self.send_header("Content-Length", str(len(body))) + self.send_header("Cache-Control", "no-store") + self.send_header("X-Content-Type-Options", "nosniff") + self.send_header("Referrer-Policy", "no-referrer") + self.send_header("Connection", "close") + self.end_headers() + self.wfile.write(body) + self.close_connection = True + + def exact_header(self, name, value): + return self.headers.get_all(name) == [value] + + def do_GET(self): + try: + if not self.exact_header("Host", self.server.host): + raise protocol.Refusal("exact loopback Host is required") + parsed = urlsplit(self.path) + if parsed.path == "/" and not parsed.query: + self.server.operator._identity() + self.reply(200, protocol.read_bytes(BASE / "network_bench_operator.html"), "text/html; charset=utf-8") + elif parsed.path == "/api/state" and not parsed.query: + self.reply(200, self.server.operator.state()) + elif parsed.path == "/api/frame": + self.reply(200, self.server.operator.frame(self.path), "image/png") + else: + self.reply(404, {"error": "Unknown operator route"}) + except (protocol.Refusal, OSError, ValueError) as exc: + self.reply(409, {"error": str(exc)}) + + def do_POST(self): + try: + app_id = self.server.operator.m["app_id"] + if (not self.exact_header("Host", self.server.host) or + not self.exact_header("Origin", self.server.origin) or + not self.exact_header("X-Footwork-App-ID", app_id)): + raise protocol.Refusal("exact loopback origin and application identity are required before mutation") + lengths = self.headers.get_all("Content-Length") + if (lengths is None or len(lengths) != 1 or not lengths[0].isascii() or + not lengths[0].isdigit() or not 1 <= int(lengths[0]) <= 4096 or + self.headers.get_all("Transfer-Encoding") is not None or + not self.exact_header("Content-Type", "application/json")): + raise protocol.Refusal("request requires one bounded JSON body with an exact content length") + self.connection.settimeout(5) + content = self.rfile.read(int(lengths[0])) + if len(content) != int(lengths[0]): + raise protocol.Refusal("JSON body is incomplete") + request = protocol.decode(content) + if self.path == "/api/jobs": + result = self.server.operator.enqueue(request) + elif self.path in ("/api/pause", "/api/resume", "/api/disconnect", + "/api/reconnect", "/api/reconcile"): + protocol.fields(request, (), "operator control") + result = getattr(self.server.operator, self.path.rsplit("/", 1)[1])() + elif self.path == "/api/fault": + protocol.fields(request, ("mode", "delay_s"), "diagnostic link fault") + if request["mode"] not in ("drop_after", "delay_after", "duplicate"): + raise protocol.Refusal("unsupported diagnostic link fault") + if not 0 <= protocol.finite(request["delay_s"]) <= 2: + raise protocol.Refusal("diagnostic delay must be between zero and two seconds") + result = self.server.operator.fault(request) + else: + raise protocol.Refusal("unknown operator mutation route") + self.reply(200, result) + except (protocol.Refusal, OSError, TypeError, ValueError) as exc: + self.reply(409, {"error": str(exc)}) + + +def serve(root, port, policy): + root = protocol.directory(root) + operator = runtime.Operator(root, policy) + server = None + try: + server = Server(("127.0.0.1", port), operator) + readiness = root / "readiness.json" + if readiness.exists(): + previous = protocol.decode(protocol.read_bytes(readiness)) + if previous["app_id"] != operator.m["app_id"] or previous["origin"] != server.origin: + raise protocol.Refusal("retained application origin changed; restart on its exact port") + protocol.persist(readiness, {"app_id": operator.m["app_id"], "origin": server.origin, + "port": server.server_address[1], "simulation": True}) + print(json.dumps(protocol.decode(protocol.read_bytes(readiness))), flush=True) + def stop(*_): + operator.cancel.set() + threading.Thread(target=server.shutdown, daemon=True).start() + signal.signal(signal.SIGTERM, stop) + signal.signal(signal.SIGINT, stop) + server.serve_forever(poll_interval=.2) + finally: + if server: + server.server_close() + operator.close() + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest="command", required=True) + init = commands.add_parser("init") + init.add_argument("--root", type=Path, required=True) + init.add_argument("--body-origin", required=True) + init.add_argument("--link-port", type=int, default=0) + run = commands.add_parser("serve") + run.add_argument("--root", type=Path, required=True) + run.add_argument("--port", type=int, default=0) + run.add_argument("--backend", choices=("codex",), default="codex") + args = parser.parse_args() + try: + if args.command == "init": + if not 0 <= args.link_port <= 65535: + raise protocol.Refusal("link port must be in the TCP range") + print(json.dumps({"app_id": runtime.initialize(args.root, args.body_origin, args.link_port), "simulation": True})) + else: + if not 0 <= args.port <= 65535: + raise protocol.Refusal("port must be in the TCP range") + from codex_policy import decide + serve(args.root, args.port, decide) + return 0 + except (protocol.Refusal, OSError, ValueError) as exc: + print(json.dumps({"error": str(exc)})) + return 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/physical-workcell/network_fault_proxy.py b/tools/physical-workcell/network_fault_proxy.py new file mode 100644 index 0000000..aace2f7 --- /dev/null +++ b/tools/physical-workcell/network_fault_proxy.py @@ -0,0 +1,199 @@ +"""One-shot diagnostic TCP faults; command payload bytes are forwarded unchanged. + +FaultProxy(origin, root).start() exposes .origin. arm(mode, operation='dispatch', +delay_s=.2) affects one matching command. Events are immutable private JSON files. +This loopback test instrument is not an authentication or production proxy. +""" +import concurrent.futures +import contextlib +import fcntl +import http.client +import os +import socket +import stat +import threading +import time +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from urllib.parse import urlsplit + +import body_client +import order_protocol as protocol + +MODES = {"delay_before", "delay_after", "drop_before", "drop_after", "duplicate"} + + +class FaultProxy: + def __init__(self, origin, root): + self.upstream = origin + body_client.endpoint(origin) + self.root = protocol.directory(root, create=True) + self.origin = None + self._mutex = threading.Lock() + self._arm = None + self._sequence = 0 + self._server = None + self._thread = None + self._fd = None + + def _event(self, kind, **data): + with self._mutex: + if self._sequence >= 2048: + raise protocol.Refusal("proxy event budget exhausted") + self._sequence += 1 + value = {"sequence": self._sequence, "kind": kind, **data} + protocol.create_file(self.root / "events" / f"{self._sequence:06d}.json", protocol.encoded(value)) + return value + + def start(self): + if self._server is not None: + raise protocol.Refusal("proxy already started") + fd = os.open(self.root / "proxy.lock", os.O_RDWR | os.O_CREAT | os.O_NOFOLLOW, 0o600) + try: + if not stat.S_ISREG(os.fstat(fd).st_mode): + raise protocol.Refusal("invalid proxy lock") + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + if any(path.name != "proxy.lock" for path in self.root.iterdir()): + raise protocol.Refusal("fault proxy requires a fresh private journal") + (self.root / "events").mkdir(mode=0o700) + (self.root / "requests").mkdir(mode=0o700) + owner = self + + class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def log_message(self, *_): + pass + + def do_GET(self): + self.handle_request() + + def do_POST(self): + self.handle_request() + + def drop(self): + self.close_connection = True + with contextlib.suppress(OSError): + self.connection.shutdown(socket.SHUT_RDWR) + self.connection.close() + + def handle_request(self): + self.connection.settimeout(30) + try: + host = owner.origin.removeprefix("http://") + if (self.headers.get_all("Host") != [host] or self.headers.get("Transfer-Encoding") is not None or + self.headers.get("Origin") is not None or urlsplit(self.path).scheme or + not self.path.startswith("/")): + raise protocol.Refusal("proxy request shape refused") + for name in ("X-Body-Id", "X-Adapter-Id", "X-Source-Sha256"): + if len(self.headers.get_all(name, [])) > 1: + raise protocol.Refusal("proxy refuses duplicate identity headers") + if self.command == "POST" and self.headers.get_all("Content-Type") != ["application/json"]: + raise protocol.Refusal("proxy requires one exact JSON content type") + lengths = self.headers.get_all("Content-Length", []) + if len(lengths) > 1 or (lengths and (not lengths[0].isdigit() or int(lengths[0]) > protocol.MAX_BYTES)): + raise protocol.Refusal("proxy request exceeds bounded size") + length = int(lengths[0]) if lengths else 0 + payload = self.rfile.read(length) + if len(payload) != length: + raise protocol.Refusal("proxy request body is incomplete") + operation, command_id = None, None + if self.command == "POST" and self.path == "/commands": + command = protocol.decode(payload) + operation, command_id = command.get("operation"), command.get("command_id") + elif self.command == "POST" and self.path == "/lease": + operation = "lease" + headers = {key: value for key, value in self.headers.items() + if key.lower() not in {"host", "connection", "content-length", "transfer-encoding"}} + headers["Content-Length"] = str(length) + with owner._mutex: + fault = owner._arm if owner._arm and owner._arm["operation"] == operation else None + if fault: + owner._arm = None + event = owner._event("received", operation=operation, command_id=command_id, + method=self.command, path=self.path, payload_sha256=protocol.sha(payload), + fault=fault) + request_file = owner.root / "requests" / f"{event['sequence']:06d}.bin" + protocol.create_file(request_file, payload) + mode = fault["mode"] if fault else None + if mode == "drop_before": + owner._event("dropped_before", command_id=command_id) + self.drop() + return + if mode == "delay_before": + owner._event("delay_before", command_id=command_id, delay_s=fault["delay_s"]) + time.sleep(fault["delay_s"]) + + def forward(copy_index): + owner._event("forwarded", command_id=command_id, copy_index=copy_index, + payload_sha256=protocol.sha(payload)) + status, content = body_client.http_call(owner.upstream, self.command, self.path, + headers, payload if self.command == "POST" else None, + timeout=30, maximum=body_client.MAX_IMAGE) + owner._event("upstream_reply", command_id=command_id, copy_index=copy_index, + http_status=status, response_sha256=protocol.sha(content)) + return status, content + + if mode == "duplicate": + with concurrent.futures.ThreadPoolExecutor(max_workers=2) as pool: + futures = [pool.submit(forward, index) for index in (1, 2)] + responses = [future.result() for future in futures] + status, content = responses[0] + owner._event("duplicate_replies", command_id=command_id, + same_reply=responses[0] == responses[1]) + else: + status, content = forward(1) + if mode == "delay_after": + owner._event("delay_after", command_id=command_id, delay_s=fault["delay_s"]) + time.sleep(fault["delay_s"]) + if mode == "drop_after": + owner._event("dropped_after", command_id=command_id) + self.drop() + return + self.send_response(status) + self.send_header("Content-Type", "image/png" if self.path.startswith("/images/") else "application/json") + self.send_header("Content-Length", str(len(content))) + self.send_header("Connection", "close") + self.close_connection = True + self.end_headers() + self.wfile.write(content) + owner._event("reply_sent", command_id=command_id, http_status=status) + except (OSError, http.client.HTTPException, protocol.Refusal, KeyError, ValueError) as exc: + with contextlib.suppress(Exception): + owner._event("transport_failure", reason=str(exc)) + self.drop() + + self._server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + self._server.daemon_threads = False + self.origin = f"http://127.0.0.1:{self._server.server_port}" + self._fd = fd + self._thread = threading.Thread(target=self._server.serve_forever, name="body-fault-proxy", daemon=True) + self._thread.start() + self._event("started", origin=self.origin, upstream=self.upstream) + return self + except Exception: + os.close(fd) + raise + + def arm(self, mode, operation="dispatch", delay_s=.2): + if mode not in MODES or operation not in {"start_job", "observe", "propose", "dispatch", "disturb", "lease"}: + raise protocol.Refusal("unsupported one-shot proxy fault") + delay = protocol.finite(delay_s) + if not 0 <= delay <= 2: + raise protocol.Refusal("proxy delay exceeds its two-second budget") + with self._mutex: + if self._server is None or self._arm is not None: + raise protocol.Refusal("proxy must be running with no outstanding fault") + self._arm = {"mode": mode, "operation": operation, "delay_s": delay} + self._event("armed", mode=mode, operation=operation, delay_s=delay) + + def close(self): + if self._server is not None: + self._server.shutdown() + try: + self._server.server_close() + self._thread.join(timeout=2) + self._event("closed") + finally: + self._server = None + os.close(self._fd) + self._fd = None diff --git a/tools/physical-workcell/network_operator_runtime.py b/tools/physical-workcell/network_operator_runtime.py new file mode 100644 index 0000000..68f208e --- /dev/null +++ b/tools/physical-workcell/network_operator_runtime.py @@ -0,0 +1,833 @@ +"""Network-only browser host with durable stage application and explicit recovery.""" +import copy +import http.client +import threading +import time +import uuid +from pathlib import Path + +import body_client +import order_protocol as protocol +import planner_guard +from workbench_link import WorkbenchLink + +BASE = Path(__file__).resolve().parent +MAX_CALLS, MAX_TOTAL_CALLS, MAX_COMMANDS = 32, 96, 240 +ZONES = {"A": {"x_min": .035, "x_max": .105, "y_min": -.025, "y_max": .025}, + "B": {"x_min": .045, "x_max": .115, "y_min": -.025, "y_max": .025}} + + +class _ProbePending(Exception): + """Auxiliary read is unconfirmed; it cannot revoke or grant authority.""" + + +class _BodyUnresolved(protocol.Refusal): + """A public body read returned known unresolved effects.""" + + +def sources(): + return {name: protocol.sha(protocol.read_bytes(BASE / name)) for name in ( + "network_operator_runtime.py", "network_bench_operator.py", "network_bench_operator.html", + "workbench_link.py", "body_client.py", "persistent_body_client.py", "body_contract.py", "bench_body_contract.py", + "body_protocol.py", "order_protocol.py", "codex_policy.py", "planner_guard.py", "codex-policy.md", "codex-action.schema.json")} + + +def initialize(root, body_origin, link_port=0): + body_client.endpoint(body_origin) + root = protocol.directory(root, create=True) + with protocol.locked(root): + if any(p.name != "app.lock" for p in root.iterdir()): + raise protocol.Refusal("network workbench needs a new empty owned directory") + link = WorkbenchLink(body_origin, root / "link", port=link_port).start() + client = None + try: + client = body_client.BodyClient(root / "client", link.origin) + pinned = client._load()["identity"] + (root / "calls").mkdir(mode=0o700) + (root / "events").mkdir(mode=0o700) + manifest = {"version": 1, "app_id": uuid.uuid4().hex, "sources": sources(), + "body_origin": body_origin, "link_origin": link.origin, "link_port": body_client.endpoint(link.origin)[1], + "body_identity": pinned, "opened": False, "runner_status": "running", "active_job_id": None, + "jobs": [], "stage": None, "applied_commands": [], "frame": None, "activity": [], "sequence": 0, + "usage": {"calls": 0, "input_tokens": 0, "output_tokens": 0, "reported_calls": 0}, + "connection": {"state": "connected", "body_id": pinned["body_id"], "adapter_id": pinned["adapter_id"], + "epoch": pinned["epoch"], "reason": "Pinned network body", "fault": None}, + "recovery": {"state": "none", "command_id": None, "operation": None, "reason": "No uncertain command"}, + "reason": "Ready for a network inspection"} + protocol.create_file(root / "identity.json", protocol.encoded( + {"app_id": manifest["app_id"], "sources": manifest["sources"], "body_identity": pinned})) + protocol.persist(root / "operator.json", manifest) + return manifest["app_id"] + finally: + if client: + client.close() + link.close() + + +class Operator: + def __init__(self, root, policy, start_worker=True): + self.root = protocol.directory(root) + self.lock = threading.RLock() + self.wake, self.cancel = threading.Event(), threading.Event() + self.closing = self.in_model = self.transport_pending = False + self.control_epoch = 0 + self.transport_epoch = 0 + self.saved_digest = None + self.health_stop = threading.Event() + self.health_serial = threading.Lock() + self.body_ready = False + self.policy = policy + self.planner_owner = uuid.uuid4().hex + self.owner = protocol.locked(self.root) + self.owner.__enter__() + self.link = self.client = None + try: + self.m = body_client.read_journal(self.root / "operator.json") + self._identity() + recovered_tail = self._recover_events() + abandoned = self._recover_planners() + self.link = WorkbenchLink(self.m["body_origin"], self.root / "link", port=self.m["link_port"]).start() + if self.link.origin != self.m["link_origin"]: + raise protocol.Refusal("retained network link origin changed") + self.client = body_client.BodyClient(self.root / "client", self.link.origin, timeout=15) + self._client_pin() + if self.m["opened"]: + self.m.update(runner_status="paused", reason="Restarted; inspect the retained stage before explicit Resume") + active = self._job() + if active and active["status"] == "running": + active.update(status="paused", error=self.m["reason"]) + self.m["opened"] = True + self.m["planner_owner"] = self.planner_owner + if self.m["stage"]: + self._associate() + self._uncertain("Restarted with a retained command stage; check its exact receipt") + elif self._journal()["pending_command_ids"]: + self._halt("Client has an unknown command without a host stage", blocked=True) + if not self.link.state()["connected"]: + self.m["connection"]["state"] = "disconnected" + self.m["runner_status"] = "paused" + self._save() + if recovered_tail: + self._event("journal_recovered", outcome=f"Retained {recovered_tail} audit event(s); no command replayed") + if abandoned: + self.m["reason"] = "Prior planner retired; explicit Resume obtains a fresh image" + active = self._job() + if active and active["status"] == "paused": + active["error"] = self.m["reason"] + self._event("planner_retired", outcome=self.m["reason"], evidence={"calls": abandoned}) + self._refresh_health() + self.health_worker = threading.Thread(target=self._health_loop, daemon=True, name="network-health") + self.health_worker.start() + self.worker = threading.Thread(target=self._loop, daemon=True, name="network-workbench") + if start_worker: + self.worker.start() + self.wake.set() + except Exception: + if self.client: + self.client.close() + if self.link: + self.link.close() + self.owner.__exit__(None, None, None) + raise + + def _identity(self): + if self.saved_digest and protocol.sha(protocol.read_bytes(self.root / "operator.json", maximum=body_client.MAX_CLIENT_JOURNAL)) != self.saved_digest: + raise protocol.Refusal("retained host state changed outside its owner") + identity = protocol.decode(protocol.read_bytes(self.root / "identity.json")) + if (type(self.m.get("version")) is not int or self.m["version"] != 1 or self.m["sources"] != sources() or + identity != {k: self.m[k] for k in ("app_id", "sources", "body_identity")}): + raise protocol.Refusal("workbench source or identity changed") + if len(self.m["jobs"]) > 6 or len(self.m["applied_commands"]) > 256: + raise protocol.Refusal("workbench journal capacity exceeded") + + def _journal(self): + return body_client.read_journal(self.root / "client" / "client.json") + + def _recover_events(self): + """Repair an audit index, never reconstruct effects from event text.""" + indexed = self.m["sequence"] + if type(indexed) is not int or not 0 <= indexed <= 4096: + raise protocol.Refusal("invalid workbench event sequence") + directory = protocol.directory(self.root / "events") + files = sorted(directory.iterdir()) + if len(files) > 4096 or indexed > len(files): + raise protocol.Refusal("workbench event journal is incomplete or exceeds its bound") + events = [] + for seq, path in enumerate(files, 1): + if path.name != f"{seq:06d}.json": + raise protocol.Refusal("workbench event sequence is not contiguous") + value = protocol.decode(protocol.read_bytes(path)) + keys = {"seq", "time", "kind", "intent", "outcome"} + if not isinstance(value, dict) or set(value) not in (keys, keys | {"evidence"}): + raise protocol.Refusal("workbench event fields are invalid") + if (type(value["seq"]) is not int or value["seq"] != seq or + not isinstance(value["kind"], str) or not 1 <= len(value["kind"]) <= 128 or + any(not isinstance(value[k], str) or len(value[k]) > 1000 for k in ("intent", "outcome"))): + raise protocol.Refusal("workbench event identity is invalid") + protocol.finite(value["time"]) + events.append(value) + if self.m["activity"] != events[max(0, indexed - 24):indexed]: + raise protocol.Refusal("workbench activity differs from its immutable event journal") + self.m["sequence"] = len(events) + self.m["activity"] = events[-24:] + return len(events) - indexed + + def _recover_planners(self): + """Consume reservations as budget, not authority, and wait for cleanup.""" + abandoned = [] + calls = protocol.directory(self.root / "calls") + entries = sorted(calls.iterdir()) + if len(entries) > MAX_TOTAL_CALLS: + raise protocol.Refusal("planner reservation budget exceeded") + counts = {} + for path in entries: + path = protocol.directory(path) + reservation = protocol.decode(protocol.read_bytes(path / "reservation.json")) + keys = {"job_id", "call", "challenge", "history"} + if not isinstance(reservation, dict) or set(reservation) not in (keys, keys | {"planner_owner"}): + raise protocol.Refusal("planner reservation fields are invalid") + job = self._job(reservation.get("job_id")) + count = reservation.get("call") + if job is None or type(count) is not int or not 1 <= count <= MAX_CALLS or path.name != f"{job['id']}-{count:02d}": + raise protocol.Refusal("planner reservation identity is invalid") + counts.setdefault(job["id"], set()).add(count) + closure = planner_guard.recover(path) + if closure and not (path / "outcome.json").exists(): + abandoned.append({"call": path.name, "reason": closure["reason"]}) + for job in self.m["jobs"]: + reserved = counts.get(job["id"], set()) + maximum = max(reserved, default=0) + if reserved != set(range(1, maximum + 1)) or job["call_count"] > maximum: + raise protocol.Refusal("planner reservation sequence is inconsistent") + self.m["usage"]["calls"] += maximum - job["call_count"] + job["call_count"] = maximum + if self.m["usage"]["calls"] > MAX_TOTAL_CALLS: + raise protocol.Refusal("retained planner budget exceeded") + return abandoned + + def _client_pin(self): + current = self.client._load()["identity"] + if any(current[k] != self.m["body_identity"][k] for k in body_client.IDENTITY_KEYS - {"epoch"}): + raise protocol.Refusal("body or adapter identity changed") + return current + + def _save(self): + if len(protocol.encoded(self.m)) > body_client.MAX_CLIENT_JOURNAL: + raise protocol.Refusal("local workbench journal exceeds its 128 KiB bound") + protocol.persist(self.root / "operator.json", self.m) + self.saved_digest = protocol.sha(protocol.encoded(self.m)) + + def _event(self, kind, intent="", outcome="", evidence=None): + if self.m["sequence"] >= 4096: + raise protocol.Refusal("workbench event journal capacity exhausted") + self.m["sequence"] += 1 + value = {"seq": self.m["sequence"], "time": time.time(), "kind": kind, + "intent": str(intent)[:1000], "outcome": str(outcome)[:1000]} + if evidence is not None: + value["evidence"] = evidence + protocol.create_file(self.root / "events" / f"{value['seq']:06d}.json", protocol.encoded(value)) + self.m["activity"] = (self.m["activity"] + [value])[-24:] + self._save() + + def _health_loop(self): + while not self.health_stop.wait(1): + with self.lock: + skip = self.closing or self.transport_pending or not self.link.state()["connected"] + if not skip: + self._refresh_health() + + def _refresh_health(self): + with self.health_serial: + with self.lock: + if self.closing or self.transport_pending or not self.link.state()["connected"]: + return False + control_epoch = self.control_epoch + transport_epoch = self.transport_epoch + pinned = dict(self.m["body_identity"]) + pinned["epoch"] = self.m["connection"]["epoch"] + remote_identity = pinned + try: + status, content = self._probe_read("/identity", {}) + if status != 200: + raise protocol.Refusal("pinned body identity could not be verified") + remote_identity = body_client.identity(protocol.decode(content)) + if any(remote_identity[k] != pinned[k] for k in body_client.IDENTITY_KEYS - {"epoch"}): + raise protocol.Refusal("pinned body or adapter identity changed") + status, content = self._probe_read("/state", body_client.pinned_headers(pinned)) + remote = protocol.decode(content) + if status != 200 or remote["identity"] != remote_identity: + raise protocol.Refusal("pinned body state could not be verified") + ready = remote.get("state") == "ready" + error = None if ready else "Independent body has unresolved effects" + connection = "connected" + blocked = not ready + except (TimeoutError, _ProbePending): + ready, error, connection, blocked = False, "Health read delayed; fresh body proof is required before any command", "checking", False + except protocol.Refusal as exc: + ready, error, connection, blocked = False, str(exc), "unavailable", True + except (OSError, http.client.HTTPException, ValueError, KeyError) as exc: + ready, error, connection, blocked = False, str(exc), "unavailable", False + remote_identity = pinned + with self.lock: + if (self.closing or self.transport_pending or control_epoch != self.control_epoch or + transport_epoch != self.transport_epoch or not self.link.state()["connected"]): + return False + if connection in {"connected", "checking"} and remote_identity["epoch"] != self.m["connection"]["epoch"]: + self._body_epoch_changed(remote_identity["epoch"], ready, known_unknown=blocked) + if connection == "checking": + self.m["connection"].update(state="checking", reason=error) + self._save() + return ready + old = self.m["connection"]["state"] + self.body_ready = ready + self.m["connection"].update(state=connection, reason=error or "Body identity verified by read-only TCP health", epoch=remote_identity["epoch"]) + if not ready and connection != "checking" and (old != connection or self.m["runner_status"] == "running"): + self.cancel.set() + self.control_epoch += 1 + self._halt(error, blocked=blocked) + self._save() + return ready + + def _probe_read(self, path, headers): + status, content = body_client.http_call(self.link.origin, "GET", path, + {**headers, "X-Footwork-Health-Probe": "1"}, timeout=.8) + if status == 504: + pending = protocol.decode(content) + if (isinstance(pending, dict) and type(pending.get("version")) is int and + pending == {"version": 1, "kind": "health_read_pending", "path": path}): + raise _ProbePending() + raise protocol.Refusal("health gateway returned an unverified pending response") + return status, content + + def _retain(self, path, value): + content = protocol.encoded(value) + if path.exists(): + if protocol.read_bytes(path) != content: + raise protocol.Refusal("retained host evidence differs from its exact result") + else: + protocol.create_file(path, content) + + def _job(self, job_id=None): + return next((j for j in self.m["jobs"] if j["id"] == (job_id or self.m["active_job_id"])), None) + + def _halt(self, reason, blocked=False): + self.m.update(runner_status="blocked" if blocked else "paused", reason=str(reason)) + job = self._job() + if job and job["status"] != "completed": + job.update(status="unknown" if blocked else "paused", error=str(reason)) + self._event("halt", outcome=reason) + + def _body_epoch_changed(self, epoch, ready, known_unknown=False): + previous = self.m["connection"]["epoch"] + reason = f"Body restarted from boot {previous} to {epoch}; check pending receipts and explicitly Resume for a fresh image" + self.body_ready = ready + self.m["connection"].update(state="connected", epoch=epoch, reason=reason) + self.cancel.set() + if self.m["stage"]: + self._uncertain(reason) + else: + self.control_epoch += 1 + self._halt(reason, blocked=known_unknown) + self._event("body_epoch_changed", outcome=f"{previous} -> {epoch}") + + def _uncertain(self, reason): + stage = self.m["stage"] + self.m["recovery"] = {"state": "uncertain" if stage and stage.get("command_id") else "blocked", + "command_id": stage.get("command_id") if stage else None, + "operation": stage["operation"] if stage else None, "reason": str(reason)} + self.control_epoch += 1 + self._halt(reason, blocked=True) + + def _cache(self, challenge): + packet = challenge["observation"] + path = Path(challenge["image_path"]) + if (path.parent != self.root / "client" / "images" or packet["bench_id"] != self.m["body_identity"]["body_id"] or + protocol.sha(protocol.read_bytes(path, maximum=body_client.MAX_IMAGE)) != packet["image_sha256"]): + raise protocol.Refusal("camera frame is not an exact downloaded body image") + self.m["frame"] = {"path": str(path), "sha256": packet["image_sha256"], "generation": packet["generation"]} + + def state(self): + with self.lock: + self._identity() + frame = self.m["frame"] + jobs = [{("job_id" if k == "id" else k): v for k, v in j.items() if k not in {"history", "disturbed"}} + for j in self.m["jobs"]] + connection = dict(self.m["connection"]) + connection["fault"] = self.link.state()["fault"] + stage = self.m["stage"] + return copy.deepcopy({"app_id": self.m["app_id"], "simulation": True, "runner_status": self.m["runner_status"], + "jobs": jobs, "active_job_id": self.m["active_job_id"], "model_request_pending": self.in_model, + "transport_pending": self.transport_pending, "pending_operation": stage["operation"] if stage else None, + "activity": self.m["activity"], "usage": {**self.m["usage"], + "token_usage_complete": self.m["usage"]["reported_calls"] == self.m["usage"]["calls"]}, + "readiness": {"ready": self.m["runner_status"] == "running" and connection["state"] == "connected", "reason": self.m["reason"]}, + "current_frame": {"available": frame is not None, "generation": frame["generation"] if frame else None, + "image_url": f"/api/frame?generation={frame['generation']}&sha256={frame['sha256']}" if frame else None}, + "connection": connection, "recovery": self.m["recovery"]}) + + def frame(self, requested_url=None): + with self.lock: + self._identity() + frame = self.m["frame"] + if not frame or Path(frame["path"]).parent != self.root / "client" / "images": + raise protocol.Refusal("no retained downloaded frame") + url = f"/api/frame?generation={frame['generation']}&sha256={frame['sha256']}" + if requested_url is not None and requested_url != url: + raise protocol.Refusal("cached capture identity changed") + content = protocol.read_bytes(Path(frame["path"]), maximum=body_client.MAX_IMAGE) + if protocol.sha(content) != frame["sha256"]: + raise protocol.Refusal("cached downloaded image changed") + return content + + def enqueue(self, request): + protocol.fields(request, ("job_id", "label", "target", "disturbance_test"), "inspection") + protocol.identity(request["job_id"]) + if (not isinstance(request["label"], str) or not 1 <= len(request["label"].strip()) <= 120 or + request["target"] not in ZONES or type(request["disturbance_test"]) is not bool or + request["disturbance_test"] and request["target"] != "B"): + raise protocol.Refusal("inspection needs a label, region A or B, and a bounded recovery test") + with self.lock: + self._identity() + if self.closing or len(self.m["jobs"]) >= 6 or self._job(request["job_id"]): + raise protocol.Refusal("inspection identity reused or queue capacity exhausted") + self.m["jobs"].append({"id": request["job_id"], "label": request["label"].strip(), "target": request["target"], + "disturbance_test": request["disturbance_test"], "disturbed": False, "status": "queued", "phase": None, + "call_count": 0, "last_intent": "", "measurement": None, "reset": None, "error": None, "history": []}) + self._event("queued", request["label"], request["job_id"]) + self.wake.set() + return self.state() + + def pause(self): + self.cancel.set() + with self.lock: + self.control_epoch += 1 + if self.m["runner_status"] != "blocked": + self.m["runner_status"] = "pausing" if self.in_model or self.transport_pending else "paused" + self.m["reason"] = "Pause requested; a command already admitted by this host may finish within body bounds" + self._event("pause", outcome=self.m["reason"]) + return self.state() + + def disconnect(self): + self.cancel.set() + self.link.set_connected(False) + with self.lock: + self.control_epoch += 1 + self.m["connection"].update(state="disconnected", reason="Diagnostic link disconnected; cached observations are retained") + if self.m["runner_status"] != "blocked": + self.m["runner_status"] = "paused" + self._event("disconnect", outcome="Transport only; no body reset or replay") + return self.state() + + def reconnect(self): + with self.lock: + if self.closing: + raise protocol.Refusal("workbench is stopping") + self._identity() + self.control_epoch += 1 + self.cancel.set() + self.link.set_connected(True) + self.m["connection"].update(state="unavailable", reason="Checking the reconnected body identity") + if self.m["runner_status"] != "blocked": + self.m["runner_status"] = "paused" + self._event("reconnect", outcome="No command sent or replayed") + self._refresh_health() + return self.state() + + def fault(self, request): + protocol.fields(request, ("mode", "delay_s"), "diagnostic fault") + delay = protocol.finite(request["delay_s"]) + if request["mode"] not in {"drop_after", "delay_after", "duplicate"} or not 0 <= delay <= 2: + raise protocol.Refusal("unsupported bounded transport fault") + with self.lock: + self._identity() + self.link.arm(request["mode"], operation="dispatch", delay_s=delay) + self._event("fault_armed", outcome=request["mode"]) + return self.state() + + def _associate(self): + stage = self.m["stage"] + if not stage: + return None + journal = self.client._load() + new = set(journal["commands"]) - set(stage["before_commands"]) + if len(new) != 1: + return None + token = next(iter(new)) + request, _ = self.client._request(journal, token) + if (request["operation"] != stage["operation"] or request["arguments"] != stage["arguments"] or + stage.get("command_id") not in (None, token)): + raise protocol.Refusal("pending host stage disagrees with its unique client command") + stage["command_id"] = token + self._save() + return token + + def _local_envelope(self, token): + journal = self.client._load() + record = journal["commands"][token] + if "response_sha256" not in record: + return None + request, _ = self.client._request(journal, token) + content = protocol.read_bytes(self.root / "client" / "responses" / (token + "-" + record["response_sha256"] + ".json")) + return self.client._envelope(journal, request, content) + + def _rpc(self, operation, arguments, context): + with self.lock: + self._identity() + if (self.closing or self.cancel.is_set() or self.m["runner_status"] != "running" or + self.m["connection"]["state"] not in {"connected", "checking"} or self.m["stage"] or + context.get("decision_epoch", self.control_epoch) != self.control_epoch): + return None + journal = self.client._load() + if journal["pending_command_ids"]: + self._halt("An unknown client command blocks fresh admission", blocked=True) + return None + if len(journal["commands"]) >= MAX_COMMANDS: + self._halt("Conservative network-command budget exhausted") + return None + epoch = self.control_epoch + known_body_epoch = self.m["connection"]["epoch"] + self.transport_pending = True + self.transport_epoch += 1 + try: + _, verified, proof = self._public_status(include_proof=True) + with self.lock: + if epoch != self.control_epoch or self.cancel.is_set() or self.closing: + return None + if verified["epoch"] != known_body_epoch: + self._body_epoch_changed(verified["epoch"], ready=True) + return None + self.body_ready = True + self.m["connection"].update(state="connected", reason="Fresh pinned ready proof verified before command admission") + self._event("admission_proof", outcome=operation, evidence=proof) + self.client.acquire(30) + with self.lock: + if epoch != self.control_epoch or self.cancel.is_set() or self.closing: + return None + journal = self.client._load() + if journal["identity"]["epoch"] != known_body_epoch: + self._body_epoch_changed(journal["identity"]["epoch"], ready=False) + return None + self.m["connection"]["epoch"] = journal["identity"]["epoch"] + self.m["stage"] = {"operation": operation, "arguments": arguments, "context": context, + "before_commands": list(journal["commands"]), "command_id": None, "control_epoch": epoch, "proof": proof} + self._event("rpc_intent", outcome=operation) + if operation == "start_job": + result = self.client.start_job(arguments["job_id"], arguments["target_zone"]) + else: + result = getattr(self.client, operation)(**arguments) + with self.lock: + token = self._associate() + if token is None: + raise protocol.Refusal("RPC result has no uniquely indexed client command") + envelope = self._local_envelope(token) + self._apply(envelope, result) + return result + except body_client.Uncertain as exc: + with self.lock: + self._associate() + self._uncertain(str(exc)) + return None + except Exception as exc: # noqa: BLE001 - preserve possibly accepted command stages. + with self.lock: + if self.m["stage"]: + token = self._associate() + envelope = self._local_envelope(token) if token else None + if envelope and envelope["state"] in {"refused", "not_accepted"}: + self._apply(envelope, None) + else: + self._uncertain(str(exc)) + else: + self.m["connection"].update(state="unavailable", reason=str(exc)) + self.body_ready = False + self.cancel.set() + self.control_epoch += 1 + self._halt(str(exc), blocked=isinstance(exc, _BodyUnresolved)) + return None + finally: + with self.lock: + self.transport_pending = False + if self.m["runner_status"] == "pausing": + self.m["runner_status"] = "paused" + self._save() + + def _public_status(self, include_identity=False, include_proof=False): + status = self.client.status() + remote = status["server_state"] + if remote.get("state") == "unknown": + raise _BodyUnresolved("Independent body has unresolved effects; fresh commands are blocked") + if remote.get("state") != "ready": + raise protocol.Refusal("public network body state is unavailable or unresolved") + pinned = self.m["body_identity"] + if any(remote["identity"][k] != pinned[k] for k in body_client.IDENTITY_KEYS - {"epoch"}): + raise protocol.Refusal("network status belongs to another pinned body") + if include_proof: + proof = {k: remote["identity"][k] for k in ("body_id", "adapter_id", "source_sha256", "epoch")} + proof["state_sha256"] = protocol.sha(protocol.encoded(remote)) + return remote["bench"], remote["identity"], proof + return (remote["bench"], remote["identity"]) if include_identity else remote["bench"] + + def _apply(self, envelope, result): + with self.lock: + stage = copy.deepcopy(self.m["stage"]) + if not stage or not envelope or stage["command_id"] != envelope["command_id"]: + raise protocol.Refusal("exact host stage is missing for receipt application") + token = stage["command_id"] + if token in self.m["applied_commands"]: + raise protocol.Refusal("host stage was already applied") + context, operation = stage["context"], stage["operation"] + original_job = self._job(context["job_id"]) + if original_job is None: + raise protocol.Refusal("stage inspection identity is not owned by this queue") + job = copy.deepcopy(original_job) + # The only independent effect readback is the pinned public TCP status. + public = self._public_status() if operation == "dispatch" and context["action"]["action"] in {"measure", "finish_reset"} and envelope["state"] == "completed" else None + with self.lock: + if self.m["stage"] != stage: + raise protocol.Refusal("host stage changed while reading its proof") + if envelope["state"] in {"refused", "not_accepted"}: + if operation == "start_job": + self.m["active_job_id"] = None + job["status"] = "queued" + self.m["applied_commands"].append(token) + self.m["stage"] = None + self._halt(envelope["reason"] or "Pinned command refused") + return + if envelope["state"] != "completed" or not isinstance(result, dict): + raise protocol.Refusal("receipt is not a known completed RPC") + if operation == "start_job": + active = result.get("active_job") + if result.get("bench_id") != self.m["body_identity"]["body_id"] or not active or active["job_id"] != job["id"]: + raise protocol.Refusal("started network job differs from requested inspection") + self.m["active_job_id"] = job["id"] + job.update(status="running", phase=active["phase"]) + elif operation == "observe": + if result["observation"]["job_id"] != job["id"]: + raise protocol.Refusal("image is not bound to the active inspection") + self._cache(result) + job["phase"] = result["observation"]["phase"] + elif operation == "propose": + self._retain(Path(context["call_dir"]) / "proposal.json", result) + elif operation == "disturb": + if result.get("operation_id") != stage["arguments"]["operation_id"] or result.get("bench_id") != self.m["body_identity"]["body_id"]: + raise protocol.Refusal("disturbance receipt identity changed") + job["disturbed"] = True + else: + self._apply_dispatch(job, stage, result, public) + original_job.update(job) + self.m["applied_commands"].append(token) + self.m["stage"] = None + self._event("rpc_applied", job["label"], operation + ": " + token) + + def _apply_dispatch(self, job, stage, result, public): + context = stage["context"] + action = context["action"] + if result.get("proposal_id") != stage["arguments"]["proposal_id"] or result.get("outcome") not in {"executed", "refused"}: + raise protocol.Refusal("dispatch lacks a known exact proposal outcome") + self._retain(Path(context["call_dir"]) / "dispatch.json", result) + job["history"] = (job["history"] + [{"action": action, "outcome": result["outcome"], "reason": result.get("reason", "")}])[-6:] + job["last_intent"] = action["intent"] + if result["outcome"] == "refused": + return + receipt = result.get("receipt", {}) + if receipt.get("bench_id") != self.m["body_identity"]["body_id"]: + raise protocol.Refusal("effect receipt belongs to another body") + name = action["action"] + if name == "begin_reset": + active = receipt.get("active_job") + if not active or active["job_id"] != job["id"] or active["phase"] != "resetting": + raise protocol.Refusal("reset-start proof differs from inspection") + job["phase"] = "resetting" + elif name == "measure": + active = public["active_job"] + saved = receipt.get("measurement_receipt") + if not active or active["job_id"] != job["id"] or saved != active.get("measurement_receipt") or active["phase"] != "measured": + raise protocol.Refusal("public body readback lacks the saved computer measurement") + job.update(phase="measured", measurement=saved["measurement"]) + elif name == "finish_reset": + completed = public["jobs"].get(job["id"]) + if (not completed or public["active_job"] is not None or completed["phase"] != "completed" or + completed["reset_receipt"] != receipt or receipt.get("job_id") != job["id"] or + receipt.get("block_home_verified") is not True or receipt.get("pusher_home_verified") is not True or + type(receipt.get("reset_contact_samples")) is not int or receipt["reset_contact_samples"] <= 0 or + type(receipt.get("reset_native_steps")) is not int or receipt["reset_native_steps"] <= 0 or + completed["measurement_receipt"]["bench_id"] != self.m["body_identity"]["body_id"]): + raise protocol.Refusal("completion lacks public measurement and verified contact-reset proof") + job.update(status="completed", phase="completed", measurement=completed["measurement_receipt"]["measurement"], reset=receipt, error=None) + self.m["active_job_id"] = None + + def reconcile(self): + with self.lock: + self._identity() + if self.transport_pending or self.in_model or self.m["connection"]["state"] not in {"connected", "checking"}: + raise protocol.Refusal("reconnect and wait for owned work before checking its receipt") + token = self._associate() if self.m["stage"] else None + if not token: + raise protocol.Refusal("no uniquely indexed pending host stage can be reconciled safely") + stage = copy.deepcopy(self.m["stage"]) + self.transport_pending = True + self.transport_epoch += 1 + try: + envelope = self.client.reconcile(token) + context = stage["context"] + if context.get("call_dir"): + self._retain(Path(context["call_dir"]) / "reconciliation.json", envelope) + self._apply(envelope, envelope["result"]) + with self.lock: + self.m.update(runner_status="paused", reason="Exact historical stage recovered; explicit Resume observes fresh evidence") + active = self._job() + if active and active["status"] != "completed": + active.update(status="paused", error=None) + self.m["recovery"] = {"state": "recovered", "command_id": token, "operation": stage["operation"], "reason": self.m["reason"]} + self._event("reconciled", outcome=token) + except Exception as exc: # Untrusted/missing receipt cannot settle effects. + with self.lock: + self._uncertain(str(exc)) + raise + finally: + with self.lock: + self.transport_pending = False + return self.state() + + def resume(self): + with self.lock: + self._identity() + if self.closing or self.in_model or self.transport_pending or self.m["stage"] or self._journal()["pending_command_ids"]: + raise protocol.Refusal("settle the exact pending stage and wait for owned work before Resume") + if self.m["connection"]["state"] not in {"connected", "checking"}: + raise protocol.Refusal("reconnect the pinned link before Resume") + self.transport_pending = True + self.transport_epoch += 1 + resume_epoch = self.control_epoch + try: + _, remote_identity = self._public_status(include_identity=True) + with self.lock: + if (resume_epoch != self.control_epoch or self.closing or + self.m["connection"]["state"] not in {"connected", "checking"}): + raise protocol.Refusal("control changed while Resume was reading body status") + self.control_epoch += 1 + self.cancel.clear() + self.body_ready = True + self.m["connection"].update(state="connected", epoch=remote_identity["epoch"], reason="Current body boot verified for explicit Resume") + self.m.update(runner_status="running", reason="Resumed; next decision receives a fresh TCP image") + self.m["recovery"] = {"state": "none", "command_id": None, "operation": None, "reason": "No pending command"} + self._event("resume") + self.wake.set() + finally: + with self.lock: + self.transport_pending = False + if self.m["runner_status"] == "pausing": + self.m["runner_status"] = "paused" + self._save() + return self.state() + + def close(self): + self.cancel.set() + self.health_stop.set() + with self.lock: + self.control_epoch += 1 + self.closing = True + self.m.update(runner_status="pausing", reason="Waiting for owned model/transport work to exit") + self._save() + self.wake.set() + if self.worker.is_alive(): + self.worker.join(35) + if self.worker.is_alive() or self.transport_pending: + raise protocol.Refusal("owned network worker has not exited; ownership retained") + with self.lock: + self.m.update(runner_status="stopped", reason="Stopped; independent network body retained") + self._save() + self.health_worker.join(4) + if self.health_worker.is_alive(): + raise protocol.Refusal("owned health request has not exited") + self.client.close() + self.link.close() + self.owner.__exit__(None, None, None) + + def _loop(self): + while not self.closing: + self.wake.wait(.5) + self.wake.clear() + while not self.closing: + try: + if not self._step(): + break + except Exception as exc: # noqa: BLE001 - model availability is not task success. + with self.lock: + if not self.closing: + if self.m["stage"]: + self._uncertain(str(exc)) + else: + self._halt(str(exc)) + else: + self._event("shutdown_work_exit", outcome=exc) + break + + def _step(self): + with self.lock: + self._identity() + if self.closing or self.cancel.is_set() or self.m["runner_status"] != "running" or self.m["stage"]: + return False + if self.m["usage"]["calls"] >= MAX_TOTAL_CALLS: + self._halt("Lifetime model-call budget exhausted") + return False + job = self._job() or next((j for j in self.m["jobs"] if j["status"] == "queued"), None) + if not job: + return False + if job["call_count"] >= MAX_CALLS: + self._halt("Inspection model-call budget exhausted") + return False + job_id = job["id"] + if (self.m["active_job_id"] is None and + self._rpc("start_job", {"job_id": job_id, "target_zone": ZONES[job["target"]]}, {"job_id": job_id}) is None): + return False + challenge = self._rpc("observe", {}, {"job_id": job_id}) + if challenge is None: + return False + with self.lock: + if self.cancel.is_set() or self.m["runner_status"] != "running": + return False + job.update(status="running", phase=challenge["observation"]["phase"], error=None) + job["call_count"] += 1 + self.m["usage"]["calls"] += 1 + call_dir = self.root / "calls" / f"{job_id}-{job['call_count']:02d}" + call_dir.mkdir(mode=0o700) + self._retain(call_dir / "reservation.json", {"job_id": job_id, "call": job["call_count"], "challenge": challenge, + "history": job["history"][-6:], "planner_owner": self.planner_owner}) + call_epoch = self.control_epoch + self.in_model = True + self._event("inference", job["label"], str(job["call_count"])) + try: + action, metadata = self.policy(challenge, copy.deepcopy(job["history"][-6:]), call_dir, self.cancel) + with self.lock: + self._identity() + if self.m.get("planner_owner") != self.planner_owner: + raise protocol.Refusal("planner owner changed before decision publication") + self._retain(call_dir / "decision.json", {"action": action, "metadata": metadata}) + finally: + with self.lock: + self.in_model = False + if self.m["runner_status"] == "pausing": + self.m["runner_status"] = "paused" + with self.lock: + usage = metadata.get("usage") or {} + if all(type(usage.get(k)) is int and usage[k] >= 0 for k in ("input_tokens", "output_tokens")): + self.m["usage"]["reported_calls"] += 1 + for key in ("input_tokens", "output_tokens"): + self.m["usage"][key] += usage[key] + if call_epoch != self.control_epoch or self.cancel.is_set() or self.m["runner_status"] != "running": + self._event("decision_withheld", outcome="Control changed before model result dispatch") + return False + protocol.fields(action, ("action", "target_x", "target_y", "intent"), "model action") + job["last_intent"] = action["intent"] + if action["action"] == "stop": + self._halt(action["intent"]) + return False + context = {"job_id": job_id, "call_dir": str(call_dir), "action": action, "decision_epoch": call_epoch} + proposal = self._rpc("propose", {"challenge_id": challenge["challenge_id"], **action}, context) + if proposal is None: + return False + if (job["disturbance_test"] and not job["disturbed"] and action["action"] == "measure" and + self._rpc("disturb", {"operation_id": "disturb-" + uuid.uuid4().hex, "force_x_n": -1.5, "steps": 80}, context) is None): + return False + result = self._rpc("dispatch", {"proposal_id": proposal["proposal_id"]}, context) + return result is not None and self.m["runner_status"] == "running" diff --git a/tools/physical-workcell/operator_runtime.py b/tools/physical-workcell/operator_runtime.py new file mode 100644 index 0000000..54ec920 --- /dev/null +++ b/tools/physical-workcell/operator_runtime.py @@ -0,0 +1,384 @@ +"""Serialized, durable model host for a local simulated bench operator.""" +import copy +import threading +import time +import uuid +from pathlib import Path + +import agent_bench as agent +import bench +import measurement +import order_protocol as protocol + +BASE = Path(__file__).resolve().parent +MAX_CALLS = 32 +MAX_TOTAL_CALLS = 96 +ZONES = {"A": {"x_min": .035, "x_max": .105, "y_min": -.025, "y_max": .025}, + "B": {"x_min": .045, "x_max": .115, "y_min": -.025, "y_max": .025}} + + +def sources(): + return {**agent.sources(), **{name: protocol.sha(protocol.read_bytes(BASE / name)) + for name in ("operator_runtime.py", "bench_operator.py", "codex_policy.py", "bench_operator.html", + "planner_guard.py", "codex-policy.md", "codex-action.schema.json")}} + + +def initialize(root): + root = protocol.directory(root, create=True) + with protocol.locked(root): + if any(p.name != "app.lock" for p in root.iterdir()): + raise protocol.Refusal("operator initialization requires a new empty directory") + source = sources() + status = bench.initialize(root / "bench", diagnostic_enabled=True) + agent.initialize(root / "agent", root / "bench") + (root / "calls").mkdir(mode=0o700) + manifest = {"version": 1, "app_id": uuid.uuid4().hex, "bench_id": status["bench_id"], + "source_fingerprints": source, "opened": False, "runner_status": "running", + "active_job_id": None, "jobs": [], "activity": [], "sequence": 0, + "frame": None, "usage": {"calls": 0, "input_tokens": 0, "output_tokens": 0, "reported_calls": 0}, + "reason": "Ready for a simulated job"} + protocol.create_file(root / "identity.json", protocol.encoded( + {"app_id": manifest["app_id"], "bench_id": manifest["bench_id"], "source_fingerprints": source})) + protocol.persist(root / "operator.json", manifest) + return manifest["app_id"] + + +def validate_job(value): + protocol.fields(value, ("job_id", "label", "target", "disturbance_test"), "job") + protocol.identity(value["job_id"]) + if (not isinstance(value["label"], str) or not 1 <= len(value["label"].strip()) <= 120 or + value["target"] not in ZONES or type(value["disturbance_test"]) is not bool): + raise protocol.Refusal("job needs a short label, target A or B, and a boolean disturbance flag") + if value["disturbance_test"] and value["target"] != "B": + raise protocol.Refusal("the disturbance trial is limited to target B") + + +class Operator: + def __init__(self, root, policy, *, start_worker=True): + self.root = protocol.directory(root) + self.lock = threading.RLock() + self.wake = threading.Event() + self.cancel = threading.Event() + self.closing = False + self.in_model = False + self.control_epoch = 0 + self.saved_digest = None + self.policy = policy + self.owner = protocol.locked(self.root) + self.owner.__enter__() + try: + self.m = protocol.decode(protocol.read_bytes(self.root / "operator.json")) + self._identity() + self.body = bench.Bench(self.root / "bench") + self.agent = agent.AgentBench(self.root / "agent") + health_error = None + try: + self._healthy() + except Exception as exc: # noqa: BLE001 - preserve unknown startup evidence. + health_error = str(exc) + if self.m["opened"]: + self.m["runner_status"] = "paused" + self.m["reason"] = "Restarted; inspect retained state and explicitly resume" + for job in self.m["jobs"]: + if job["status"] == "running": + job["status"] = "paused" + job["error"] = "Restarted during an unfinished job; no action replayed" + if health_error: + self.m.update(runner_status="blocked", reason=health_error) + for job in self.m["jobs"]: + if job["id"] == self.m["active_job_id"]: + job.update(status="unknown", error=health_error) + self.m["opened"] = True + if self.m["frame"] is None and not health_error: + self._cache(self.agent.observe()) + self._save() + self.worker = threading.Thread(target=self._loop, name="bench-operator", daemon=True) + if start_worker: + self.worker.start() + self.wake.set() + except Exception: + self.owner.__exit__(None, None, None) + raise + + def _identity(self): + if self.saved_digest and protocol.sha(protocol.read_bytes(self.root / "operator.json")) != self.saved_digest: + raise protocol.Refusal("retained operator state changed outside its owner") + identity = protocol.decode(protocol.read_bytes(self.root / "identity.json")) + if identity != {key: self.m[key] for key in ("app_id", "bench_id", "source_fingerprints")}: + raise protocol.Refusal("persistent operator identity changed") + protocol.fields(self.m, ("version", "app_id", "bench_id", "source_fingerprints", "opened", + "runner_status", "active_job_id", "jobs", "activity", "sequence", "frame", "usage", "reason"), "operator manifest") + if (type(self.m["version"]) is not int or self.m["version"] != 1 or + not isinstance(self.m["app_id"], str) or not protocol.UUID.fullmatch(self.m["app_id"]) or + self.m["source_fingerprints"] != sources()): + raise protocol.Refusal("operator identity or measured source changed") + if not isinstance(self.m["jobs"], list) or len(self.m["jobs"]) > 6: + raise protocol.Refusal("operator job budget exceeded") + + def _healthy(self, allow_completed=False): + self._identity() + state = self.agent.status() + if state["bench_id"] != self.m["bench_id"] or state["bench"]["state"] != "ready" or state["unresolved_proposals"]: + raise protocol.Refusal("native or agent outcome is unknown; operator intervention required") + active = state["bench"]["active_job"] + expected = self.m["active_job_id"] + if ((active is not None and active["job_id"] != expected) or + (expected is not None and active is None and + (not allow_completed or expected not in state["bench"]["jobs"]))): + raise protocol.Refusal("active physical job differs from the operator job") + for job in self.m["jobs"]: + if job["status"] == "completed": + completed = state["bench"]["jobs"].get(job["id"]) + if (not completed or completed["phase"] != "completed" or + completed["reset_receipt"].get("block_home_verified") is not True or + completed["reset_receipt"].get("pusher_home_verified") is not True or + job["measurement"] != agent.public_response(completed["measurement_receipt"]["measurement"])): + raise protocol.Refusal("displayed completion lacks retained native and computer proof") + return state["bench"] + + def _save(self): + protocol.persist(self.root / "operator.json", self.m) + self.saved_digest = protocol.sha(protocol.encoded(self.m)) + + def _event(self, kind, intent="", outcome=""): + self.m["sequence"] += 1 + value = {"seq": self.m["sequence"], "time": time.time(), "kind": kind, + "intent": str(intent)[:2000], "outcome": str(outcome)[:1000]} + protocol.create_file(self.root / "calls" / f"event-{value['seq']:06d}.json", protocol.encoded(value)) + self.m["activity"] = (self.m["activity"] + [value])[-24:] + self._save() + + def _cache(self, challenge): + packet = challenge["observation"] + path = Path(challenge["image_path"]) + if path.parent != self.root / "bench" / "images": + raise protocol.Refusal("camera frame is not bench-owned") + if protocol.sha(protocol.read_bytes(path, maximum=8 * 1024 * 1024)) != packet["image_sha256"]: + raise protocol.Refusal("camera frame bytes changed") + self.m["frame"] = {"path": str(path), "sha256": packet["image_sha256"], "generation": packet["generation"]} + + def state(self): + with self.lock: + self._identity() + try: + self._healthy() + except Exception as exc: # noqa: BLE001 - report changed native evidence as blocked. + if self.m["runner_status"] != "blocked": + self._halt(str(exc), "blocked") + frame = self.m["frame"] + jobs = [{("job_id" if key == "id" else key): value for key, value in job.items() + if key not in {"history", "disturbed"}} for job in self.m["jobs"]] + return copy.deepcopy({"app_id": self.m["app_id"], "simulation": True, + "runner_status": self.m["runner_status"], "jobs": jobs, + "model_request_pending": self.in_model, + "active_job_id": self.m["active_job_id"], "activity": self.m["activity"], + "usage": {**self.m["usage"], "token_usage_complete": self.m["usage"]["reported_calls"] == self.m["usage"]["calls"]}, "readiness": {"ready": self.m["runner_status"] == "running", "reason": self.m["reason"]}, + "current_frame": {"generation": frame["generation"] if frame else None, + "image_url": f"/api/frame?generation={frame['generation']}&sha256={frame['sha256']}" if frame else None, + "available": frame is not None}}) + + def frame(self, requested_url=None): + with self.lock: + self._identity() + frame = self.m["frame"] + if not frame or Path(frame["path"]).parent != self.root / "bench" / "images": + raise protocol.Refusal("no owned camera frame") + expected_url = f"/api/frame?generation={frame['generation']}&sha256={frame['sha256']}" + if requested_url is not None and requested_url != expected_url: + raise protocol.Refusal("requested capture is no longer the current cached image") + data = protocol.read_bytes(Path(frame["path"]), maximum=8 * 1024 * 1024) + if protocol.sha(data) != frame["sha256"]: + raise protocol.Refusal("cached camera frame changed") + return data + + def enqueue(self, request): + validate_job(request) + with self.lock: + self._identity() + if self.closing or len(self.m["jobs"]) >= 6 or any(j["id"] == request["job_id"] for j in self.m["jobs"]): + raise protocol.Refusal("job identity reused or operator queue budget exhausted") + self.m["jobs"].append({"id": request["job_id"], "label": request["label"].strip(), + "target": request["target"], "disturbance_test": request["disturbance_test"], + "disturbed": False, "status": "queued", "phase": None, "call_count": 0, + "last_intent": "", "measurement": None, "error": None, "history": []}) + self._event("queued", request["label"], request["job_id"]) + self.wake.set() + return self.state() + + def pause(self): + # Set immediately, even if a native action currently owns the action-boundary lock. + self.cancel.set() + with self.lock: + self.control_epoch += 1 + if self.m["runner_status"] != "blocked": + self.m["runner_status"] = "pausing" if self.in_model else "paused" + self.m["reason"] = "Paused; an in-flight native action may finish before the next boundary" + for job in self.m["jobs"]: + if job["id"] == self.m["active_job_id"] and job["status"] == "running": + job["status"] = "paused" + self._event("pause", outcome=self.m["reason"]) + return self.state() + + def resume(self): + with self.lock: + if self.closing or self.cancel.is_set() and self.in_model: + raise protocol.Refusal("wait for the cancelled model call to exit before resuming") + try: + self._healthy() + except Exception as exc: + self.m.update(runner_status="blocked", reason=str(exc)) + self._save() + raise + self.cancel.clear() + self.m.update(runner_status="running", reason="Ready for the next action boundary") + self._event("resume") + self.wake.set() + return self.state() + + def close(self): + self.cancel.set() + with self.lock: + self.control_epoch += 1 + self.closing = True + self.m.update(runner_status="pausing", reason="Stopping; waiting for the owned worker to exit") + self._save() + self.wake.set() + if self.worker.is_alive(): + self.worker.join(timeout=35) + if self.worker.is_alive(): + raise protocol.Refusal("owned worker has not exited; ownership lock retained") + with self.lock: + self.m.update(runner_status="stopped", reason="Operator stopped; retained bench has not been reset") + self._save() + self.owner.__exit__(None, None, None) + + def _loop(self): + while not self.closing: + self.wake.wait(.5) + self.wake.clear() + while not self.closing: + with self.lock: + if self.m["runner_status"] != "running" or self.cancel.is_set(): + break + try: + if not self._step(): + break + except Exception as exc: # noqa: BLE001 - retain model and native failures. + with self.lock: + if self.closing: + self._event("shutdown_model_exit", outcome=str(exc)) + break + try: + self._healthy() + state = "paused" + except Exception: # noqa: BLE001 - uncertainty cannot authorize resume. + state = "blocked" + self._halt(str(exc), state) + + def _halt(self, reason, state="paused"): + self.m.update(runner_status=state, reason=str(reason)) + job = next((j for j in self.m["jobs"] if j["id"] == self.m["active_job_id"]), None) + if state == "blocked": + for previous in self.m["jobs"]: + if previous["status"] == "completed": + previous.update(status="unknown", error="Retained completion evidence could not be validated") + if job: + job.update(status="unknown" if state == "blocked" else "paused", error=str(reason)) + self._event("halt", outcome=reason) + + def _step(self): + with self.lock: + if self.closing or self.m["runner_status"] != "running" or self.cancel.is_set(): + return False + self._healthy() + if self.m["usage"]["calls"] >= MAX_TOTAL_CALLS: + self._halt("Operator lifetime model-call budget exhausted") + return False + job = next((j for j in self.m["jobs"] if j["id"] == self.m["active_job_id"]), None) + if job is None: + job = next((j for j in self.m["jobs"] if j["status"] == "queued"), None) + if job is None: + return False + # Durable start intention precedes changing the native bench. + self.m["active_job_id"] = job["id"] + job["status"] = "running" + self._event("start_intent", job["label"], job["id"]) + self.body.start_job(job["id"], ZONES[job["target"]]) + if job["call_count"] >= MAX_CALLS: + self._halt("Model call budget exhausted") + return False + challenge = agent.public_response(self.agent.observe()) + self._cache(challenge) + job.update(status="running", phase=challenge["observation"]["phase"], error=None) + job["call_count"] += 1 + self.m["usage"]["calls"] += 1 + call_dir = self.root / "calls" / f"{job['id']}-{job['call_count']:02d}" + call_dir.mkdir(mode=0o700) + protocol.create_file(call_dir / "reservation.json", protocol.encoded({"job_id": job["id"], + "call": job["call_count"], "challenge": challenge, "history": job["history"][-6:]})) + self._event("inference", job["label"], f"Model call {job['call_count']} reserved") + self.in_model = True + call_epoch = self.control_epoch + try: + action, metadata = self.policy(challenge, copy.deepcopy(job["history"][-6:]), call_dir, self.cancel) + protocol.create_file(call_dir / "decision.json", protocol.encoded({"action": action, "metadata": metadata})) + finally: + with self.lock: + self.in_model = False + with self.lock: + usage = metadata.get("usage") + if isinstance(usage, dict) and all(type(usage.get(key)) is int and usage[key] >= 0 for key in ("input_tokens", "output_tokens")): + self.m["usage"]["reported_calls"] += 1 + for key in ("input_tokens", "output_tokens"): + tokens = (metadata.get("usage") or {}).get(key, 0) + if type(tokens) is int and tokens >= 0: + self.m["usage"][key] += tokens + if (call_epoch != self.control_epoch or self.cancel.is_set() or + self.m["runner_status"] != "running" or self.closing): + if self.m["runner_status"] == "pausing": + self.m["runner_status"] = "paused" + self._event("decision_withheld", outcome="Paused model reply was not dispatched") + return False + self._healthy() + protocol.fields(action, ("action", "target_x", "target_y", "intent"), "model action") + job["last_intent"] = action["intent"] + if action["action"] == "stop": + self._halt(action["intent"]) + return False + proposal = self.agent.propose(challenge["challenge_id"], action["action"], action["intent"], + action["target_x"], action["target_y"]) + protocol.create_file(call_dir / "proposal.json", protocol.encoded(proposal)) + if job["disturbance_test"] and not job["disturbed"] and action["action"] == "measure": + job["disturbed"] = True + self._event("disturbance_intent", outcome="Bounded external-force test before saved proposal dispatch") + self.body.disturb("disturb-" + uuid.uuid4().hex, -1.5, 80) + result = agent.public_response(self.agent.dispatch(proposal["proposal_id"])) + protocol.create_file(call_dir / "dispatch.json", protocol.encoded(result)) + job["history"].append({"action": action, "outcome": result["outcome"], "reason": result.get("reason", "")}) + job["history"] = job["history"][-6:] + self._event("dispatch", action["intent"], result["outcome"] + ": " + result.get("reason", action["action"])) + if result["outcome"] == "unknown" or result.get("next_observation") is None: + self._halt(result.get("reason", "Unknown outcome or observation unavailable"), "blocked") + return False + self._cache(result["next_observation"]) + status = self._healthy(allow_completed=action["action"] == "finish_reset" and result["outcome"] == "executed") + active = status["active_job"] + if active: + job["phase"] = active["phase"] + if "measurement_receipt" in active: + job["measurement"] = agent.public_response(active["measurement_receipt"]["measurement"]) + if action["action"] == "finish_reset" and result["outcome"] == "executed": + completed = status["jobs"].get(job["id"]) + receipt = result.get("receipt", {}) + if (not completed or completed["phase"] != "completed" or active is not None or + receipt != completed["reset_receipt"] or receipt.get("job_id") != job["id"] or + receipt.get("block_home_verified") is not True or receipt.get("pusher_home_verified") is not True): + raise protocol.Refusal("completion is missing independent bench reset evidence") + saved = measurement.read_result(self.root / "bench", completed["measurement_operation_id"]) + if saved != completed["measurement_receipt"]: + raise protocol.Refusal("completion is missing the durable computer measurement") + job.update(status="completed", phase="completed", measurement=agent.public_response(saved["measurement"]), error=None) + self.m["active_job_id"] = None + self._event("completed", job["label"], "Saved measurement and contact reset verified") + self._save() + return True diff --git a/tools/physical-workcell/order_app.html b/tools/physical-workcell/order_app.html new file mode 100644 index 0000000..dfecf15 --- /dev/null +++ b/tools/physical-workcell/order_app.html @@ -0,0 +1,212 @@ + + + + + +Work orders · Footwork + + + +
    +

    Work orders

    Bind an order, stage the part, then verify fulfillment.

    MuJoCo simulation
    +
    +
    +

    Loading order…

    +

    +

    +

    Requested staging region

    +
    +
    + + +
    +
    +
    +

    Workcell

    Actual rendered RGB
    +

    Bind the current order to connect its workcell.

    +

    No current physical evidence.

    +

    Orange: part · Blue: pusher · Green: staging region

    +

    Physical work is performed through the bound handoff tools. Verification checks the current controller evidence and saved artifact before updating this order.

    +
    +
    +
    Local diagnostic · Changes and cancellation advance the order revision. A stale lease cannot fulfill it.
    +
    + + + diff --git a/tools/physical-workcell/order_app.py b/tools/physical-workcell/order_app.py new file mode 100644 index 0000000..5cea4af --- /dev/null +++ b/tools/physical-workcell/order_app.py @@ -0,0 +1,549 @@ +"""Loopback browser orders with SQLite revision leases and independently checked fulfillment.""" + +import argparse +import contextlib +import importlib.util +import json +import os +import signal +import sqlite3 +import stat +import sys +import threading +import time +import uuid +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from urllib.parse import unquote, urlsplit + +BASE = Path(__file__).resolve().parent +sys.path.insert(0, str(BASE)) +import order_protocol as protocol + +Refusal = protocol.Refusal +DB_NAME = "orders.sqlite3" +SOURCES = ("order_app.py", "order_protocol.py", "order_app.html", "controller.py", "episode_runner.py", + "workcell.py", "image_guided.py", "episode_cases.py", "proposal.schema.json", "scene.xml") + + +def fingerprints(): + return {name: protocol.sha(protocol.read_bytes(BASE / name)) for name in SOURCES} + + +def work_order(order): + return {key: order[key] for key in ("version", "order_id", "project", "part_id", "target_zone")} + + +def sqlite_file(path): + info = path.lstat() + if not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid() or info.st_mode & 0o077: + raise Refusal("application store must be an owned private regular file") + return [info.st_dev, info.st_ino] + + +def initialize(app_dir, order_file, order_sha256): + if not isinstance(order_sha256, str) or not protocol.DIGEST.fullmatch(order_sha256): + raise Refusal("order SHA256 must be exact lowercase hexadecimal") + content = protocol.read_bytes(Path(order_file)) + if protocol.sha(content) != order_sha256: + raise Refusal("order input differs from its bound digest") + order = protocol.validate_order(protocol.decode(content)) + if order["state"] == "fulfilled": + raise Refusal("a seeded order cannot invent a fulfillment receipt") + binding_path = app_dir / "app-binding.json" + if binding_path.exists() or binding_path.is_symlink(): + store = Store(app_dir) + if store.binding["seed_sha256"] != order_sha256: + raise Refusal("application already belongs to a different seed order") + return {"app_id": store.app_id, "app_dir": str(app_dir), "phase": "initialized", "order": store.order(order["order_id"])} + if any(path.name != "app.lock" for path in app_dir.iterdir()): + raise Refusal("unreceipted app files exist; implicit initialization/reset is forbidden") + app_id, store_id = uuid.uuid4().hex, uuid.uuid4().hex + protocol.create_file(app_dir / "seed-order.json", content) + database = app_dir / DB_NAME + protocol.create_file(database, b"") + binding = {"version": 1, "app_id": app_id, "store_id": store_id, "app_dir": str(app_dir), + "database": DB_NAME, "database_identity": sqlite_file(database), "seed_sha256": order_sha256, + "source_fingerprints": fingerprints()} + with contextlib.closing(sqlite3.connect(database)) as connection: + connection.execute("PRAGMA synchronous=FULL") + connection.executescript(""" + CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT NOT NULL); + CREATE TABLE orders (order_id TEXT PRIMARY KEY, revision INTEGER NOT NULL, + state TEXT NOT NULL, work_order_json TEXT NOT NULL); + CREATE TABLE leases (lease_id TEXT PRIMARY KEY, order_id TEXT NOT NULL, + revision INTEGER NOT NULL, lease_json TEXT NOT NULL); + CREATE UNIQUE INDEX one_lease_per_revision ON leases(order_id, revision); + CREATE TABLE operations (operation_id TEXT PRIMARY KEY, kind TEXT NOT NULL, + order_id TEXT NOT NULL, lease_id TEXT NOT NULL, + request_json TEXT NOT NULL, receipt_json TEXT NOT NULL); + """) + connection.execute("INSERT INTO meta VALUES ('binding', ?)", (protocol.encoded(binding).decode(),)) + connection.execute("INSERT INTO orders VALUES (?,?,?,?)", ( + order["order_id"], order["revision"], order["state"], protocol.encoded(work_order(order)).decode())) + connection.commit() + protocol.sync_directory(app_dir) + protocol.create_file(binding_path, protocol.encoded(binding)) + protocol.directory(app_dir / "workcells", create=True) + return {"app_id": app_id, "app_dir": str(app_dir), "phase": "initialized", "order": order} + + +class Store: + def __init__(self, app_dir): + self.app_dir = app_dir + self.binding = protocol.decode(protocol.read_bytes(app_dir / "app-binding.json")) + protocol.fields(self.binding, ("version", "app_id", "store_id", "app_dir", "database", "database_identity", + "seed_sha256", "source_fingerprints"), "application binding") + if (self.binding["version"] != 1 or self.binding["app_dir"] != str(app_dir) or + self.binding["database"] != DB_NAME or not protocol.UUID.fullmatch(self.binding["app_id"]) or + not protocol.UUID.fullmatch(self.binding["store_id"])): + raise Refusal("application identity or store binding is invalid") + self.app_id = self.binding["app_id"] + self.database = app_dir / DB_NAME + self.validate_current() + with self.connection() as connection: + if connection.execute("PRAGMA quick_check").fetchone()[0] != "ok": + raise Refusal("application SQLite store failed integrity check") + + @contextlib.contextmanager + def connection(self): + sqlite_file(self.database) + connection = sqlite3.connect(self.database, timeout=2, isolation_level=None) + connection.row_factory = sqlite3.Row + try: + connection.execute("PRAGMA synchronous=FULL") + yield connection + finally: + connection.close() + + @contextlib.contextmanager + def transaction(self): + self.validate_current() + with self.connection() as connection: + connection.execute("BEGIN IMMEDIATE") + try: + yield connection + connection.commit() + except BaseException: + connection.rollback() + raise + + def validate_current(self): + if (protocol.decode(protocol.read_bytes(self.app_dir / "app-binding.json")) != self.binding or + self.binding["source_fingerprints"] != fingerprints() or + self.binding["database_identity"] != sqlite_file(self.app_dir / DB_NAME) or + protocol.sha(protocol.read_bytes(self.app_dir / "seed-order.json")) != self.binding["seed_sha256"]): + raise Refusal("application source, seed or store binding changed") + with self.connection() as connection: + row = connection.execute("SELECT value FROM meta WHERE key='binding'").fetchone() + if row is None or protocol.decode(row[0].encode()) != self.binding: + raise Refusal("SQLite application identity does not match its owned store") + origin = connection.execute("SELECT value FROM meta WHERE key='origin'").fetchone() + origin_path = self.app_dir / "origin.json" + if ((origin is not None or origin_path.exists() or origin_path.is_symlink()) and + (origin is None or protocol.decode(protocol.read_bytes(origin_path)) != { + "app_id": self.app_id, "origin": origin[0]})): + raise Refusal("application origin identity changed") + + def origin(self): + with self.connection() as connection: + row = connection.execute("SELECT value FROM meta WHERE key='origin'").fetchone() + return row[0] if row else None + + def bind_origin(self, origin): + prior = self.origin() + if prior is not None: + if prior != origin: + raise Refusal("application restart must preserve its exact loopback origin") + return + with self.transaction() as connection: + protocol.create_file(self.app_dir / "origin.json", protocol.encoded({"app_id": self.app_id, "origin": origin})) + connection.execute("INSERT INTO meta VALUES ('origin', ?)", (origin,)) + + def order(self, order_id, connection=None): + protocol.identity(order_id) + if connection is None: + with self.connection() as opened: + return self.order(order_id, opened) + row = connection.execute("SELECT * FROM orders WHERE order_id=?", (order_id,)).fetchone() + if row is None: + raise LookupError("order identity is unavailable") + order = protocol.decode(row["work_order_json"].encode()) + order.update(revision=row["revision"], state=row["state"]) + return protocol.validate_order(order) + + def lease(self, lease_id, connection=None): + if not isinstance(lease_id, str) or not protocol.UUID.fullmatch(lease_id): + raise Refusal("lease identity must be an exact UUID") + if connection is None: + with self.connection() as opened: + return self.lease(lease_id, opened) + row = connection.execute("SELECT * FROM leases WHERE lease_id=?", (lease_id,)).fetchone() + if row is None: + raise LookupError("lease identity is unavailable") + lease = protocol.decode(row["lease_json"].encode()) + protocol.fields(lease, ("app_id", "lease_id", "order_id", "expected_revision", "work_order", "work_order_sha256", + "binding_operation_id", "physical_completion_id", "completion_operation_id", "origin", + "app_dir", "workspace_dir", "run_dir", "controller_dir", "handoff_dir"), "retained lease") + expected_paths = protocol.paths_for(self.app_dir, lease_id) + if (lease["app_id"] != self.app_id or lease["lease_id"] != lease_id or + lease["order_id"] != row["order_id"] or lease["expected_revision"] != row["revision"] or + lease["work_order"]["order_id"] != row["order_id"] or + any(lease[key] != value for key, value in expected_paths.items()) or + lease["origin"] != self.origin() or + protocol.sha(protocol.encoded(protocol.validate_work_order(lease["work_order"]))) != lease["work_order_sha256"]): + raise Refusal("retained lease identity, origin, order or assigned paths changed") + return lease + + def operation(self, operation_id, connection=None): + protocol.identity(operation_id) + if connection is None: + with self.connection() as opened: + return self.operation(operation_id, opened) + row = connection.execute("SELECT * FROM operations WHERE operation_id=?", (operation_id,)).fetchone() + if row is None: + raise LookupError("application operation is unavailable") + return row + + def bind(self, order_id, request): + protocol.fields(request, ("expected_revision", "binding_operation_id"), "bind request") + protocol.revision(request["expected_revision"]) + operation_id = protocol.identity(request["binding_operation_id"]) + exact = {"order_id": order_id, **request} + with self.transaction() as connection: + previous = connection.execute("SELECT * FROM operations WHERE operation_id=?", (operation_id,)).fetchone() + if previous: + if previous["kind"] != "bind" or protocol.decode(previous["request_json"].encode()) != exact: + raise Refusal("binding operation ID belongs to different arguments") + return {"app_id": self.app_id, "lease": self.lease(previous["lease_id"], connection), + "order": self.order(order_id, connection), "historical": True} + order = self.order(order_id, connection) + if order["state"] != "open" or order["revision"] != request["expected_revision"]: + raise Refusal("only the exact current open revision can be bound") + if connection.execute("SELECT 1 FROM leases WHERE order_id=? AND revision=?", (order_id, order["revision"])).fetchone(): + raise Refusal("this order revision already has a lease; reuse its original binding operation") + lease_id = uuid.uuid4().hex + snapshot = work_order(order) + lease = {"app_id": self.app_id, "lease_id": lease_id, "order_id": order_id, + "expected_revision": order["revision"], "work_order": snapshot, + "work_order_sha256": protocol.sha(protocol.encoded(snapshot)), + "binding_operation_id": operation_id, "physical_completion_id": "stage-" + uuid.uuid4().hex, + "completion_operation_id": "fulfill-" + uuid.uuid4().hex, "origin": self.origin(), + **protocol.paths_for(self.app_dir, lease_id)} + protocol.directory(Path(lease["workspace_dir"]), create=True) + connection.execute("INSERT INTO leases VALUES (?,?,?,?)", ( + lease_id, order_id, order["revision"], protocol.encoded(lease).decode())) + connection.execute("INSERT INTO operations VALUES (?,?,?,?,?,?)", ( + operation_id, "bind", order_id, lease_id, protocol.encoded(exact).decode(), protocol.encoded(lease).decode())) + return {"app_id": self.app_id, "lease": lease, "order": order, "historical": False} + + def mutate(self, order_id, request, cancel=False): + protocol.fields(request, ("expected_revision",) if cancel else ("expected_revision", "target_zone"), "order mutation") + protocol.revision(request["expected_revision"]) + if not cancel: + protocol.validate_zone(request["target_zone"]) + with self.transaction() as connection: + order = self.order(order_id, connection) + if order["state"] != "open" or order["revision"] != request["expected_revision"]: + raise Refusal("order changed or is no longer open") + order["revision"] = protocol.revision(order["revision"] + 1) + if cancel: + order["state"] = "cancelled" + else: + order["target_zone"] = request["target_zone"] + connection.execute("UPDATE orders SET revision=?,state=?,work_order_json=? WHERE order_id=?", ( + order["revision"], order["state"], protocol.encoded(work_order(order)).decode(), order_id)) + return {"app_id": self.app_id, "order": order} + + def fulfilled_repeat(self, row, exact, connection): + if row["kind"] != "fulfill" or protocol.decode(row["request_json"].encode()) != exact: + raise Refusal("completion operation ID belongs to different arguments") + return {"app_id": self.app_id, "receipt": protocol.decode(row["receipt_json"].encode()), + "current_order": self.order(row["order_id"], connection), "historical": True} + + def fulfill(self, order_id, request): + protocol.fields(request, ("lease_id", "completion_operation_id"), "fulfillment request") + operation_id = protocol.identity(request["completion_operation_id"]) + exact = {"order_id": order_id, **request} + # Reconcile a committed reply before consulting possibly unavailable physical evidence. + with self.connection() as connection: + previous = connection.execute("SELECT * FROM operations WHERE operation_id=?", (operation_id,)).fetchone() + if previous: + return self.fulfilled_repeat(previous, exact, connection) + lease = self.lease(request["lease_id"], connection) + order = self.order(order_id, connection) + if (lease["order_id"] != order_id or lease["completion_operation_id"] != operation_id or + order["state"] != "open" or order["revision"] != lease["expected_revision"]): + raise Refusal("completion must reference the assigned operation and unchanged open leased order") + proof = verify_physical(lease) + with self.transaction() as connection: + previous = connection.execute("SELECT * FROM operations WHERE operation_id=?", (operation_id,)).fetchone() + if previous: + return self.fulfilled_repeat(previous, exact, connection) + current_lease = self.lease(request["lease_id"], connection) + current = self.order(order_id, connection) + if (current_lease != lease or current["state"] != "open" or + current["revision"] != lease["expected_revision"] or work_order(current) != lease["work_order"]): + raise Refusal("order or lease changed after physical verification; fulfillment refused") + new_revision = protocol.revision(current["revision"] + 1) + changed = connection.execute("UPDATE orders SET state='fulfilled', revision=? WHERE order_id=? AND revision=? AND state='open'", + (new_revision, order_id, lease["expected_revision"])).rowcount + if changed != 1: + raise Refusal("current order no longer matches the leased revision") + receipt = {"app_id": self.app_id, "order_id": order_id, "lease_id": lease["lease_id"], + "completion_operation_id": operation_id, "physical_completion_id": lease["physical_completion_id"], + "leased_revision": lease["expected_revision"], "fulfilled_revision": new_revision, + "work_order_sha256": lease["work_order_sha256"], "state": "fulfilled", + "committed_time_unix": time.time(), "physical_proof": {"historical": True, **proof}} + connection.execute("INSERT INTO operations VALUES (?,?,?,?,?,?)", ( + operation_id, "fulfill", order_id, lease["lease_id"], protocol.encoded(exact).decode(), protocol.encoded(receipt).decode())) + return {"app_id": self.app_id, "receipt": receipt, "current_order": self.order(order_id, connection), "historical": True} + + +def controller_module(): + spec = importlib.util.spec_from_file_location("order_frozen_controller", BASE / "controller.py") + module = importlib.util.module_from_spec(spec) + try: + spec.loader.exec_module(module) + except ImportError as exc: + raise Refusal("pinned physical controller dependencies are unavailable") from exc + return module + + +def controller_query(controller, control, request): + try: + return controller.client(control, request) + except (controller.Refusal, OSError, ValueError, TypeError, KeyError) as exc: + raise Refusal("assigned live controller evidence is unavailable") from exc + + +def controller_observe(lease): + controller = controller_module() + control = protocol.directory(Path(lease["controller_dir"])) + observed = controller_query(controller, control, {"command": "observe"}) + if observed.get("phase") != "ready" or observed.get("current_health") != "ready": + raise Refusal("assigned controller has no current supported observation") + packet = observed["observation"] + expected = {key: lease["work_order"][key] for key in ("order_id", "project", "part_id", "target_zone")} + if (packet["task_intent"] != expected or packet["run_dir"] != lease["run_dir"] or + packet["binding"]["work_order_sha256"] != lease["work_order_sha256"]): + raise Refusal("controller observation does not belong to the leased order") + return controller, observed, packet + + +def verify_physical(lease): + controller = controller_module() + control = protocol.directory(Path(lease["controller_dir"])) + goal = controller_query(controller, control, {"command": "status", "operation_id": lease["physical_completion_id"]}) + if (goal.get("operation_id") != lease["physical_completion_id"] or goal.get("historical_outcome") != "staged" or + goal.get("phase") != "staged" or goal.get("current_health") != "ready" or + not goal.get("current_software_artifact_check", {}).get("ok")): + raise Refusal("assigned physical completion has no staged history and current verified artifact") + _, observed, packet = controller_observe(lease) + receipt = goal["receipt"] + if (receipt["decision"] != "complete" or receipt["token"] != lease["physical_completion_id"] or + receipt["phase"] != "staged" or receipt["image_generation"] != packet["generation"] or + receipt["after_generation"] != packet["generation"] or receipt["after_state_sha256"] != packet["state_sha256"] or + receipt["image_sha256"] != packet["image_sha256"] or packet["perception"]["state"] != "recognized" or + packet["calibration"]["valid"] is not True or not observed.get("current_software_artifact_check", {}).get("ok") or + receipt.get("historical_visual_check", {}).get("inside_with_margin") is not True or + goal["current_incarnation"] != observed["current_incarnation"]): + raise Refusal("current RGB, calibration, controller history or software evidence is unsupported") + content = protocol.read_bytes(Path(lease["run_dir"]) / "order.json") + artifact_sha = protocol.sha(content) + for checked in (goal["current_software_artifact_check"], observed["current_software_artifact_check"]): + if checked.get("expected_sha256") != artifact_sha or checked.get("observed_sha256") != artifact_sha: + raise Refusal("saved physical artifact bytes do not match the live checker") + artifact = protocol.decode(content) + expected = lease["work_order"] + if (artifact.get("source_kind") != "mujoco_simulation" or artifact.get("state") != "staged" or + artifact.get("work_order_sha256") != lease["work_order_sha256"] or + any(artifact.get(key) != expected[key] for key in ("order_id", "project", "part_id", "target_zone")) or + artifact.get("simulator_generation") != packet["generation"] or + artifact.get("simulator_state_sha256") != packet["state_sha256"]): + raise Refusal("saved physical artifact identity differs from the leased task") + return {"source_kind": "mujoco_simulation", "operation_id": lease["physical_completion_id"], + "controller_incarnation": goal["current_incarnation"], "accepted_incarnation": goal["accepted_incarnation"], + "generation": packet["generation"], "state_sha256": packet["state_sha256"], + "image_sha256": packet["image_sha256"], "artifact_sha256": artifact_sha, "verified_time_unix": time.time()} + + +class Server(ThreadingHTTPServer): + daemon_threads = False + block_on_close = True + + def __init__(self, store, port): + super().__init__(("127.0.0.1", port), Handler) + self.store = store + self.origin = f"http://127.0.0.1:{self.server_address[1]}" + self.incarnation = uuid.uuid4().hex + self.timeout = .2 + + +class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def setup(self): + super().setup() + self.connection.settimeout(3) + + def log_message(self, *_): + pass + + def send(self, value, status=200, content_type="application/json; charset=utf-8", extra_headers=None): + content = value if isinstance(value, bytes) else protocol.encoded(value) + self.send_response(status) + self.send_header("Content-Type", content_type) + self.send_header("Content-Length", str(len(content))) + self.send_header("Cache-Control", "no-store") + self.send_header("X-Content-Type-Options", "nosniff") + self.send_header("Content-Security-Policy", "default-src 'self'; img-src 'self' blob:; style-src 'unsafe-inline'; script-src 'unsafe-inline'; frame-ancestors 'none'") + for key, header in (extra_headers or {}).items(): + self.send_header(key, str(header)) + self.end_headers() + self.wfile.write(content) + + def gate(self, writing=False): + expected_host = urlsplit(self.server.origin).netloc + if self.client_address[0] != "127.0.0.1" or self.headers.get_all("Host") != [expected_host]: + raise Refusal("request must target the exact loopback application host") + if writing and self.headers.get_all("Origin") != [self.server.origin]: + raise Refusal("write requires the exact application Origin") + if writing and self.headers.get_all("X-Footwork-App-ID") != [self.server.store.app_id]: + raise Refusal("write requires the exact immutable application identity") + self.server.store.validate_current() + + def route(self): + parsed = urlsplit(self.path) + if parsed.scheme or parsed.netloc or parsed.query or parsed.fragment: + raise Refusal("request path must be an exact local route") + return [unquote(part) for part in parsed.path.split("/") if part] + + def execute(self, writing=False): + try: + self.gate(writing) + route = self.route() + store = self.server.store + if not writing: + if not route: + return self.send(protocol.read_bytes(BASE / "order_app.html"), content_type="text/html; charset=utf-8") + if route == ["api", "health"]: + return self.send({"app_id": store.app_id, "app_dir": str(store.app_dir), "origin": self.server.origin, + "server_incarnation": self.server.incarnation, "phase": "ready", + "source_fingerprints": store.binding["source_fingerprints"]}) + if route == ["api", "orders"]: + with store.connection() as connection: + orders = [store.order(row[0], connection) for row in connection.execute("SELECT order_id FROM orders ORDER BY order_id")] + return self.send({"app_id": store.app_id, "orders": orders}) + if len(route) == 3 and route[:2] == ["api", "orders"]: + return self.send({"app_id": store.app_id, "order": store.order(route[2])}) + if len(route) == 3 and route[:2] == ["api", "leases"]: + return self.send({"app_id": store.app_id, "lease": store.lease(route[2])}) + if len(route) == 4 and route[:2] == ["api", "leases"] and route[3] == "frame": + lease = store.lease(route[2]) + _, _, packet = controller_observe(lease) + expected_path = Path(lease["run_dir"]) / f"vision-frame-{packet['generation']:06d}.png" + if packet["image_path"] != str(expected_path): + raise Refusal("controller frame path differs from its assigned run") + content = protocol.read_bytes(expected_path, maximum=1024 * 1024) + if protocol.sha(content) != packet["image_sha256"] or not content.startswith(b"\x89PNG\r\n\x1a\n"): + raise Refusal("current rendered frame bytes do not match controller evidence") + return self.send(content, content_type="image/png", extra_headers={ + "X-Image-Generation": packet["generation"], "X-Image-SHA256": packet["image_sha256"], + "X-Perception-State": packet["perception"]["state"], + "X-Calibration-Valid": str(packet["calibration"]["valid"]).lower()}) + if len(route) == 3 and route[:2] == ["api", "operations"]: + row = store.operation(route[2]) + return self.send({"app_id": store.app_id, "receipt": protocol.decode(row["receipt_json"].encode()), + "current_order": store.order(row["order_id"]), "historical": True}) + raise LookupError("application route is unavailable") + if (self.headers.get_all("Content-Type") != ["application/json"] or + len(self.headers.get_all("Content-Length") or []) != 1 or self.headers.get("Transfer-Encoding") is not None): + raise Refusal("write requires bounded exact JSON framing") + length = int(self.headers["Content-Length"]) + if not 0 < length <= protocol.MAX_BYTES: + raise Refusal("JSON request size is outside its bound") + content = self.rfile.read(length) + if len(content) != length: + raise Refusal("JSON request ended before its declared length") + request = protocol.decode(content) + if len(route) != 4 or route[:2] != ["api", "orders"]: + raise LookupError("application write route is unavailable") + order_id, action = protocol.identity(route[2]), route[3] + if action == "bind": + result = store.bind(order_id, request) + elif action in {"edit", "cancel"}: + result = store.mutate(order_id, request, cancel=action == "cancel") + elif action == "fulfill": + result = store.fulfill(order_id, request) + else: + raise LookupError("application write route is unavailable") + return self.send(result) + except LookupError as exc: + return self.send({"app_id": self.server.store.app_id, "phase": "unavailable", "reason": str(exc)}, 404) + except (Refusal, OSError, ValueError, KeyError, TypeError, sqlite3.Error) as exc: + reason = str(exc) if isinstance(exc, Refusal) else "application input or current evidence is unavailable" + return self.send({"app_id": self.server.store.app_id, "phase": "refused", "reason": reason}, 409) + + def do_GET(self): + self.execute() + + def do_POST(self): + self.execute(writing=True) + + +def serve(app_dir, port): + if type(port) is not int or not 0 <= port <= 65535: + raise Refusal("port must be a bounded integer") + store = Store(app_dir) + prior = store.origin() + if prior is not None and port != urlsplit(prior).port: + raise Refusal("restart requires the exact prior loopback port; origin changes are unsupported") + with Server(store, port) as server: + store.bind_origin(server.origin) + ready = {"app_id": store.app_id, "app_dir": str(app_dir), "origin": server.origin, "pid": os.getpid(), + "server_incarnation": server.incarnation, "phase": "ready", "source_fingerprints": fingerprints()} + protocol.persist(app_dir / "readiness.json", ready) + stop = threading.Event() + prior_handlers = {sig: signal.signal(sig, lambda *_: stop.set()) for sig in (signal.SIGTERM, signal.SIGINT)} + try: + while not stop.is_set(): + server.handle_request() + finally: + # server_close joins request threads before the app ownership lock can unwind. + server.server_close() + ready.update(phase="stopped", stopped_time_unix=time.time()) + protocol.persist(app_dir / "readiness.json", ready) + for sig, handler in prior_handlers.items(): + signal.signal(sig, handler) + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest="command", required=True) + for command in ("init", "serve"): + child = commands.add_parser(command) + child.add_argument("--app-dir", required=True) + if command == "init": + child.add_argument("--order-file", required=True) + child.add_argument("--order-sha256", required=True) + else: + child.add_argument("--port", type=int, default=0) + args = parser.parse_args(argv) + try: + app_dir = protocol.directory(args.app_dir, create=args.command == "init") + with protocol.locked(app_dir): + if args.command == "init": + result = initialize(app_dir, args.order_file, args.order_sha256) + print(json.dumps(result, sort_keys=True, indent=2)) + else: + serve(app_dir, args.port) + return 0 + except (Refusal, OSError, ValueError, TypeError, KeyError, sqlite3.Error, KeyboardInterrupt) as exc: + reason = str(exc) if isinstance(exc, Refusal) else "application or owned store is unavailable" + print(json.dumps({"phase": "refused", "reason": reason})) + return 2 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/physical-workcell/order_protocol.py b/tools/physical-workcell/order_protocol.py new file mode 100644 index 0000000..00f729f --- /dev/null +++ b/tools/physical-workcell/order_protocol.py @@ -0,0 +1,191 @@ +"""Bounded stdlib schemas and owned paths for the local versioned order app.""" + +import contextlib +import fcntl +import hashlib +import json +import math +import os +import re +import stat +from pathlib import Path + +BASE = Path(__file__).resolve().parent +OUTPUT = BASE / "output" +MAX_BYTES = 64 * 1024 +DIGEST = re.compile(r"[0-9a-f]{64}\Z") +IDENTITY = re.compile(r"[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}\Z") +UUID = re.compile(r"[0-9a-f]{32}\Z") + + +class Refusal(ValueError): + pass + + +def sha(content): + return hashlib.sha256(content).hexdigest() + + +def encoded(value): + return (json.dumps(value, sort_keys=True, indent=2, allow_nan=False) + "\n").encode() + + +def decode(content): + def unique(pairs): + result = {} + for key, value in pairs: + if key in result: + raise Refusal("duplicate JSON key") + result[key] = value + return result + + def nonfinite(_): + raise Refusal("nonfinite JSON value") + + if len(content) > MAX_BYTES: + raise Refusal("JSON exceeds its bounded size") + try: + return json.loads(content, object_pairs_hook=unique, parse_constant=nonfinite) + except (UnicodeError, json.JSONDecodeError) as exc: + raise Refusal("invalid UTF-8 JSON") from exc + + +def fields(value, keys, label): + if not isinstance(value, dict) or set(value) != set(keys): + raise Refusal(f"{label} has invalid fields") + + +def identity(value): + if not isinstance(value, str) or not IDENTITY.fullmatch(value): + raise Refusal("identity must be an exact bounded safe string") + return value + + +def revision(value): + if type(value) is not int or not 1 <= value <= 2147483647: + raise Refusal("revision must be a positive bounded integer") + return value + + +def finite(value): + if type(value) not in (int, float) or not math.isfinite(value): + raise Refusal("target coordinates must be finite numbers") + return float(value) + + +def validate_zone(zone): + fields(zone, ("x_min", "x_max", "y_min", "y_max"), "target zone") + for value in zone.values(): + finite(value) + if not (-.35 <= zone["x_min"] < zone["x_max"] <= .35 and -.2 <= zone["y_min"] < zone["y_max"] <= .2): + raise Refusal("target zone must be a bounded nonempty tabletop rectangle") + return zone + + +def validate_work_order(order): + fields(order, ("version", "order_id", "project", "part_id", "target_zone"), "work order") + if type(order["version"]) is not int or order["version"] != 1 or order["part_id"] != "block": + raise Refusal("only version 1 block work orders are supported") + fields(order["project"], ("namespace", "project_id"), "project identity") + for value in (order["order_id"], *order["project"].values()): + identity(value) + validate_zone(order["target_zone"]) + return order + + +def validate_order(order): + fields(order, ("version", "order_id", "project", "part_id", "target_zone", "revision", "state"), "application order") + validate_work_order({key: value for key, value in order.items() if key not in {"revision", "state"}}) + revision(order["revision"]) + if order["state"] not in {"open", "cancelled", "fulfilled"}: + raise Refusal("application order state is invalid") + return order + + +def directory(path, create=False): + root = Path(os.path.abspath(os.fspath(path))) + if root == OUTPUT or OUTPUT not in root.parents: + raise Refusal("owned directory must be under physical-workcell/output") + current = OUTPUT + for part in (None, *root.relative_to(OUTPUT).parts): + if part is not None: + current /= part + if create: + try: + current.mkdir(mode=0o700) + except FileExistsError: + pass + info = current.lstat() + if not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid() or info.st_mode & 0o077: + raise Refusal("owned directories must be private and free of symlinks") + return root + + +def read_bytes(path, maximum=MAX_BYTES): + try: + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK) + with os.fdopen(fd, "rb") as source: + before = os.fstat(source.fileno()) + if not stat.S_ISREG(before.st_mode) or before.st_size > maximum: + raise Refusal("input must be a bounded regular file") + content = source.read(maximum + 1) + after = os.fstat(source.fileno()) + if len(content) > maximum or (before.st_size, before.st_mtime_ns, before.st_ctime_ns) != ( + after.st_size, after.st_mtime_ns, after.st_ctime_ns): + raise Refusal("input changed while reading") + return content + except OSError as exc: + raise Refusal("input is missing, symlinked or unreadable") from exc + + +def create_file(path, content): + fd = os.open(path, os.O_CREAT | os.O_EXCL | os.O_WRONLY | os.O_NOFOLLOW, 0o600) + with os.fdopen(fd, "wb") as sink: + sink.write(content) + sink.flush() + os.fsync(sink.fileno()) + sync_directory(path.parent) + + +def sync_directory(path): + fd = os.open(path, os.O_RDONLY) + try: + os.fsync(fd) + finally: + os.close(fd) + + +def persist(path, value): + import uuid + + staging = path.with_name("." + path.name + "." + uuid.uuid4().hex) + try: + create_file(staging, encoded(value)) + os.replace(staging, path) + sync_directory(path.parent) + finally: + with contextlib.suppress(FileNotFoundError): + staging.unlink() + + +@contextlib.contextmanager +def locked(root): + fd = os.open(root / "app.lock", os.O_RDWR | os.O_CREAT | os.O_NOFOLLOW, 0o600) + try: + if not stat.S_ISREG(os.fstat(fd).st_mode): + raise Refusal("invalid application lock") + try: + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError as exc: + raise Refusal("another process owns this application directory") from exc + yield + finally: + os.close(fd) + + +def paths_for(app_dir, lease_id): + if not isinstance(lease_id, str) or not UUID.fullmatch(lease_id): + raise Refusal("lease identity must be an exact UUID") + workspace = app_dir / "workcells" / lease_id + return {"app_dir": str(app_dir), "workspace_dir": str(workspace), "run_dir": str(workspace / "r"), + "controller_dir": str(workspace / "c"), "handoff_dir": str(workspace / "h")} diff --git a/tools/physical-workcell/persistent_body_client.py b/tools/physical-workcell/persistent_body_client.py new file mode 100644 index 0000000..087366e --- /dev/null +++ b/tools/physical-workcell/persistent_body_client.py @@ -0,0 +1,365 @@ +"""Reusable durable single-send transport for a pinned loopback simulated body. + +PersistentBodyClient returns RPC results, except reconcile() returns the historical envelope. +A pending command blocks new commands and lease rotation. Request files and send +intent are durable before HTTP; reconciliation only issues GET and never replays. +""" +import fcntl +import http.client +import json +import os +import stat +import threading +import uuid +from urllib.parse import urlencode, urlsplit + +import body_contract as wire +import order_protocol as protocol + +PROTOCOL = wire.PROTOCOL +MAX_CLIENT_JOURNAL = 128 * 1024 +IDENTITY_KEYS = wire.IDENTITY_FIELDS +ENVELOPE_KEYS = wire.ENVELOPE_FIELDS +Refusal = protocol.Refusal + + +class Uncertain(Refusal): + def __init__(self, command_id, reason="command outcome is unknown"): + self.command_id = command_id + super().__init__(reason) + + +def endpoint(origin): + if not isinstance(origin, str): + raise Refusal("body endpoint must be text") + parsed = urlsplit(origin) + try: + port = parsed.port + except ValueError as exc: + raise Refusal("invalid loopback endpoint") from exc + if (parsed.scheme != "http" or parsed.hostname != "127.0.0.1" or not port or + parsed.username is not None or parsed.password is not None or parsed.path or parsed.query or parsed.fragment or + origin != f"http://127.0.0.1:{port}"): + raise Refusal("body endpoint must be an exact literal IPv4 loopback HTTP origin") + return "127.0.0.1", port + + +def identity(value): + return wire.validate_identity(value) + + +def pinned_headers(value): + return {"X-Body-Id": value["body_id"], "X-Adapter-Id": value["adapter_id"], + "X-Source-Sha256": value["source_sha256"]} + + +def http_call(origin, method, path, headers=None, content=None, timeout=15, maximum=protocol.MAX_BYTES): + host, port = endpoint(origin) + connection = http.client.HTTPConnection(host, port, timeout=timeout) + try: + request_headers = dict(headers or {}) + if content is not None: + request_headers["Content-Type"] = "application/json" + connection.request(method, path, body=content, headers=request_headers) + response = connection.getresponse() + length = response.getheader("Content-Length") + if length is not None and (not length.isdigit() or int(length) > maximum): + raise Refusal("body response exceeds its bounded size") + body = response.read(maximum + 1) + if len(body) > maximum: + raise Refusal("body response exceeds its bounded size") + if 300 <= response.status < 400: + raise Refusal("body redirects are refused") + return response.status, body + finally: + connection.close() + + +def read_journal(path): + """The bounded local index holds 256 records; wire JSON keeps its 64 KiB cap.""" + content = protocol.read_bytes(path, maximum=MAX_CLIENT_JOURNAL) + + def unique(pairs): + result = {} + for key, value in pairs: + if key in result: + raise Refusal("duplicate local client journal key") + result[key] = value + return result + + def nonfinite(_): + raise Refusal("nonfinite local client journal value") + + try: + return json.loads(content, object_pairs_hook=unique, parse_constant=nonfinite) + except (UnicodeError, json.JSONDecodeError) as exc: + raise Refusal("invalid local client journal JSON") from exc + + +class PersistentBodyClient: + def __init__(self, root, origin, operations, timeout=15): + # Each adapter must supply strict argument/result schemas and explicit + # lower-proof uncertainty. There is no permissive default contract. + if any(not callable(getattr(operations, name, None)) for name in + ("validate_arguments", "validate_result", "is_unknown")): + raise Refusal("an explicit operation contract with all schema hooks is required") + self.operations = operations + endpoint(origin) + self.timeout = protocol.finite(timeout) + if not .01 <= self.timeout <= 30: + raise Refusal("client timeout must be between .01 and 30 seconds") + self.root = protocol.directory(root, create=True) + self.origin = origin + self._mutex = threading.RLock() + self._closed = False + self._fd = os.open(self.root / "client.lock", os.O_RDWR | os.O_CREAT | os.O_NOFOLLOW, 0o600) + try: + if not stat.S_ISREG(os.fstat(self._fd).st_mode): + raise Refusal("invalid client lock") + try: + fcntl.flock(self._fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError as exc: + raise Refusal("another client process owns this journal") from exc + if (self.root / "client.json").exists(): + self._load() + else: + if any(p.name != "client.lock" for p in self.root.iterdir()): + raise Refusal("new client journal requires an empty private directory") + status, content = http_call(origin, "GET", "/identity", timeout=self.timeout) + if status != 200: + raise Refusal("body identity was unavailable") + remote = identity(protocol.decode(content)) + for name in ("requests", "responses", "events", "images"): + (self.root / name).mkdir(mode=0o700) + self._save({"version": 1, "origin": origin, "client_id": "client-" + uuid.uuid4().hex, + "identity": remote, "lease": None, "commands": {}, "pending_command_ids": [], "sequence": 0}) + except Exception: + os.close(self._fd) + self._closed = True + raise + + def close(self): + with self._mutex: + if not self._closed: + os.close(self._fd) + self._closed = True + + def __enter__(self): + return self + + def __exit__(self, *_): + self.close() + + def _load(self): + if self._closed: + raise Refusal("client is closed") + m = read_journal(self.root / "client.json") + protocol.fields(m, ("version", "origin", "client_id", "identity", "lease", "commands", "pending_command_ids", "sequence"), + "client journal") + if type(m["version"]) is not int or m["version"] != 1 or m["origin"] != self.origin: + raise Refusal("client endpoint or journal version changed") + identity(m["identity"]) + protocol.identity(m["client_id"]) + if not isinstance(m["commands"], dict) or len(m["commands"]) > 256 or not isinstance(m["pending_command_ids"], list): + raise Refusal("client journal budget or pending schema is invalid") + expected_pending = [] + for token, record in m["commands"].items(): + protocol.fields(record, {"request_sha256", "state", "send_count"} | + ({"response_sha256"} if "response_sha256" in record else set()), "client command index") + if type(record["send_count"]) is not int or record["send_count"] != 1: + raise Refusal("client command must retain exactly one send intent") + request, _ = self._request(m, token) + if "response_sha256" in record: + digest = record["response_sha256"] + if not isinstance(digest, str) or not protocol.DIGEST.fullmatch(digest): + raise Refusal("client response digest is invalid") + content = protocol.read_bytes(self.root / "responses" / (token + "-" + digest + ".json")) + if protocol.sha(content) != digest: + raise Refusal("durable client response changed") + value = self._envelope(m, request, content) + if record["state"] != value["state"]: + raise Refusal("client indexed command state changed") + terminal = self._terminal(value) + else: + if record["state"] != "unknown": + raise Refusal("command without a durable response cannot be resolved") + terminal = False + if not terminal: + expected_pending.append(token) + if (len(m["pending_command_ids"]) != len(set(m["pending_command_ids"])) or + set(m["pending_command_ids"]) != set(expected_pending)): + raise Refusal("client pending commands disagree with their durable responses") + return m + + def _save(self, m): + if len(protocol.encoded(m)) > MAX_CLIENT_JOURNAL: + raise Refusal("client journal exceeds its 128 KiB capacity") + protocol.persist(self.root / "client.json", m) + + def _event(self, m, kind, data): + if type(m["sequence"]) is not int or not 0 <= m["sequence"] < 2048: + raise Refusal("client event budget exhausted") + m["sequence"] += 1 + protocol.create_file(self.root / "events" / f"{m['sequence']:06d}.json", + protocol.encoded({"sequence": m["sequence"], "kind": kind, "data": data})) + self._save(m) + + def _request(self, m, token): + protocol.identity(token) + content = protocol.read_bytes(self.root / "requests" / (token + ".json")) + if protocol.sha(content) != m["commands"][token]["request_sha256"]: + raise Refusal("durable outgoing request changed") + request = wire.validate_command(protocol.decode(content)) + self.operations.validate_arguments(request["operation"], request["arguments"]) + if request["command_id"] != token or request["client_id"] != m["client_id"]: + raise Refusal("durable command identity changed") + return request, content + + def _pin(self, m, remote): + identity(remote) + for key in IDENTITY_KEYS - {"epoch"}: + if remote[key] != m["identity"][key]: + raise Refusal("body, adapter or source identity changed") + + def _blocked(self, m): + if m["pending_command_ids"]: + raise Uncertain(m["pending_command_ids"][0], "an unknown command blocks fresh commands and lease rotation") + + def acquire(self, duration_s=30): + duration = protocol.finite(duration_s) + if not .05 <= duration <= 30: + raise Refusal("lease duration must be between .05 and 30 seconds") + with self._mutex: + m = self._load() + self._blocked(m) + status, content = http_call(self.origin, "GET", "/identity", pinned_headers(m["identity"]), timeout=self.timeout) + if status != 200: + raise Refusal("body identity could not be refreshed") + remote = identity(protocol.decode(content)) + self._pin(m, remote) + m["identity"] = remote + m["lease"] = None + self._save(m) + request = {"client_id": m["client_id"], "expected_epoch": remote["epoch"], "duration_s": duration} + status, content = http_call(self.origin, "POST", "/lease", pinned_headers(remote), + protocol.encoded(request), self.timeout) + result = protocol.decode(content) + if status != 200: + raise Refusal(result.get("reason", "lease acquisition refused")) + protocol.fields(result, ("client_id", "lease_id", "epoch", "duration_s"), "lease") + if (result["client_id"] != m["client_id"] or type(result["epoch"]) is not int or + result["epoch"] != remote["epoch"] or protocol.finite(result["duration_s"]) != duration or + not isinstance(result["lease_id"], str) or not protocol.UUID.fullmatch(result["lease_id"])): + raise Refusal("lease response identity changed") + m["lease"] = result + self._event(m, "lease_acquired", result) + return result + + def _envelope(self, m, request, content): + value = wire.validate_envelope(protocol.decode(content), request, + m["commands"][request["command_id"]]["request_sha256"]) + if value["state"] == "completed": + self.operations.validate_result(request, value["result"], m["identity"]) + return value + + def _terminal(self, value): + return wire.terminal(value, self.operations.is_unknown(value["result"])) + + def _retain(self, m, token, value): + content = protocol.encoded(value) + digest = protocol.sha(content) + path = self.root / "responses" / (token + "-" + digest + ".json") + if path.exists(): + if protocol.read_bytes(path) != content: + raise Refusal("retained command response changed") + else: + protocol.create_file(path, content) + terminal = self._terminal(value) + m["commands"][token].update(state=value["state"], response_sha256=digest) + if terminal and token in m["pending_command_ids"]: + m["pending_command_ids"].remove(token) + self._event(m, "response_read", {"command_id": token, "state": value["state"], "response_sha256": digest, + "historical": True}) + if not terminal: + raise Uncertain(token, value["reason"] or "command outcome remains unknown") + return value + + def _result(self, m, operation, result): + return result + + def command(self, operation, arguments): + """Issue one operation accepted by the explicitly supplied schema.""" + return self._command(operation, arguments) + + def _command(self, operation, arguments): + protocol.identity(operation) + if not isinstance(arguments, dict): + raise Refusal("operation arguments must be an object") + self.operations.validate_arguments(operation, arguments) + with self._mutex: + m = self._load() + self._blocked(m) + if m["lease"] is None or len(m["commands"]) >= 256: + raise Refusal("an acquired lease and remaining command budget are required") + token = "command-" + uuid.uuid4().hex + pinned = m["identity"] + request = {"protocol": PROTOCOL, "adapter_id": pinned["adapter_id"], "body_id": pinned["body_id"], + "source_sha256": pinned["source_sha256"], "client_id": m["client_id"], + "lease_id": m["lease"]["lease_id"], "epoch": m["lease"]["epoch"], "command_id": token, + "operation": operation, "arguments": arguments} + wire.validate_command(request) + content = protocol.encoded(request) + if len(content) > protocol.MAX_BYTES: + raise Refusal("command exceeds bounded JSON size") + protocol.create_file(self.root / "requests" / (token + ".json"), content) + m["commands"][token] = {"request_sha256": protocol.sha(content), "state": "unknown", "send_count": 1} + m["pending_command_ids"].append(token) + self._event(m, "send_intent", {"command_id": token, "request_sha256": protocol.sha(content), "send_count": 1}) + try: + _, reply = http_call(self.origin, "POST", "/commands", pinned_headers(pinned), content, self.timeout) + value = self._envelope(m, request, reply) + except (TimeoutError, OSError, http.client.HTTPException, Refusal) as exc: + self._event(m, "transport_unknown", {"command_id": token, "reason": str(exc)}) + raise Uncertain(token, "reply is uncertain; reconcile exact command without resending") from exc + value = self._retain(m, token, value) + if value["state"] in {"refused", "not_accepted"}: + raise Refusal(value["reason"] or "body command refused") + return self._result(m, operation, value["result"]) + + def reconcile(self, command_id=None): + with self._mutex: + m = self._load() + if command_id is None: + if not m["pending_command_ids"]: + raise Refusal("no unknown command requires reconciliation") + command_id = m["pending_command_ids"][0] + if command_id not in m["commands"]: + raise Refusal("command is not owned by this client") + request, _ = self._request(m, command_id) + query = urlencode({"client_id": request["client_id"], "lease_id": request["lease_id"], "epoch": request["epoch"], + "request_sha256": m["commands"][command_id]["request_sha256"]}) + try: + _, content = http_call(self.origin, "GET", "/commands/" + command_id + "?" + query, + pinned_headers(m["identity"]), timeout=self.timeout) + value = self._envelope(m, request, content) + except (TimeoutError, OSError, http.client.HTTPException, Refusal) as exc: + self._event(m, "reconciliation_unknown", {"command_id": command_id, "reason": str(exc)}) + raise Uncertain(command_id, "read-only reconciliation is unavailable or untrusted") from exc + value = self._retain(m, command_id, value) + if value["state"] == "completed": + return {**value, "result": self._result(m, request["operation"], value["result"])} + return value + + def status(self): + with self._mutex: + m = self._load() + try: + status, content = http_call(self.origin, "GET", "/state", pinned_headers(m["identity"]), timeout=self.timeout) + remote = protocol.decode(content) + if status != 200: + raise Refusal("body state was unavailable") + self._pin(m, remote["identity"]) + except (TimeoutError, OSError, http.client.HTTPException, Refusal, KeyError) as exc: + remote = {"state": "unavailable", "reason": str(exc)} + return {"identity": m["identity"], "lease": m["lease"], "pending_command_ids": m["pending_command_ids"], + "server_state": remote} diff --git a/tools/physical-workcell/planner_guard.py b/tools/physical-workcell/planner_guard.py new file mode 100644 index 0000000..14a0034 --- /dev/null +++ b/tools/physical-workcell/planner_guard.py @@ -0,0 +1,317 @@ +"""Parent-liveness supervisor for a tool-free planner, never a body dispatcher. + +An inherited pipe closes on host death. The supervisor holds an inherited flock +until the planner group has been terminated and reaped. Restart only waits on +that lock; it never kills a process based on a retained PID. +""" +import argparse +import contextlib +import fcntl +import os +import select +import signal +import stat +import subprocess +import sys +import time +from pathlib import Path + +import order_protocol as protocol + +RECOVERY_TIMEOUT = 12 +MAX_DESCENDANTS = 128 +REASONS = {"completed", "host exited before planner startup", "host liveness pipe closed", + "host cancelled planner", "planner watchdog timeout", "planner failed", "planner guardian failed"} + + +def _positive_pid(value): + return type(value) is int and value > 0 + + +def _bound_record(value, intent): + return (isinstance(value, dict) and all(value.get(k) == intent[k] for k in ("host_pid", "owner", "request_sha256")) + and _positive_pid(value.get("guardian_pid"))) + + +def _present(path): + return path.exists() or path.is_symlink() + + +def _processes(): + """Bounded identity-only inventory; never inspect command arguments/env.""" + result = subprocess.run(["ps", "-axo", "pid=,ppid=,pgid=,uid=,lstart="], capture_output=True, timeout=2, check=True) + if len(result.stdout) > 2 * 1024 * 1024: + raise protocol.Refusal("planner process inventory exceeds its bound") + rows = {} + for line in result.stdout.decode().splitlines(): + parts = line.split(maxsplit=4) + if len(parts) != 5: + raise protocol.Refusal("planner process inventory is malformed") + pid, parent, group, uid = map(int, parts[:4]) + rows[pid] = {"pid": pid, "parent": parent, "group": group, "uid": uid, "started": parts[4]} + return rows + + +def _same_process(row, known): + return row is not None and all(row[k] == known[k] for k in ("pid", "uid", "started")) + + +def _track(child, observed): + rows = _processes() + owned = {child.pid} + owned.update(pid for pid, known in observed.items() if _same_process(rows.get(pid), known)) + while True: + added = {pid for pid, row in rows.items() if row["parent"] in owned} - owned + if not added: + break + owned.update(added) + changed = False + for pid in owned: + if pid not in rows: + continue + if rows[pid]["uid"] != os.getuid(): + raise protocol.Refusal("owned planner descendant has a different user") + if pid in observed and not _same_process(rows[pid], observed[pid]): + raise protocol.Refusal("planner descendant PID identity became ambiguous") + if pid not in observed: + observed[pid] = rows[pid] + changed = True + if len(observed) > MAX_DESCENDANTS: + raise protocol.Refusal("planner descendant inventory exceeds its bound") + return changed + + +def _descendant_proof(root, closure, observed, complete=False, remaining=None): + value = {k: closure[k] for k in ("host_pid", "owner", "request_sha256", "guardian_pid")} + value.update(version=1, complete=complete, observed=list(observed.values()), remaining=remaining or []) + protocol.persist(root / "planner-descendants.json", value) + + +def _verify_descendants(root, intent, startup): + value = protocol.decode(protocol.read_bytes(root / "planner-descendants.json")) + protocol.fields(value, ("version", "host_pid", "owner", "request_sha256", "guardian_pid", "complete", "observed", "remaining"), + "planner descendant proof") + if (type(value["version"]) is not int or value["version"] != 1 or not _bound_record(value, intent) or + value["guardian_pid"] != startup["guardian_pid"] or value["complete"] is not True or value["remaining"] != [] or + not isinstance(value["observed"], list) or not value["observed"] or len(value["observed"]) > MAX_DESCENDANTS): + raise protocol.Refusal("planner descendant cleanup proof is inconsistent") + identities = set() + for row in value["observed"]: + protocol.fields(row, ("pid", "parent", "group", "uid", "started"), "planner descendant") + if (not _positive_pid(row["pid"]) or not _positive_pid(row["group"]) or + type(row["parent"]) is not int or row["parent"] < 0 or type(row["uid"]) is not int or row["uid"] != os.getuid() or + not isinstance(row["started"], str) or not 1 <= len(row["started"]) <= 64): + raise protocol.Refusal("planner descendant identity is invalid") + key = (row["pid"], row["uid"], row["started"]) + if key in identities: + raise protocol.Refusal("planner descendant identity is repeated") + identities.add(key) + if not any(row["pid"] == startup["planner_pid"] and row["group"] == startup["planner_pgid"] and + row["parent"] == startup["guardian_pid"] for row in value["observed"]): + raise protocol.Refusal("planner descendant proof omits its spawned leader identity") + + +def lock_call(root): + root = protocol.directory(root) + fd = os.open(root / "planner.lock", os.O_CREAT | os.O_RDWR | os.O_NOFOLLOW, 0o600) + info = os.fstat(fd) + if not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid() or info.st_mode & 0o077: + os.close(fd) + raise protocol.Refusal("planner lock must be a private regular file") + return fd + + +def recover(root): + """Wait for prior writers before exposing a restarted host; no PID signals.""" + root = protocol.directory(root) + if not _present(root / "planner-intent.json"): + if any(_present(root / name) for name in ("planner-process.json", "planner-closure.json", "planner-descendants.json", "planner.lock")): + raise protocol.Refusal("planner ownership artifacts have no intent") + return None + intent = protocol.decode(protocol.read_bytes(root / "planner-intent.json")) + protocol.fields(intent, ("version", "host_pid", "owner", "request_sha256"), "planner intent") + if type(intent["version"]) is not int or intent["version"] != 1 or type(intent["host_pid"]) is not int or intent["host_pid"] <= 0: + raise protocol.Refusal("invalid planner intent") + protocol.identity(intent["owner"]) + if not isinstance(intent["request_sha256"], str) or not protocol.DIGEST.fullmatch(intent["request_sha256"]): + raise protocol.Refusal("invalid planner request digest") + reservation = protocol.decode(protocol.read_bytes(root / "reservation.json")) + if not isinstance(reservation, dict) or reservation.get("planner_owner", intent["owner"]) != intent["owner"]: + raise protocol.Refusal("planner intent differs from its reservation owner") + fd = lock_call(root) + try: + deadline = time.monotonic() + RECOVERY_TIMEOUT + while True: + try: + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + break + except BlockingIOError: + if time.monotonic() >= deadline: + raise protocol.Refusal("prior planner ownership has not closed; restart refused") from None + time.sleep(.05) + started = root / "planner-process.json" + closed = root / "planner-closure.json" + if _present(started) and not _present(closed): + raise protocol.Refusal("planner ownership ended without group cleanup proof") + if _present(closed): + if not _present(started): + raise protocol.Refusal("planner cleanup has no startup identity") + startup = protocol.decode(protocol.read_bytes(started)) + base = {"owner", "request_sha256", "guardian_pid", "host_pid", "process_exit_observed", "group_closed"} + if (not isinstance(startup, dict) or set(startup) not in (base, base | {"planner_pid", "planner_pgid"}) or not _bound_record(startup, intent) or + startup["group_closed"] is not False or startup["process_exit_observed"] is not False or + "planner_pid" in startup and (not _positive_pid(startup["planner_pid"]) or startup["planner_pgid"] != startup["planner_pid"])): + raise protocol.Refusal("planner startup proof is inconsistent") + closure = protocol.decode(protocol.read_bytes(closed)) + if (not isinstance(closure, dict) or set(closure) != base | {"reason", "exit_code"} or + not _bound_record(closure, intent) or closure["guardian_pid"] != startup["guardian_pid"] or + closure["group_closed"] is not True or closure["process_exit_observed"] is not True or + not isinstance(closure["reason"], str) or closure["reason"] not in REASONS or + closure["exit_code"] is not None and type(closure["exit_code"]) is not int): + raise protocol.Refusal("planner cleanup proof is inconsistent") + if "planner_pid" in startup: + _verify_descendants(root, intent, startup) + return closure + # Intent was saved before launch. The inherited lock was never handed to + # a supervisor which published startup, so no planner can have started. + return {"owner": intent["owner"], "request_sha256": intent["request_sha256"], + "reason": "host exited before planner startup", "group_closed": True, "process_exit_observed": True} + finally: + os.close(fd) + + +def _close_group(child): + # The group is created by this supervisor, not identified from disk. Always + # terminate descendants even if the leader has already exited. + with contextlib.suppress(ProcessLookupError, PermissionError): + os.killpg(child.pid, signal.SIGTERM) + deadline = time.monotonic() + 3 + while time.monotonic() < deadline: + try: + os.killpg(child.pid, 0) + except ProcessLookupError: + child.wait(timeout=3) + return + except PermissionError: + # Darwin may return EPERM while only unsignalable zombie members + # remain. Reap our child and require ESRCH before claiming closure. + pass + child.poll() + time.sleep(.05) + with contextlib.suppress(ProcessLookupError, PermissionError): + os.killpg(child.pid, signal.SIGKILL) + child.wait(timeout=3) + # A zombie descendant can briefly retain group membership while its new + # parent reaps it. Refuse proof rather than releasing uncertain ownership. + deadline = time.monotonic() + 3 + while time.monotonic() < deadline: + try: + os.killpg(child.pid, 0) + except ProcessLookupError: + return + except PermissionError: + pass + time.sleep(.05) + raise protocol.Refusal("planner process group did not close") + + +def supervise(root, executable, lifetime_fd, lock_fd): + root = protocol.directory(root) + intent = protocol.decode(protocol.read_bytes(root / "planner-intent.json")) + request = protocol.decode(protocol.read_bytes(root / "request.json")) + cancelled = False + def stop(*_): + nonlocal cancelled + cancelled = True + signal.signal(signal.SIGTERM, stop) + signal.signal(signal.SIGINT, stop) + child = None + observed = {} + reason = "completed" + closure = {"owner": intent["owner"], "request_sha256": intent["request_sha256"], + "guardian_pid": os.getpid(), "host_pid": intent["host_pid"], "process_exit_observed": False, "group_closed": False} + try: + # Publish before spawning: missing closure after this point is ambiguous + # and restart refuses even if a guardian was itself killed. + protocol.persist(root / "planner-process.json", {**closure, "host_pid": intent["host_pid"]}) + readable, _, _ = select.select([lifetime_fd], [], [], 0) + if readable and not os.read(lifetime_fd, 1): + reason = "host exited before planner startup" + else: + child = subprocess.Popen([executable, "exec", *request["flags"]], stdin=subprocess.PIPE, + stdout=sys.stdout, stderr=sys.stderr, start_new_session=True, close_fds=True) + protocol.persist(root / "planner-process.json", {**closure, "host_pid": intent["host_pid"], + "planner_pid": child.pid, "planner_pgid": child.pid}) + _track(child, observed) + _descendant_proof(root, closure, observed) + prompt = protocol.read_bytes(root / "prompt.txt") + sent = 0 + os.set_blocking(child.stdin.fileno(), False) + started = time.monotonic() + while child.poll() is None: + if _track(child, observed): + _descendant_proof(root, closure, observed) + writable = [child.stdin.fileno()] if sent < len(prompt) else [] + readable, ready, _ = select.select([lifetime_fd], writable, [], .05) + if readable and not os.read(lifetime_fd, 1): + reason = "host liveness pipe closed" + break + if cancelled: + reason = "host cancelled planner" + break + if time.monotonic() - started > 105: + reason = "planner watchdog timeout" + break + if ready: + try: + sent += os.write(child.stdin.fileno(), prompt[sent:sent + 4096]) + except BlockingIOError: + pass + except BrokenPipeError: + reason = "planner failed" + break + if sent == len(prompt): + child.stdin.close() + if reason == "completed" and sent != len(prompt): + reason = "planner failed" + if reason == "completed" and child.returncode != 0: + reason = "planner failed" + except Exception: + reason = "planner guardian failed" + raise + finally: + try: + if child is not None: + try: + if _track(child, observed): + _descendant_proof(root, closure, observed) + finally: + _close_group(child) + deadline = time.monotonic() + 3 + while True: + rows = _processes() + remaining = [rows[pid] for pid, known in observed.items() if _same_process(rows.get(pid), known)] + if not remaining: + break + _descendant_proof(root, closure, observed, remaining=remaining) + if time.monotonic() >= deadline: + raise protocol.Refusal("observed planner descendants remain; cleanup proof withheld") + time.sleep(.05) + _descendant_proof(root, closure, observed, complete=True) + closure.update(reason=reason, exit_code=child.returncode if child else None, + process_exit_observed=True, group_closed=True) + protocol.persist(root / "planner-closure.json", closure) + finally: + os.close(lifetime_fd) + os.close(lock_fd) + return child.returncode if reason == "completed" and child else 1 + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--executable", required=True) + parser.add_argument("--lifetime-fd", type=int, required=True) + parser.add_argument("--lock-fd", type=int, required=True) + args = parser.parse_args() + raise SystemExit(supervise(args.root, args.executable, args.lifetime_fd, args.lock_fd)) diff --git a/tools/physical-workcell/proposal.schema.json b/tools/physical-workcell/proposal.schema.json new file mode 100644 index 0000000..6c40d13 --- /dev/null +++ b/tools/physical-workcell/proposal.schema.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "SIM2 image-bound proposal", + "type": "object", + "additionalProperties": false, + "required": ["version", "run_dir", "generation", "image_sha256", "token", "block_box", "decision"], + "properties": { + "version": {"const": 1}, + "run_dir": {"type": "string", "minLength": 1, "maxLength": 2048}, + "generation": {"type": "integer", "minimum": 0, "maximum": 999999}, + "image_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, + "token": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$"}, + "block_box": { + "type": "array", "minItems": 4, "maxItems": 4, + "items": {"type": "integer", "minimum": 0, "maximum": 640} + }, + "decision": {"enum": ["move", "complete"]}, + "target": { + "type": "object", "additionalProperties": false, "required": ["x", "y"], + "properties": {"x": {"type": "number", "minimum": -0.32, "maximum": 0.18}, + "y": {"type": "number", "minimum": -0.01, "maximum": 0.01}} + } + }, + "allOf": [{"if": {"properties": {"decision": {"const": "move"}}}, + "then": {"required": ["target"]}, + "else": {"not": {"required": ["target"]}}}], + "$comment": "Runtime additionally requires canonical run binding, finite numbers, correct bbox dimensions/order, fresh image/generation and RGB evidence; coordinates use exclusive maximum pixel edges." +} diff --git a/tools/physical-workcell/qualify_bench.py b/tools/physical-workcell/qualify_bench.py new file mode 100644 index 0000000..702d2db --- /dev/null +++ b/tools/physical-workcell/qualify_bench.py @@ -0,0 +1,169 @@ +"""Frozen SIM6 public-interface trial with separate native/SQLite scoring.""" + +import argparse +import hashlib +import json +import os +import sqlite3 +import subprocess +import time +import traceback +from pathlib import Path + +import bench +import workcell as body + +BASE = Path(__file__).resolve().parent +REPO = BASE.parents[1] +SOURCES = [BASE / name for name in ( + "bench.py", "measurement.py", "bench_runner.py", "qualify_bench.py", "workcell.py", + "image_guided.py", "episode_cases.py", "order_protocol.py", "scene.xml")] +SOURCES.append(BASE.parent / "camera-capture-experiment" / "capture.py") + + +def retain(path, value): + body.create_file(path, body.encoded(value)) + + +def source_snapshot(commit): + if subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=REPO, text=True).strip() != commit: + raise AssertionError("qualification must start at the named source commit") + snapshot = {} + for path in SOURCES: + relative = str(path.relative_to(REPO)) + frozen = subprocess.check_output(["git", "show", f"{commit}:{relative}"], cwd=REPO) + current = body.read_bytes(path) + if frozen != current: + raise AssertionError(f"source differs from named freeze: {relative}") + snapshot[relative] = hashlib.sha256(current).hexdigest() + return snapshot + + +def native_score(root, generation, state_sha256, zone): + # Separate scorer only: never imported or invoked by the RGB host. + content = body.read_bytes(body.state_path(root, generation)) + if body.sha(content) != state_sha256: + raise AssertionError("scored state digest differs from receipt") + state = body.decoded(content) + model = body.mujoco.MjModel.from_xml_path(str(root / "model.xml")) + data = body.mujoco.MjData(model) + body.mujoco.mj_setState(model, data, body.np.asarray(state["values"]), body.mujoco.mjtState.mjSTATE_INTEGRATION) + body.mujoco.mj_forward(model, data) + block = data.xpos[model.body("block").id].tolist() + pusher = data.xpos[model.body("pusher").id].tolist() + return {"role": "independent_native_state_scorer", "generation": generation, + "state_sha256": state_sha256, "native_time_s": float(data.time), "block_xyz": block, + "pusher_xyz": pusher, "block_center_in_zone": bool( + zone["x_min"] <= block[0] <= zone["x_max"] and + zone["y_min"] <= block[1] <= zone["y_max"] and .02 <= block[2] <= .08), + "pusher_home": abs(pusher[0] + .3) <= .005 and abs(pusher[1]) <= .005, + "external_force_cleared": bool(not body.np.any(data.xfrc_applied))} + + +def sqlite_score(root): + with sqlite3.connect((root / "measurements.sqlite").as_uri() + "?mode=ro", uri=True) as connection: + connection.row_factory = sqlite3.Row + rows = [dict(row) for row in connection.execute("SELECT * FROM measurements ORDER BY job_id")] + for row in rows: + row["receipt"] = json.loads(row.pop("receipt_json")) + return {"role": "independent_read_only_sqlite_scorer", "count": len(rows), "rows": rows} + + +def score_job(root, journal, result, expected_count, expected_bench_id): + measurement = result["measurement"]["measurement_receipt"] + reset = result["reset"] + stage = native_score(root, measurement["generation"], measurement["state_sha256"], + {"x_min": .035, "x_max": .105, "y_min": -.025, "y_max": .025}) + home = native_score(root, reset["generation"], reset["state_sha256"], bench.HOME_ZONE) + database = sqlite_score(root) + row = next(row for row in database["rows"] if row["job_id"] == result["job_id"]) + assert row["receipt"] == measurement and database["count"] == expected_count + assert stage["block_center_in_zone"] and home["block_center_in_zone"] and home["pusher_home"] + assert stage["external_force_cleared"] and home["external_force_cleared"] + assert measurement["bench_id"] == reset["bench_id"] == expected_bench_id + assert home["native_time_s"] > stage["native_time_s"] + events = [body.decoded(body.read_bytes(path)) for path in sorted(journal.glob("event-*.json"))] + motions = [event for event in events if event["kind"] == "motion"] + contact_reset = [event for event in motions if event["purpose"] == "contact_reset"] + contact_samples = sum(event["receipt"]["pusher_block_contact_samples"] for event in contact_reset) + assert contact_samples > 0 + steps = sum(event["receipt"]["native_steps"] for event in motions) + assert steps > 0 and all(event["receipt"]["max_axis_actuator_force_n"] <= 4.00000001 for event in motions) + assert measurement["worker_pid"] != os.getpid() + if result["recovery"]: + assert result["recovery"]["stale_refused"] == ["move", "measure"] + assert result["recovery"]["rgb_displacement_m"] >= .02 + recovery_moves = [event for event in motions if event["purpose"] == "recover_unexpected_displacement"] + assert recovery_moves and sum(e["receipt"]["pusher_block_contact_samples"] for e in recovery_moves) > 0 + observations = [event["packet"] for event in events if event["kind"] == "rgb_observation"] + private_keys = {"block_xyz", "pusher_xyz", "qpos", "qvel", "native_pose"} + assert all(not private_keys.intersection(packet) for packet in observations) + return {"job_id": result["job_id"], "measurement_state": stage, "reset_state": home, + "sqlite": database, "motion_count": len(motions), "native_motion_steps": steps, + "reset_contact_samples": contact_samples, "recovery": result["recovery"], + "planner_role": "frozen_deterministic_color_RGB_host", "passed": True} + + +def run(root, commit): + source = source_snapshot(commit) + root = body.directory(root, create=True) + retain(root / "source-freeze.json", {"commit": commit, "files": source}) + bench_root = root / "bench" + initialized = bench.initialize(bench_root, diagnostic_enabled=True) + retain(root / "initialized.json", initialized) + scores, attempts = [], [] + try: + for index, name in enumerate(("ordinary", "disturbed"), 1): + journal = root / (name + "-journal") + command = [str(BASE / ".venv/bin/python"), str(BASE / "bench_runner.py"), + "--bench-dir", str(bench_root), "--journal-dir", str(journal), "--job-id", name] + if name == "disturbed": + command.append("--disturb") + before = bench.Bench(bench_root).status() + started = time.monotonic() + process = subprocess.run(command, capture_output=True, timeout=240, check=False) + body.create_file(root / (name + ".stdout.json"), process.stdout) + body.create_file(root / (name + ".stderr.log"), process.stderr) + attempt = {"job_id": name, "attempt": 1, "returncode": process.returncode, + "elapsed_s": time.monotonic() - started, "before": before, + "all_caller_and_worker_processes_synchronous": True} + attempts.append(attempt) + retain(root / (name + "-attempt.json"), attempt) + if process.returncode != 0: + raise AssertionError(f"{name} failed; original stderr/stdout and partial journal retained") + result = body.decoded(process.stdout) + after = bench.Bench(bench_root).status() + assert after["bench_id"] == initialized["bench_id"] and after["active_job"] is None + assert after["generation"] > before["generation"] and after["native_time_s"] > before["native_time_s"] + current_job = after["jobs"][name] + assert current_job["started_generation"] == before["generation"] + assert current_job["started_native_time_s"] == before["native_time_s"] + score = score_job(bench_root, journal, result, index, initialized["bench_id"]) + score["continuity"] = {"before": before, "after": after} + scores.append(score) + retain(root / (name + "-score.json"), score) + unchanged = source_snapshot(commit) == source + assert unchanged + summary = {"version": 1, "source_commit": commit, "source_unchanged": unchanged, + "bench_id": initialized["bench_id"], "successful_cycles": len(scores), "required_cycles": 2, + "attempts": attempts, "scores": scores, "passed": True, + "limits": "deterministic RGB policy; axial force disturbance; simulation; no hardware or model trial"} + except Exception as exc: + summary = {"version": 1, "source_commit": commit, "passed": False, "attempts": attempts, + "scores": scores, "error": str(exc), "traceback": traceback.format_exc()} + retain(root / "qualification.json", summary) + raise + retain(root / "qualification.json", summary) + return {key: summary[key] for key in ("passed", "source_commit", "bench_id", "successful_cycles", "source_unchanged")} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--output", required=True, type=Path) + parser.add_argument("--source-commit", required=True) + args = parser.parse_args() + print(json.dumps(run(args.output, args.source_commit), sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/qualify_network_body.py b/tools/physical-workcell/qualify_network_body.py new file mode 100644 index 0000000..02c3a0e --- /dev/null +++ b/tools/physical-workcell/qualify_network_body.py @@ -0,0 +1,176 @@ +"""Frozen actual-model network trial; native-state scoring is post-trial only.""" + +import argparse +import json +import socket +import subprocess +import threading +import time +import traceback +import urllib.request +from pathlib import Path + +import agent_bench +import body_client +import codex_policy +import network_fault_proxy +import order_protocol as protocol + +BASE = Path(__file__).resolve().parent +REPO = BASE.parents[1] +SOURCES = [BASE / name for name in ( + "body_protocol.py", "body_contract.py", "bench_body_contract.py", "persistent_body_client.py", + "body_network.py", "body_client.py", "network_fault_proxy.py", "planner_guard.py", + "qualify_network_body.py", "score_network_body.py", "codex_policy.py", "codex-policy.md", + "codex-action.schema.json", "agent_bench.py", "bench.py", "measurement.py", "workcell.py", + "image_guided.py", "episode_cases.py", "order_protocol.py", "scene.xml", "qualify_bench.py")] +SOURCES.append(BASE.parent / "camera-capture-experiment" / "capture.py") +ZONES = ({"x_min": .035, "x_max": .105, "y_min": -.025, "y_max": .025}, + {"x_min": .045, "x_max": .115, "y_min": -.025, "y_max": .025}) + + +def retain(path, value): + protocol.create_file(path, protocol.encoded(value)) + + +def freeze(root, commit): + if subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=REPO, text=True).strip() != commit: + raise protocol.Refusal("trial must start at named source commit") + files = {} + for path in SOURCES: + name = str(path.relative_to(REPO)) + content = protocol.read_bytes(path) + if subprocess.check_output(["git", "show", f"{commit}:{name}"], cwd=REPO) != content: + raise protocol.Refusal("source differs from named commit: " + name) + files[name] = protocol.sha(content) + retain(root / "source-freeze.json", {"commit": commit, "files": files}) + + +def available_port(): + with socket.socket() as sock: + sock.bind(("127.0.0.1", 0)) + return sock.getsockname()[1] + + +def run(root, commit): + root = protocol.directory(root, create=True) + freeze(root, commit) + port = available_port() + origin = f"http://127.0.0.1:{port}" + log = (root / "service.log").open("xb") + process = subprocess.Popen([str(BASE / ".venv/bin/python"), str(BASE / "body_network.py"), + "--root", str(root / "body"), "--port", str(port), + "--initialize", "--diagnostic"], stdout=log, stderr=log) + proxy, client = None, None + ledger = {"attempt": 1, "planner": "actual_codex_cli_images_over_TCP", "jobs": [], + "faults": [], "service_pid": process.pid, "body_origin": origin, "passed": False} + try: + deadline = time.monotonic() + 20 + while True: + try: + with urllib.request.urlopen(origin + "/identity", timeout=1) as response: + identity = protocol.decode(response.read(protocol.MAX_BYTES + 1)) + break + except (OSError, ValueError): + if process.poll() is not None or time.monotonic() >= deadline: + raise RuntimeError("body service failed to start") + time.sleep(.1) + retain(root / "identity.json", identity) + proxy = network_fault_proxy.FaultProxy(origin, root / "proxy") + proxy.start() + client = body_client.BodyClient(root / "client", proxy.origin, timeout=15) + client.acquire() + retain(root / "initial-public.json", client.status()) + (root / "calls").mkdir(mode=0o700) + injected = set() + for index, zone in enumerate(ZONES, 1): + job_id = f"network-ai-{index}" + history = [] + client.acquire() + client.start_job(job_id, zone) + entry = {"job_id": job_id, "calls": 0, "completed": False} + ledger["jobs"].append(entry) + for number in range(1, 33): + client.acquire() + challenge = client.observe() + directory = root / "calls" / f"job-{index}-call-{number:03d}" + directory.mkdir(mode=0o700) + retain(directory / "reservation.json", {"job_id": job_id, "call": number, + "challenge": challenge, "history": history[-6:]}) + action, metadata = codex_policy.decide(challenge, history[-6:], directory, threading.Event()) + retain(directory / "decision.json", {"action": action, "metadata": metadata}) + entry["calls"] += 1 + if action["action"] == "stop": + raise RuntimeError("model stopped: " + action["intent"]) + client.acquire() + proposal = client.propose(challenge["challenge_id"], action["action"], action["intent"], + action["target_x"], action["target_y"]) + retain(directory / "proposal.json", proposal) + mode = None + if action["action"] == "move": + for candidate in ("drop_after", "duplicate", "delay_after"): + if candidate not in injected: + mode = candidate + injected.add(mode) + proxy.arm(mode, operation="dispatch", delay_s=.2) + break + try: + outcome = client.dispatch(proposal["proposal_id"]) + except body_client.Uncertain as exc: + # Only read the original command's result. Never send dispatch again. + reconciliation = client.reconcile(exc.command_id) + retain(directory / "reconciliation.json", reconciliation) + if reconciliation["state"] != "completed": + raise RuntimeError("dispatch remains unresolved") from exc + outcome = reconciliation["result"] + ledger["faults"].append({"mode": mode, "command_id": exc.command_id, + "reconciled_state": reconciliation["state"], + "read_only": True, "call": str(directory.relative_to(root))}) + if mode and mode != "drop_after": + ledger["faults"].append({"mode": mode, "call": str(directory.relative_to(root))}) + outcome = agent_bench.public_response(outcome) + retain(directory / "dispatch.json", outcome) + history.append({"action": action, "outcome": outcome["outcome"], + "reason": outcome.get("reason", "")}) + if outcome["outcome"] == "unknown": + raise RuntimeError("local guard returned unknown") + print(json.dumps({"job": job_id, "call": number, "action": action["action"], + "outcome": outcome["outcome"], "fault": mode}), flush=True) + if action["action"] == "finish_reset" and outcome["outcome"] == "executed": + entry["completed"] = True + break + if not entry["completed"]: + raise RuntimeError("model job exhausted 32-call bound") + retain(root / "final-public.json", client.status()) + ledger["passed"] = len(ledger["jobs"]) == 2 and all(job["completed"] for job in ledger["jobs"]) + except Exception as exc: + ledger["error"] = type(exc).__name__ + ": " + str(exc) + (root / "failure.txt").write_text(traceback.format_exc()) + raise + finally: + if client and hasattr(client, "close"): + client.close() + if proxy: + proxy.close() + process.terminate() + try: + process.wait(timeout=15) + except subprocess.TimeoutExpired: + process.kill() + process.wait(timeout=5) + log.close() + ledger["service_exit_observed"] = process.poll() is not None + retain(root / "attempt.json", ledger) + return ledger + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--source-commit", required=True) + args = parser.parse_args() + run(args.root, args.source_commit) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/qualify_network_workbench.py b/tools/physical-workcell/qualify_network_workbench.py new file mode 100644 index 0000000..257a2c3 --- /dev/null +++ b/tools/physical-workcell/qualify_network_workbench.py @@ -0,0 +1,140 @@ +"""Own a frozen browser/model trial's processes; UI actions come from the browser.""" + +import argparse +import json +import signal +import socket +import subprocess +import threading +import time +import urllib.request +from pathlib import Path + +import order_protocol as protocol +import qualify_network_body + +BASE = Path(__file__).resolve().parent +REPO = BASE.parents[1] +SOURCES = qualify_network_body.SOURCES + [BASE / name for name in ( + "workbench_link.py", "network_operator_runtime.py", "network_bench_operator.py", + "network_bench_operator.html", "qualify_network_workbench.py", "score_network_workbench.py", + "score_network_body.py", "record_network_workbench.py")] + + +def retain(path, value): + protocol.create_file(path, protocol.encoded(value)) + + +def available_port(): + with socket.socket() as sock: + sock.bind(("127.0.0.1", 0)) + return sock.getsockname()[1] + + +def wait_json(origin, path, processes): + deadline = time.monotonic() + 30 + while time.monotonic() < deadline: + try: + with urllib.request.urlopen(origin + path, timeout=1) as response: + return protocol.decode(response.read(protocol.MAX_BYTES + 1)) + except (OSError, ValueError): + if any(process.poll() is not None for process in processes): + raise protocol.Refusal("owned trial process exited during startup") + time.sleep(.1) + raise protocol.Refusal("owned trial endpoint did not become ready") + + +def freeze(root, commit): + if subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=REPO, text=True).strip() != commit: + raise protocol.Refusal("start the trial at its committed source") + files = {} + for path in SOURCES: + name = str(path.relative_to(REPO)) + content = protocol.read_bytes(path) + if subprocess.check_output(["git", "show", f"{commit}:{name}"], cwd=REPO) != content: + raise protocol.Refusal("source is not frozen: " + name) + files[name] = protocol.sha(content) + retain(root / "source-freeze.json", {"commit": commit, "files": files}) + + +def run(root, commit): + root = protocol.directory(root, create=True) + freeze(root, commit) + stopped = threading.Event() + signal.signal(signal.SIGTERM, lambda *_: stopped.set()) + signal.signal(signal.SIGINT, lambda *_: stopped.set()) + processes, logs = [], [] + ledger = {"source_commit": commit, "planner": "actual_codex_cli_images_over_TCP", + "simulation": True, "processes": [], "ready": False} + + def launch(name, arguments): + log = (root / (name + ".log")).open("xb") + logs.append(log) + process = subprocess.Popen([str(BASE / ".venv/bin/python"), *arguments], + cwd=REPO, stdout=log, stderr=log) + processes.append(process) + ledger["processes"].append({"role": name, "pid": process.pid}) + return process + + try: + body_origin = f"http://127.0.0.1:{available_port()}" + launch("body", [str(BASE / "body_network.py"), "--root", str(root / "body"), + "--port", body_origin.rsplit(":", 1)[1], "--initialize", "--diagnostic"]) + identity = wait_json(body_origin, "/identity", processes) + retain(root / "identity.json", identity) + browser_origin = f"http://127.0.0.1:{available_port()}" + initialization = subprocess.run([ + str(BASE / ".venv/bin/python"), str(BASE / "network_bench_operator.py"), "init", + "--root", str(root / "operator"), "--body-origin", body_origin], + cwd=REPO, capture_output=True, timeout=30, check=False) + protocol.create_file(root / "initialization.stdout", initialization.stdout) + protocol.create_file(root / "initialization.stderr", initialization.stderr) + if initialization.returncode: + raise protocol.Refusal("network browser initialization failed; evidence retained") + launch("workbench", [str(BASE / "network_bench_operator.py"), "serve", "--root", str(root / "operator"), + "--port", browser_origin.rsplit(":", 1)[1], "--backend", "codex"]) + initial = wait_json(browser_origin, "/api/state", processes) + retain(root / "initial-public.json", initial) + ledger.update(body_origin=body_origin, browser_origin=browser_origin, ready=True, + app_id=initial["app_id"], body_id=identity["body_id"]) + retain(root / "ready.json", ledger) + print(json.dumps(ledger), flush=True) + deadline = time.monotonic() + 1800 + while not stopped.is_set() and not (root / "stop-request.json").exists(): + if any(process.poll() is not None for process in processes): + raise protocol.Refusal("owned trial process exited before requested cleanup") + if time.monotonic() >= deadline: + raise protocol.Refusal("browser qualification exceeded its 30-minute bound") + stopped.wait(.2) + retain(root / "final-public.json", wait_json(browser_origin, "/api/state", processes)) + except Exception as exc: + ledger["error"] = type(exc).__name__ + ": " + str(exc) + raise + finally: + # Stop planning first. The body remains available while the host settles shutdown. + for process in reversed(processes): + process.terminate() + try: + process.wait(timeout=40) + except subprocess.TimeoutExpired: + process.kill() + process.wait(timeout=5) + for record, process in zip(ledger["processes"], processes, strict=True): + record.update(exit_observed=process.poll() is not None, returncode=process.returncode) + for log in logs: + log.close() + ledger["owned_processes_exited"] = all(p.poll() is not None for p in processes) + retain(root / "process-ledger.json", ledger) + return ledger + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--source-commit", required=True) + args = parser.parse_args() + print(json.dumps(run(args.root, args.source_commit)), flush=True) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/qualify_order_handoff.py b/tools/physical-workcell/qualify_order_handoff.py new file mode 100644 index 0000000..4d7e72e --- /dev/null +++ b/tools/physical-workcell/qualify_order_handoff.py @@ -0,0 +1,407 @@ +"""Seven independent SIM5 API/adapter/native diagnostics after an explicit source freeze. + +Uses identified backend fixture transport helpers, never calls unit-test methods. +Planning uses public RGB only. SQLite scoring is separately labelled and read-only. +Actual browser read/bind/fulfill is separately driven by the parent. +""" +import argparse +import hashlib +import json +import os +import shutil +import socket +import sqlite3 +import subprocess +import time +import traceback +import urllib.parse +from pathlib import Path + +import order_protocol as protocol +from test_order_app import BASE, CONTROLLER, OrderApplicationTests, encoded, sha + +HANDOFF = BASE / 'app_handoff.py' +SOURCE_FILES = ('qualify_order_handoff.py', 'test_order_app.py', 'order_app.py', 'order_protocol.py', + 'order_app.html', 'app_handoff.py', 'controller.py', 'episode_runner.py', + 'workcell.py', 'image_guided.py', 'episode_cases.py', 'proposal.schema.json', 'scene.xml') + + +def freeze(commit): + """Bind the named SOURCE_FILES only; no transitive or live qualification claim.""" + repo = BASE.parents[1] + if subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=repo, text=True).strip() != commit: + raise ValueError('requested source freeze differs from current HEAD') + sources = {} + for name in SOURCE_FILES: + path = BASE / name + content = protocol.read_bytes(path) + relative = path.relative_to(repo).as_posix() + if subprocess.check_output(['git', 'show', f'{commit}:{relative}'], cwd=repo) != content: + raise ValueError('source differs from named commit: ' + relative) + sources[name] = sha(content) + return sources + + +def expected_socket_path(control): + import controller + + return controller.socket_path(control) + + +def bound_socket_path(control): + import controller + + control = protocol.directory(control) + binding = protocol.decode(protocol.read_bytes(control / 'controller-binding.json')) + expected = expected_socket_path(control) + if (not isinstance(binding, dict) or type(binding.get('version')) is not int or binding.get('version') != 1 + or binding.get('source_kind') != controller.body.SOURCE_KIND + or binding.get('controller_sha256') != protocol.sha(protocol.read_bytes(CONTROLLER)) + or binding.get('controller_dir') != str(control) or binding.get('socket_path') != str(expected)): + raise ValueError('controller/socket binding is invalid') + return expected + + +class Fixture(OrderApplicationTests): + def initialize(self, directory, name): + self.setUp() + self.directory.rename(directory) + self.directory = directory + self.app = directory / 'app' + self.order_id = 'qualification-' + name + self.work_order['order_id'] = self.order_id + self.work_order['project'] = {'namespace': 'diagnostic', 'project_id': 'sim5-independent'} + self.work_order['target_zone'] = {'x_min': .12, 'x_max': .20, 'y_min': -.035, 'y_max': .035} + self.seed = {**self.work_order, 'revision': 3, 'state': 'open'} + self.expected_native_pid = None + self.adapter_stopped = False + + def bind(self): + code, reply = self.http('POST', self.order_path('bind'), { + 'expected_revision': 3, 'binding_operation_id': 'bind-' + self.order_id}) + self.assertEqual(code, 200, reply) + self.lease = reply['lease'] + self.assertEqual(self.lease['app_id'], self.app_id) + self.assertEqual(self.lease['work_order'], self.work_order) + return self.lease + + def handoff(self, command, *args): + return self.command([str(BASE / '.venv/bin/python'), HANDOFF, command, + '--handoff-dir', self.lease['handoff_dir'], *args]) + + def prepare(self, *, placed=False, pace=0): + scene = {'block_xy': [.163, .004] if placed else [-.117, .004], + 'illumination_scale': 1, 'occlusion_fraction': 0, 'camera_offset_x_m': 0} + path = self.retain(self.directory / 'scene.json', scene) + prepared = self.command([str(BASE / '.venv/bin/python'), HANDOFF, 'prepare', '--app-url', self.origin, + '--expected-app-id', self.app_id, '--lease-id', self.lease['lease_id'], '--scene-file', path, + '--scene-sha256', sha(path.read_bytes()), '--pace-s', pace]) + self.assertEqual(prepared['phase'], 'prepared', prepared) + self.assertEqual(prepared['ownership'], 'owned') + self.control = Path(self.lease['controller_dir']) + self.incarnation = prepared['controller']['current_incarnation'] + self.expected_native_pid = prepared['controller']['pid'] + return prepared + + def observe(self): + reply = self.handoff('observe') + self.assertEqual(reply['phase'], 'ready', reply) + packet = reply['observation'] + self.event({'kind': 'public_rgb_planning_basis', 'generation': packet['generation'], + 'image_sha256': packet['image_sha256'], 'image_path': packet['image_path'], + 'state_sha256': packet['state_sha256'], 'task_intent': packet['task_intent']}) + return packet + + def submit(self, ident, proposal): + reply = self.handoff('submit', '--operation-id', ident, '--proposal-file', proposal[0], + '--proposal-sha256', proposal[1]) + self.assertIn(reply['phase'], {'accepted', 'running', 'executed', 'staged'}, reply) + return reply + + def stop_adapter(self): + stopped = self.handoff('stop') + self.assertTrue(stopped['process_termination_observed'], stopped) + self.adapter_stopped = True + return stopped + + def scoring(self): + # Scorer role begins here. No native poses or private controller journals are read. + order, count = self.db_rows() + database = self.app / 'orders.sqlite3' + with sqlite3.connect('file:' + str(database) + '?mode=ro', uri=True) as connection: + binding = json.loads(connection.execute("SELECT value FROM meta WHERE key='binding'").fetchone()[0]) + receipts = [dict(zip(('operation_id', 'order_id', 'lease_id', 'receipt_json'), row, strict=True)) + for row in connection.execute("SELECT operation_id,order_id,lease_id,receipt_json FROM operations WHERE kind='fulfill'")] + self.assertEqual(binding['app_id'], self.app_id) + self.assertEqual(binding['app_dir'], str(self.app)) + self.assertEqual(binding['database_identity'], [database.stat().st_dev, database.stat().st_ino]) + score = {'role': 'read_only_sqlite_scorer', 'order_row': order, 'fulfillment_count': count, + 'app_id': binding['app_id'], 'store_id': binding['store_id'], + 'database_identity': binding['database_identity'], 'source_fingerprints': binding['source_fingerprints'], + 'fulfillment_receipts': receipts} + self.event({'kind': 'separate_persisted_application_score', **score}) + return score + + def cleanup(self): + errors = [] + if self.expected_native_pid is not None and not self.adapter_stopped: + try: + self.stop_adapter() + except (AssertionError, KeyError, OSError, ValueError, subprocess.TimeoutExpired) as exc: + errors.append(str(exc)) + self.event({'kind': 'adapter_cleanup_unconfirmed', 'expected_owned_pid': self.expected_native_pid, + 'reason': str(exc), 'unconfirmed_process_not_blindly_signalled': True}) + for process in reversed(self.processes): + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=8) + except subprocess.TimeoutExpired: + process.kill() + process.wait(timeout=4) + for handle in self.handles: + handle.close() + bound, socket_exists = None, None + if self.control is not None: + try: + bound = bound_socket_path(self.control) + try: + bound.lstat() + socket_exists = True + except FileNotFoundError: + socket_exists = False + if socket_exists: + errors.append('bound controller socket remains after cleanup') + except (OSError, ValueError) as exc: + errors.append('controller socket cleanup unconfirmed: ' + str(exc)) + self.event({'kind': 'owned_process_cleanup', + 'processes': [{'pid': p.pid, 'exit_code': p.poll()} for p in self.processes], + 'all_parent_handles_exited': all(p.poll() is not None for p in self.processes), + 'adapter_stop_observed': self.adapter_stopped, + 'socket_path': str(bound) if bound is not None else None, + 'socket_exists': socket_exists, + 'socket_absence_verified': bound is not None and socket_exists is False}) + if errors: + raise RuntimeError('adapter cleanup unconfirmed: ' + '; '.join(errors)) + + +def normal(f): + f.start() + f.bind() + f.prepare() + staged, contacts = f.stage() + f.assertGreater(contacts, 0) + code, fulfilled = f.fulfill() + f.assertEqual(code, 200, fulfilled) + reconciled = f.handoff('reconcile') + f.assertEqual(reconciled['phase'], 'fulfilled', reconciled) + f.assertEqual(reconciled['receipt'], fulfilled['receipt']) + score = f.scoring() + f.assertEqual((score['order_row'][1], score['order_row'][2], score['fulfillment_count']), (4, 'fulfilled', 1)) + return {'expected_outcome': 'fulfilled', 'contact_samples': contacts, 'physical_receipt': staged, + 'application_receipt': fulfilled, 'sqlite_score': score, 'browser_scope': 'HTTP-only; parent owns actual browser case'} + + +def changed(f, *, cancelled=False): + f.start() + f.bind() + f.prepare(pace=.003) + packet = f.observe() + ident = 'admitted-before-app-change' + f.submit(ident, f.proposal(packet, ident)) + f.until(ident, {'running'}) + time.sleep(.4) + action = 'cancel' if cancelled else 'edit' + mutation = {'expected_revision': 3} + if not cancelled: + mutation['target_zone'] = {'x_min': .15, 'x_max': .23, 'y_min': -.035, 'y_max': .035} + code, app = f.http('POST', f.order_path(action), mutation) + f.assertEqual(code, 200, app) + guard = f.handoff('guard') + f.assertFalse(guard['guard_current']) + f.assertTrue(guard['physical_effects_may_be_partial']) + f.assertFalse(guard['rollback_claim']) + stopped = f.until(ident) + f.assertEqual(stopped['phase'], 'cancelled', stopped) + f.assertTrue(stopped['stop_evidence']['stop_verified']) + f.assertTrue(stopped['stop_evidence']['state_retained']) + f.assertGreater(stopped['stop_evidence']['native_steps_before_cancel'], 0) + current_packet = f.controller('observe')['observation'] + f.assertEqual(stopped['stop_evidence']['retained_generation'], current_packet['generation']) + f.assertEqual(stopped['stop_evidence']['retained_state_sha256'], current_packet['state_sha256']) + f.event({'kind': 'read_only_cancel_stop_scoring', 'generation': current_packet['generation'], + 'state_sha256': current_packet['state_sha256'], 'after_application_guard': 'withheld'}) + blocked = f.handoff('submit', '--operation-id', 'after-change', '--proposal-file', + f.proposal(packet, 'after-change')[0], '--proposal-sha256', + sha((f.directory / 'proposal-after-change.json').read_bytes())) + f.assertTrue(blocked['new_submissions_withheld'], blocked) + f.assertNotEqual(f.fulfill()[0], 200) + score = f.scoring() + f.assertEqual((score['order_row'][1], score['order_row'][2], score['fulfillment_count']), + (4, 'cancelled' if cancelled else 'open', 0)) + return {'expected_outcome': 'stale_fulfillment_refused', 'app_mutation': app, + 'guard': guard, 'retained_cancelled_receipt': stopped, 'sqlite_score': score} + + +def lost_reply(f): + f.start() + f.bind() + f.prepare(placed=True) + f.stage() + content = encoded({'lease_id': f.lease['lease_id'], 'completion_operation_id': f.lease['completion_operation_id']}) + port = urllib.parse.urlsplit(f.origin).port + request = (f'POST {f.order_path("fulfill")} HTTP/1.1\r\nHost: 127.0.0.1:{port}\r\nOrigin: {f.origin}\r\n' + f'X-Footwork-App-ID: {f.app_id}\r\nContent-Type: application/json\r\nContent-Length: {len(content)}\r\n' + 'Connection: close\r\n\r\n').encode() + content + with socket.create_connection(('127.0.0.1', port), timeout=5) as caller: + caller.sendall(request) + f.event({'kind': 'actual_http_socket_closed_before_reply', 'reply_consumed': False, + 'app_id': f.app_id, 'completion_operation_id': f.lease['completion_operation_id']}) + deadline = time.monotonic() + 6 + while time.monotonic() < deadline: + code, saved = f.http('GET', '/api/operations/' + f.lease['completion_operation_id']) + if code == 200: + break + time.sleep(.05) + f.assertEqual(code, 200, saved) + f.stop_adapter() + code, repeated = f.fulfill() + f.assertEqual(code, 200, repeated) + f.assertEqual(saved['receipt'], repeated['receipt']) + f.assertEqual(f.handoff('reconcile')['receipt'], saved['receipt']) + conflicting_code, conflicting = f.fulfill(lease_id='0' * 32) + f.assertNotEqual(conflicting_code, 200, conflicting) + score = f.scoring() + f.assertEqual((score['order_row'][1], score['fulfillment_count']), (4, 1)) + return {'expected_outcome': 'one_effect_historical_reconciliation', 'readback': saved, + 'repeat_after_controller_stop': repeated, 'sqlite_score': score} + + +def identities(f): + f.start() + f.bind() + f.assertNotEqual(f.fulfill()[0], 200) # No physical controller/evidence exists. + f.prepare(placed=True) + f.stage() + original = f.app, f.origin, f.app_id, f.lease + f.app = f.directory / 'foreign-app' + f.initialize_app() + f.serve_app() + foreign = f.bind() + foreign_app_id = f.app_id + f.app, f.origin, f.app_id, f.lease = original + f.assertNotEqual(f.fulfill(lease_id=foreign['lease_id'])[0], 200) + for fields in ({'success': True}, {'receipt_path': '/tmp/unassigned-receipt'}, + {'completion_operation_id': f.lease['binding_operation_id']}, + {'completion_operation_id': 'wrong-assigned-operation'}): + f.assertNotEqual(f.fulfill(**fields)[0], 200) + for identity in ('', foreign_app_id): + code, _ = f.http('POST', f.order_path('fulfill'), { + 'lease_id': f.lease['lease_id'], 'completion_operation_id': f.lease['completion_operation_id']}, + extra_headers={'X-Footwork-App-ID': identity}) + f.assertNotEqual(code, 200) + wrong_order, _ = f.http('POST', f.order_path().replace(f.order_id, f.order_id + '-other') + '/fulfill', { + 'lease_id': f.lease['lease_id'], 'completion_operation_id': f.lease['completion_operation_id']}) + f.assertNotEqual(wrong_order, 200) + score = f.scoring() + f.assertEqual((score['order_row'][1], score['order_row'][2], score['fulfillment_count']), (3, 'open', 0)) + return {'expected_outcome': 'wrong_identity_flags_paths_and_unsupported_evidence_refused', 'sqlite_score': score} + + +def restart(f): + app_process, initial_app = f.start() + f.bind() + prepared = f.prepare() + staged, _ = f.stage() + before = f.observe() + f.stop_adapter() + app_process.terminate() + app_process.wait(timeout=8) + _, current_app = f.serve_app(restart=True) + f.assertEqual(initial_app['app_id'], current_app['app_id']) + f.native = f.spawn([str(BASE / '.venv/bin/python'), CONTROLLER, 'serve', '--run-dir', f.lease['run_dir'], + '--controller-dir', f.control], 'independent-controller-restart') + current = f.native_ready() + f.assertNotEqual(current['current_incarnation'], prepared['controller']['current_incarnation']) + retained = f.controller('status', '--operation-id', f.lease['physical_completion_id']) + f.assertEqual(retained['receipt'], staged['receipt']) + after = f.controller('observe')['observation'] + f.assertEqual((before['generation'], before['state_sha256']), (after['generation'], after['state_sha256'])) + code, fulfilled = f.fulfill() + f.assertEqual(code, 200, fulfilled) + score = f.scoring() + f.assertEqual((score['order_row'][1], score['fulfillment_count']), (4, 1)) + return {'expected_outcome': 'same_identity_history_no_redispatch', 'before_packet': before, + 'after_packet': after, 'retained_physical': retained, 'app_receipt': fulfilled, 'sqlite_score': score} + + +def artifact(f): + f.start() + f.bind() + f.prepare(placed=True) + staged, _ = f.stage() + current_file = Path(f.lease['run_dir']) / 'order.json' + preserved = f.directory / 'original-physical-order.json' + shutil.copyfile(current_file, preserved) + preserved.chmod(0o600) + with current_file.open('ab') as sink: + sink.write(b' ') + f.event({'kind': 'controlled_current_artifact_byte_fault', 'path': str(current_file), + 'preserved_original': str(preserved), 'native_state_not_modified': True}) + current = f.controller('status', '--operation-id', f.lease['physical_completion_id']) + f.assertEqual(current['phase'], 'staged') + f.assertEqual(current['receipt'], staged['receipt']) + f.assertEqual(current['current_health'], 'blocked') + f.assertNotEqual(f.fulfill()[0], 200) + score = f.scoring() + f.assertEqual((score['order_row'][1], score['order_row'][2], score['fulfillment_count']), (3, 'open', 0)) + return {'expected_outcome': 'historical_stage_current_artifact_refuses_new_fulfillment', + 'current_physical_status': current, 'sqlite_score': score} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--output-dir', required=True) + parser.add_argument('--source-commit', required=True) + args = parser.parse_args() + output = Path(os.path.abspath(args.output_dir)) + if BASE / 'output' not in output.parents or output.exists(): + raise ValueError('qualification needs a new owned output directory; no episode rerun') + commit = args.source_commit + sources = freeze(commit) + output.mkdir(mode=0o700) + result = {'source_commit': commit, 'source_sha256': sources, 'fixture_source': 'test_order_app.OrderApplicationTests transport helpers', + 'classification': 'independent public API/adapter/native integration diagnostics; no AI benchmark', + 'browser_scope': 'parent separately owns actual browser evidence', 'cases': []} + scenarios = [('01-normal-http-native', normal), ('02-edit-during-motion', changed), + ('03-cancel-during-motion', lambda f: changed(f, cancelled=True)), ('04-lost-app-reply', lost_reply), + ('05-identity-and-authority', identities), ('06-restart-history', restart), ('07-current-artifact', artifact)] + for name, scenario in scenarios: + fixture = Fixture('test_real_contact_fulfillment_commits_exact_version_and_receipt') + case = {'case_id': name, 'passed': False} + try: + fixture.initialize(output / name, name) + case['evidence'] = scenario(fixture) + case['passed'] = True + except Exception as exc: # noqa: BLE001 - retain each failed attempt, no effect retry + case.update(error=str(exc), traceback=traceback.format_exc()) + finally: + try: + fixture.cleanup() + except Exception as exc: # noqa: BLE001 - retain owned-resource cleanup failure + case.update(passed=False, cleanup_error=str(exc)) + case['events'] = fixture.events + fixture.retain(output / name / 'case-result.json', case) + result['cases'].append(case) + result['passed'] = sum(item['passed'] for item in result['cases']) + result['attempted'] = len(result['cases']) + fixture.retain(output / 'qualification.json', result) + print(json.dumps({'case_id': name, 'passed': case['passed'], 'error': case.get('error')}), flush=True) + result['final_source_sha256'] = {name: hashlib.sha256((BASE / name).read_bytes()).hexdigest() for name in SOURCE_FILES} + result['source_unchanged'] = result['final_source_sha256'] == sources + fixture.retain(output / 'qualification.json', result) + return 0 if result['passed'] == 7 and result['source_unchanged'] else 1 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/tools/physical-workcell/qualify_workbench_crashes.py b/tools/physical-workcell/qualify_workbench_crashes.py new file mode 100644 index 0000000..9f5fc14 --- /dev/null +++ b/tools/physical-workcell/qualify_workbench_crashes.py @@ -0,0 +1,610 @@ +"""Owned SIGKILL browser trials; all model and product actions come from the UI.""" +import argparse +import contextlib +import http.client +import json +import os +import signal +import subprocess +import threading +import time +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from urllib.parse import urlsplit + +import body_client +import order_protocol as protocol +import qualify_workbench_restarts as previous + +BASE, REPO = previous.BASE, previous.REPO +CASES = {"host-inflight": "workbench", "body-native-gap": "body", "body-uncertain": "body", "host-uncertain": "workbench"} +SOURCES = list(dict.fromkeys(previous.SOURCES + [BASE / name for name in ( + "crash_body.py", "qualify_workbench_crashes.py", "score_workbench_crashes.py", + "test_workbench_crash_harness.py")])) +read, retain, get = previous.read, previous.retain, previous.get + + +def descendants(pid): + rows = subprocess.check_output(["ps", "-axo", "pid=,ppid=,pgid=,comm="], text=True) + parsed = [] + for line in rows.splitlines(): + parts = line.strip().split(None, 3) + if len(parts) == 4: + parsed.append({"pid": int(parts[0]), "parent": int(parts[1]), "group": int(parts[2]), "command": parts[3]}) + selected, parents = [], {pid} + while True: + new = [row for row in parsed if row["parent"] in parents and row["pid"] not in parents] + if not new: + return selected + selected.extend(new) + parents.update(row["pid"] for row in new) + + +def indexed_command(root, token): + protocol.identity(token) + directory = root / "body" / "commands" / protocol.sha(token.encode()) + intent = read(directory / "intent.json") + request = intent["request"] + if request["command_id"] != token or intent["request_sha256"] != protocol.sha(protocol.encoded(request)): + raise protocol.Refusal("receiver command digest differs from its exact identity") + terminal = None + valid = intent["result_sha256"] is None + if intent["result_sha256"] is not None: + content = protocol.read_bytes(directory / "result.json") + terminal = protocol.decode(content) + valid = (protocol.sha(content) == intent["result_sha256"] and + terminal["request_sha256"] == intent["request_sha256"] and + all(terminal[key] == request[key] for key in ("command_id", "body_id", "adapter_id", "source_sha256"))) + if not valid: + raise protocol.Refusal("receiver terminal receipt is not indexed to its exact command") + return {"intent": intent, "terminal": terminal, "digest_valid": valid} + + +class HeldReplyRelay: + """Diagnostic single-send relay; first executed dispatch waits for host kill.""" + + def __init__(self, upstream, root, hold_first_dispatch=True): + body_client.endpoint(upstream) + self.upstream, self.root = upstream, protocol.directory(root, create=True) + if any(self.root.iterdir()): + raise protocol.Refusal("relay requires a new owned directory") + (self.root / "requests").mkdir(mode=0o700) + self.lock, self.release = threading.Lock(), threading.Event() + self.sequence = self.active = self.peak = 0 + if type(hold_first_dispatch) is not bool: + raise protocol.Refusal("relay hold selection must be an exact boolean") + self.held = not hold_first_dispatch + + def start(self): + owner = self + + class Server(ThreadingHTTPServer): + daemon_threads = False + block_on_close = True + + def __init__(self, address, handler): + self.capacity = threading.BoundedSemaphore(8) + super().__init__(address, handler) + + def process_request(self, request, address): + if not self.capacity.acquire(False): + self.shutdown_request(request) + return + try: + super().process_request(request, address) + except BaseException: + self.capacity.release() + raise + + def process_request_thread(self, request, address): + try: + super().process_request_thread(request, address) + finally: + self.capacity.release() + + class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def log_message(self, *_): + pass + + def do_GET(self): + self.forward() + + def do_POST(self): + self.forward() + + def forward(self): + folder = None + with owner.lock: + owner.active += 1 + owner.peak = max(owner.peak, owner.active) + try: + self.connection.settimeout(15) + parsed = urlsplit(self.path) + if (self.headers.get_all("Host") != [owner.origin.removeprefix("http://")] or + self.headers.get_all("Origin") or self.headers.get_all("Transfer-Encoding") or + parsed.scheme or parsed.netloc or parsed.fragment or not self.path.startswith("/")): + raise protocol.Refusal("relay requires exact closed loopback framing") + allowed_read = parsed.path in ("/identity", "/state") or parsed.path.startswith(("/commands/", "/images/")) + if self.command == "GET" and not allowed_read or self.command == "POST" and self.path not in ("/lease", "/commands"): + raise protocol.Refusal("unsupported relay route") + lengths = self.headers.get_all("Content-Length", []) + if len(lengths) > 1 or lengths and (not lengths[0].isascii() or not lengths[0].isdigit() or int(lengths[0]) > protocol.MAX_BYTES): + raise protocol.Refusal("relay payload is not bounded") + length = int(lengths[0]) if lengths else 0 + if self.command == "GET" and length or self.command == "POST" and (length < 1 or self.headers.get_all("Content-Type") != ["application/json"]): + raise protocol.Refusal("relay requires an exact bounded JSON body") + payload = self.rfile.read(length) + if len(payload) != length: + raise protocol.Refusal("relay body is incomplete") + headers = {key: value for key, value in self.headers.items() if key.lower() not in {"host", "connection", "content-length"}} + request = protocol.decode(payload) if self.path == "/commands" else None + with owner.lock: + if owner.sequence >= 4096: + raise protocol.Refusal("relay request budget exhausted") + owner.sequence += 1 + folder = owner.root / "requests" / f"{owner.sequence:06d}" + folder.mkdir(mode=0o700) + sequence = owner.sequence + protocol.create_file(folder / "request.bin", payload) + retain(folder / "request.json", {"sequence": sequence, "method": self.command, "path": self.path, + "headers": headers, "payload_sha256": protocol.sha(payload), "started_time": time.time(), + "command_id": request["command_id"] if request else None, "upstream_sends": 1}) + status, content = body_client.http_call(owner.upstream, self.command, self.path, headers, + payload if self.command == "POST" else None, timeout=15, maximum=body_client.MAX_IMAGE) + protocol.create_file(folder / "response.bin", content) + retain(folder / "response.json", {"status": status, "sha256": protocol.sha(content), "time": time.time()}) + if request and request["operation"] == "dispatch" and status == 200: + result = protocol.decode(content) + with owner.lock: + hold = not owner.held and result["state"] == "completed" and result["result"]["outcome"] == "executed" + if hold: + owner.held = True + if hold: + retain(owner.root / "barrier.json", {"version": 1, "barrier": "body_receipt_before_link_reply", + "command_id": request["command_id"], "request_sha256": protocol.sha(payload), + "response_sha256": protocol.sha(content), "proposal_id": request["arguments"]["proposal_id"], + "body_id": request["body_id"], "epoch": request["epoch"], "relay_sequence": sequence, "time": time.time()}) + if not owner.release.wait(10): + raise protocol.Refusal("held reply gate exceeded ten seconds without owned crash") + self.send_response(status) + self.send_header("Content-Type", "image/png" if self.path.startswith("/images/") else "application/json") + self.send_header("Content-Length", str(len(content))) + self.send_header("Connection", "close") + self.end_headers() + self.wfile.write(content) + retain(folder / "result.json", {"delivered": True, "time": time.time()}) + except (OSError, http.client.HTTPException, protocol.Refusal, ValueError, TypeError, KeyError) as exc: + if folder and not (folder / "result.json").exists(): + retain(folder / "result.json", {"delivered": False, "error": str(exc), "time": time.time()}) + self.close_connection = True + finally: + self.close_connection = True + with owner.lock: + owner.active -= 1 + + self.server = Server(("127.0.0.1", 0), Handler) + self.origin = f"http://127.0.0.1:{self.server.server_port}" + self.thread = threading.Thread(target=self.server.serve_forever, kwargs={"poll_interval": .05}) + self.thread.start() + retain(self.root / "ready.json", {"origin": self.origin, "upstream": self.upstream}) + return self + + def close(self): + self.release.set() + self.server.shutdown() + self.thread.join(3) + self.server.server_close() + retain(self.root / "cleanup.json", {"active_workers": self.active, "peak_workers": self.peak, + "server_thread_exited": not self.thread.is_alive(), "request_count": self.sequence}) + + +def freeze(root, commit): + if subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=REPO, text=True).strip() != commit: + raise protocol.Refusal("actual trial requires its committed source head") + files = {} + for path in SOURCES: + name, content = str(path.relative_to(REPO)), protocol.read_bytes(path) + if subprocess.check_output(["git", "show", f"{commit}:{name}"], cwd=REPO) != content: + raise protocol.Refusal("source is not frozen: " + name) + files[name] = protocol.sha(content) + retain(root / "source-freeze.json", {"commit": commit, "files": files}) + + +def expectation(public): + return {"app_id": public["app_id"], "body_id": public["connection"]["body_id"], + "epoch": public["connection"]["epoch"], "runner_status": public["runner_status"], + "active_job_id": public["active_job_id"], "command_id": public["recovery"]["command_id"], + "model_request_pending": public["model_request_pending"]} + + +def validate_control(value): + protocol.fields(value, ("sequence", "action", "role", "expect", "requested_time"), "crash control") + if type(value["sequence"]) is not int or not 1 <= value["sequence"] <= 64: + raise protocol.Refusal("control sequence must be bounded") + if value["action"] not in ("crash-role", "start-role", "finish") or value["role"] not in (None, "body", "workbench"): + raise protocol.Refusal("unsupported owned crash action") + if (value["action"] == "finish") != (value["role"] is None): + raise protocol.Refusal("role must match this control action") + protocol.finite(value["requested_time"]) + if value["action"] == "crash-role": + protocol.fields(value["expect"], ("app_id", "body_id", "epoch", "runner_status", "active_job_id", "command_id", "model_request_pending"), "crash expectation") + if value["expect"]["runner_status"] != "blocked" or value["expect"]["model_request_pending"] is not False: + raise protocol.Refusal("crash requires the exact uncertain idle-model boundary") + for key in ("app_id", "body_id", "active_job_id", "command_id"): + protocol.identity(value["expect"][key]) + protocol.revision(value["expect"]["epoch"]) + elif value["expect"] is not None: + raise protocol.Refusal("only crashes carry an exact fresh expectation") + return value + + +def request(root, action, role=None): + root = protocol.directory(root) + ready = read(root / "ready.json") + public = get(ready["browser_origin"], "/api/state") if action == "crash-role" else None + expected = expectation(public) if public else None + if action == "crash-role": + if ready["case"] not in ("body-uncertain", "host-uncertain") or CASES[ready["case"]] != role: + raise protocol.Refusal("manual crash does not match this uncertain scenario") + receiver = indexed_command(root, expected["command_id"]) + if (not receiver["terminal"] or receiver["terminal"]["state"] != "completed" or + receiver["terminal"].get("result", {}).get("outcome") != "executed"): + raise protocol.Refusal("uncertain scenario requires an indexed completed receiver receipt") + sequence = max((int(path.name.split(".")[0]) for path in (root / "control").glob("*.request.json")), default=0) + 1 + value = validate_control({"sequence": sequence, "action": action, "role": role, "expect": expected, "requested_time": time.time()}) + retain(root / "control" / f"{sequence:06d}.request.json", value) + return value + + +class Trial(previous.Trial): + def __init__(self, root, commit, case, body_port=0, browser_port=0): + if case not in CASES: + raise protocol.Refusal("unsupported hard crash case") + super().__init__(root, commit, body_port, browser_port) + self.case, self.relay = case, None + self.boundary = None + self.ledger["case"] = case + self.snapshot_sequence = 0 + + def launch(self, role, initialize=False): + if role != "body" or not initialize or self.case != "body-native-gap": + process = super().launch(role, initialize) + else: + log = (self.root / "body-01.log").open("xb") + process = subprocess.Popen([str(BASE / ".venv/bin/python"), str(BASE / "crash_body.py"), "--root", str(self.root / "body"), + "--port", str(self.body_port), "--initialize", "--diagnostic", "--gate", "after_native"], cwd=REPO, + stdout=log, stderr=log, start_new_session=True) + record = {"role": role, "boot": 1, "pid": process.pid, "root": str(self.root / "body"), + "port": self.body_port, "initialized": True, "log": "body-01.log"} + self.processes.append((process, record)) + self.logs.append(log) + self.ledger["processes"].append(record) + self.active[role] = process + record = next(entry for handle, entry in self.processes if handle is process) + record["group"] = os.getpgid(process.pid) + if record["group"] != process.pid: + raise protocol.Refusal("owned trial process did not create its own group") + return process + + def wait_public(self, origin, path): + deadline = time.monotonic() + 30 + while time.monotonic() < deadline: + try: + public = get(origin, path) + if origin == self.body_origin: + pinned = read(self.root / "body" / "identity.json") + epoch = read(self.root / "body" / "service.json")["epoch"] + expected = {**pinned, "epoch": epoch} + else: + expected = read(self.root / "operator" / "identity.json") + if origin == self.body_origin and public != expected: + raise protocol.Refusal("endpoint identity does not match this owned body root and boot") + if origin == self.browser_origin and public["app_id"] != expected["app_id"]: + raise protocol.Refusal("endpoint application does not match this owned workbench root") + if any(process is not None and process.poll() is not None for process in self.active.values()): + raise protocol.Refusal("owned role exited while endpoint identity was being checked") + return public + except (OSError, http.client.HTTPException): + if any(process is not None and process.poll() is not None for process in self.active.values()): + raise protocol.Refusal("owned role exited during readiness") + time.sleep(.05) + raise protocol.Refusal("owned endpoint readiness exceeded thirty seconds") + + def snapshot(self, label, token=None, body_reads=True): + self.snapshot_sequence += 1 + value = {"label": label, "time": time.time(), "public": None, "body_identity": None, "body_state": None, + "processes": {role: p.pid if p is not None and p.poll() is None else None for role, p in self.active.items()}} + for key, origin, path in (("public", self.browser_origin, "/api/state"), + ("body_identity", self.body_origin, "/identity"), ("body_state", self.body_origin, "/state")): + if key != "public" and not body_reads: + continue + try: + value[key] = get(origin, path, self.identity if key == "body_state" else None) + except (OSError, ValueError, http.client.HTTPException) as exc: + value[key + "_error"] = str(exc) + host = read(self.root / "operator" / "operator.json") + client = body_client.read_journal(self.root / "operator" / "client" / "client.json") + value["host"] = {key: host[key] for key in ("app_id", "body_identity", "stage", "frame", "runner_status", "sequence", "usage")} + commands = {} + for command_id, entry in client["commands"].items(): + content = protocol.read_bytes(self.root / "operator" / "client" / "requests" / (command_id + ".json")) + if protocol.sha(content) != entry["request_sha256"]: + raise protocol.Refusal("retained client request changed") + commands[command_id] = {"request_sha256": entry["request_sha256"], "send_count": entry["send_count"], + "state": entry["state"], "operation": protocol.decode(content)["operation"]} + value["client"] = {"commands": commands, "pending_command_ids": client["pending_command_ids"], + "lease": client["lease"], "identity": client["identity"]} + value["posts"] = [] + for path in sorted((self.root / "operator" / "link" / "events").glob("*.json")): + event = read(path) + if event["kind"] == "received" and event.get("method") == "POST" and event.get("command_id"): + value["posts"].append({key: event[key] for key in ("sequence", "command_id", "payload_sha256")}) + value["body_command"] = indexed_command(self.root, token) if token else None + name = f"{self.snapshot_sequence:06d}-{label}.json" + retain(self.root / "snapshots" / name, value) + return name, value + + def kill(self, role): + process = self.active.get(role) + record = next((entry for handle, entry in self.processes if handle is process), None) + if (process is None or process.poll() is not None or record is None or record["pid"] != process.pid or + record["role"] != role or record.get("group") != process.pid or os.getpgid(process.pid) != process.pid): + raise protocol.Refusal("SIGKILL requires this trial's exact live owned process group") + children = descendants(process.pid) + if children: + raise protocol.Refusal("crash gate still has a live child process; no planner may survive the boundary") + os.killpg(process.pid, signal.SIGKILL) + process.wait(timeout=5) + if process.returncode != -signal.SIGKILL: + raise protocol.Refusal("owned crash did not exit by SIGKILL") + record.update(exit_observed=True, returncode=process.returncode, stopped_time=time.time(), signal="SIGKILL", + children_before=children, children_after=descendants(process.pid)) + self.active[role] = None + return dict(record) + + def crash(self, role, token, gate=None, expected=None): + if self.boundary or role != CASES[self.case]: + raise protocol.Refusal("this case permits one exact owned hard crash") + receiver = indexed_command(self.root, token) + if receiver["intent"]["request"]["operation"] != "dispatch": + raise protocol.Refusal("crash must be bound to a dispatch") + if self.case != "body-native-gap" and (not receiver["terminal"] or receiver["terminal"]["state"] != "completed" or + receiver["terminal"].get("result", {}).get("outcome") != "executed"): + raise protocol.Refusal("recoverable crash requires a known indexed body result") + if gate: + if gate["command_id"] != token or gate["request_sha256"] != receiver["intent"]["request_sha256"]: + raise protocol.Refusal("gate differs from exact accepted receiver command") + if role == "body" and gate["pid"] != self.active[role].pid: + raise protocol.Refusal("native gate belongs to another body process") + if role == "workbench" and gate["response_sha256"] != receiver["intent"]["result_sha256"]: + raise protocol.Refusal("held reply is not the receiver's indexed terminal result") + before_name, before = self.snapshot("before", token, body_reads=gate is None) + if expected is not None: + public = get(self.browser_origin, "/api/state") + if expectation(public) != expected or before["public"] is None or expectation(before["public"]) != expected: + raise protocol.Refusal("uncertain state changed immediately before owned SIGKILL") + process = self.kill(role) + if self.relay: + self.relay.release.set() + # Wait only on the still-running host; never query a SIGSTOP/dead body. + if role == "body": + until = time.monotonic() + 20 + while time.monotonic() < until: + public = get(self.browser_origin, "/api/state") + if public["runner_status"] != "running" and public["recovery"]["command_id"] == token and not public["model_request_pending"]: + break + time.sleep(.05) + else: + raise protocol.Refusal("surviving host did not retain the crashed command as uncertain") + offline_name, _ = self.snapshot("offline", token, body_reads=False) + self.boundary = {"case": self.case, "role": role, "command_id": token, + "request_sha256": receiver["intent"]["request_sha256"], "gate": gate, "pre_snapshot": before_name, + "offline_snapshot": offline_name, "restored_snapshot": None, "process": process, + "old_epoch": receiver["intent"]["request"]["epoch"], "new_epoch": None} + self.stops[role] = True + protocol.persist(self.root / "crash-ledger.json", {"case": self.case, "boundaries": [self.boundary]}) + + def control(self, command): + validate_control(command) + action, role = command["action"], command["role"] + if action == "crash-role": + if self.case not in ("body-uncertain", "host-uncertain"): + raise protocol.Refusal("this scenario has an automatic gate, not a manual crash") + self.crash(role, command["expect"]["command_id"], expected=command["expect"]) + elif action == "start-role": + if not self.boundary or role != self.boundary["role"] or role not in self.stops or self.active.get(role) is not None: + raise protocol.Refusal("restart requires this case's exact reaped role") + self.launch(role) + self.wait_public(self.body_origin if role == "body" else self.browser_origin, "/identity" if role == "body" else "/api/state") + body_current = self.wait_public(self.body_origin, "/identity") + if any(body_current[key] != self.identity[key] for key in body_client.IDENTITY_KEYS - {"epoch"}): + raise protocol.Refusal("restarted endpoint belongs to another pinned body") + until = time.monotonic() + 15 + while time.monotonic() < until: + public = get(self.browser_origin, "/api/state") + if (public["runner_status"] != "running" and public["recovery"]["command_id"] == self.boundary["command_id"] and + not public["model_request_pending"] and public["connection"]["epoch"] == body_current["epoch"] and + public["connection"]["body_id"] == self.identity["body_id"] and public["connection"]["adapter_id"] == self.identity["adapter_id"]): + break + time.sleep(.05) + else: + raise protocol.Refusal("restart silently lost uncertainty or resumed planning") + name, snapshot = self.snapshot("restored", self.boundary["command_id"]) + self.boundary.update(restored_snapshot=name, new_epoch=snapshot["body_identity"]["epoch"]) + self.stops.pop(role) + protocol.persist(self.root / "crash-ledger.json", {"case": self.case, "boundaries": [self.boundary]}) + else: + if not self.boundary or self.boundary["restored_snapshot"] is None: + raise protocol.Refusal("finish requires the recorded crash and explicit restart") + name, _ = self.snapshot("finished", self.boundary["command_id"]) + self.boundary["finished_snapshot"] = name + protocol.persist(self.root / "crash-ledger.json", {"case": self.case, "boundaries": [self.boundary]}) + retain(self.root / "control" / f"{command['sequence']:06d}.result.json", {"request": command, "passed": True, "time": time.time()}) + return action == "finish" + + def run(self): + freeze(self.root, self.commit) + for name in ("control", "snapshots"): + (self.root / name).mkdir(mode=0o700) + self.body_port = self.body_port or previous.previous.available_port() + self.browser_port = self.browser_port or previous.previous.available_port() + if self.body_port == self.browser_port or any(type(port) is not int or not 1 <= port <= 65535 for port in (self.body_port, self.browser_port)): + raise protocol.Refusal("body and browser ports must be distinct bounded owned endpoints") + self.body_origin, self.browser_origin = f"http://127.0.0.1:{self.body_port}", f"http://127.0.0.1:{self.browser_port}" + stopped = threading.Event() + signal.signal(signal.SIGTERM, lambda *_: stopped.set()) + signal.signal(signal.SIGINT, lambda *_: stopped.set()) + try: + self.launch("body", initialize=True) + self.identity = self.wait_public(self.body_origin, "/identity") + retain(self.root / "identity.json", self.identity) + upstream = self.body_origin + self.relay = HeldReplyRelay(self.body_origin, self.root / "relay", hold_first_dispatch=self.case == "host-inflight").start() + upstream = self.relay.origin + log = (self.root / "initialization.log").open("xb") + self.logs.append(log) + init = subprocess.Popen([str(BASE / ".venv/bin/python"), str(BASE / "network_bench_operator.py"), "init", + "--root", str(self.root / "operator"), "--body-origin", upstream], cwd=REPO, stdout=log, stderr=log, start_new_session=True) + record = {"role": "initialization", "pid": init.pid, "group": init.pid} + self.processes.append((init, record)) + self.ledger["processes"].append(record) + if init.wait(timeout=30): + raise protocol.Refusal("owned workbench initialization failed") + self.launch("workbench") + public = self.wait_public(self.browser_origin, "/api/state") + retain(self.root / "initial-public.json", public) + self.ledger.update(ready=True, body_origin=self.body_origin, browser_origin=self.browser_origin, + app_id=public["app_id"], body_id=self.identity["body_id"], case=self.case) + retain(self.root / "ready.json", self.ledger) + print(json.dumps(self.ledger), flush=True) + deadline, handled, done = time.monotonic() + 1200, set(), False + while not stopped.is_set() and not done: + if time.monotonic() >= deadline: + raise protocol.Refusal("hard crash case exceeded its twenty-minute bound") + if any(process is not None and process.poll() is not None for process in self.active.values()): + raise protocol.Refusal("owned role exited without its exact crash control") + if not self.boundary and self.case in ("host-inflight", "body-native-gap"): + gate_path = self.root / ("relay" if self.case == "host-inflight" else "body") / "barrier.json" + if gate_path.exists(): + gate = read(gate_path) + self.crash(CASES[self.case], gate["command_id"], gate) + for path in sorted((self.root / "control").glob("*.request.json")): + if path.name in handled: + continue + command = read(path) + if path.name != f"{command['sequence']:06d}.request.json": + raise protocol.Refusal("control filename and sequence disagree") + handled.add(path.name) + done = self.control(command) + if done: + break + stopped.wait(.05) + retain(self.root / "final-public.json", get(self.browser_origin, "/api/state")) + except BaseException as exc: + self.ledger["error"] = type(exc).__name__ + ": " + str(exc) + raise + finally: + for role in ("workbench",): + if self.active.get(role) is not None and self.active[role].poll() is None: + with contextlib.suppress(OSError, protocol.Refusal, subprocess.SubprocessError): + self.stop(role) + if self.relay: + self.relay.close() + if self.active.get("body") is not None and self.active["body"].poll() is None: + with contextlib.suppress(OSError, protocol.Refusal, subprocess.SubprocessError): + self.stop("body") + for process, record in self.processes: + if process.poll() is None: + os.killpg(process.pid, signal.SIGKILL) + process.wait(timeout=5) + self.ledger["cleanup_error"] = "a role required forced cleanup after its ordinary stop" + record.update(exit_observed=process.poll() is not None, returncode=process.returncode) + for log in self.logs: + log.close() + self.ledger["owned_processes_exited"] = all(process.poll() is not None for process, _ in self.processes) + retain(self.root / "process-ledger.json", self.ledger) + return self.ledger + + + +def selected_public(value): + return {key: value[key] for key in ("app_id", "runner_status", "jobs", "connection", "recovery", + "model_request_pending", "active_job_id", "current_frame")} + + +def record(root, name, text_file, action, screenshot=None): + root = protocol.directory(root) + protocol.identity(name) + ready = read(root / "ready.json") + text_path = Path(text_file).resolve() + if root not in text_path.parents: + raise protocol.Refusal("browser evidence must be retained inside its owned trial root") + visible = protocol.read_bytes(text_path, maximum=16000).decode("utf-8") + screenshot_path = Path(screenshot).resolve() if screenshot else None + if screenshot_path and (root not in screenshot_path.parents or not screenshot_path.is_file()): + raise protocol.Refusal("screenshot evidence must be owned by this trial") + step = {"name": name, "time": time.time(), "browser_action": action, "visible_text": visible, + "screenshot": str(screenshot_path) if screenshot_path else None, "offline": False, "cached_public": None} + try: + public = get(ready["browser_origin"], "/api/state") + if public["app_id"] != ready["app_id"] or public["connection"]["body_id"] != ready["body_id"]: + raise protocol.Refusal("browser evidence belongs to another application or body") + client = body_client.read_journal(root / "operator" / "client" / "client.json") + step.update(state=selected_public(public), client_commands=len(client["commands"]), pending_command_ids=client["pending_command_ids"]) + except (OSError, http.client.HTTPException) as exc: + ledger = read(root / "crash-ledger.json") + boundary = ledger["boundaries"][0] + if boundary["role"] != "workbench" or boundary["restored_snapshot"] is not None: + raise protocol.Refusal("offline evidence requires this trial's crashed, unrestored host") from exc + snapshot_path = root / "snapshots" / boundary["offline_snapshot"] + snapshot, before = read(snapshot_path), read(root / "snapshots" / boundary["pre_snapshot"]) + step.update(state=None, offline=True, fetch_error=str(exc), cached_public=selected_public(before["public"]) if before["public"] else None, + pins={key: ready[key] for key in ("app_id", "body_id")}, + snapshot_provenance={"name": boundary["offline_snapshot"], "sha256": protocol.sha(protocol.read_bytes(snapshot_path))}, + client_commands=len(snapshot["client"]["commands"]), pending_command_ids=snapshot["client"]["pending_command_ids"]) + path = root / "ui-ledger.json" + ledger = read(path) if path.exists() else {"steps": []} + if any(item["name"] == name for item in ledger["steps"]): + raise protocol.Refusal("browser evidence step already retained") + ledger["steps"].append(step) + if len(protocol.encoded(ledger)) > protocol.MAX_BYTES: + raise protocol.Refusal("browser evidence aggregate exceeded its 64 KiB bound") + protocol.persist(path, ledger) + return {"name": name, "offline": step["offline"], "client_commands": step["client_commands"]} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + sub = parser.add_subparsers(dest="mode", required=True) + run = sub.add_parser("run") + run.add_argument("--root", type=Path, required=True) + run.add_argument("--source-commit", required=True) + run.add_argument("--case", choices=tuple(CASES), required=True) + run.add_argument("--body-port", type=int, default=0) + run.add_argument("--browser-port", type=int, default=0) + control = sub.add_parser("request") + control.add_argument("--root", type=Path, required=True) + control.add_argument("--action", choices=("crash-role", "start-role", "finish"), required=True) + control.add_argument("--role", choices=("body", "workbench")) + capture = sub.add_parser("record") + capture.add_argument("--root", type=Path, required=True) + capture.add_argument("--name", required=True) + capture.add_argument("--text-file", type=Path, required=True) + capture.add_argument("--action", required=True) + capture.add_argument("--screenshot") + args = parser.parse_args() + if args.mode == "run": + root = protocol.directory(args.root, create=True) + with protocol.locked(root): + value = Trial(root, args.source_commit, args.case, args.body_port, args.browser_port).run() + elif args.mode == "request": + value = request(args.root, args.action, args.role) + else: + value = record(args.root, args.name, args.text_file, args.action, args.screenshot) + print(json.dumps(value), flush=True) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/qualify_workbench_health.py b/tools/physical-workcell/qualify_workbench_health.py new file mode 100644 index 0000000..05b04b2 --- /dev/null +++ b/tools/physical-workcell/qualify_workbench_health.py @@ -0,0 +1,334 @@ +"""Own a frozen browser trial and a bounded relay delaying tagged health replies. + +The relay never drives UI, plans actions, retries requests, or reads native state. +Only auxiliary GET /identity and /state replies receive the diagnostic delay. +""" +import argparse +import contextlib +import http.client +import json +import signal +import socket +import subprocess +import threading +import time +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from urllib.parse import urlsplit + +import body_client +import order_protocol as protocol +import qualify_network_workbench as previous + +BASE, REPO = previous.BASE, previous.REPO +SOURCES = list(dict.fromkeys(previous.SOURCES + [BASE / name for name in ( + "qualify_workbench_health.py", "score_workbench_health.py")])) +DELAY = 1.2 +MAX_REQUESTS = 16384 +MAX_WORKERS = 8 + + +def retain(path, value): + previous.retain(path, value) + + +class HealthRelay: + """Bounded threaded TCP relay with immutable exact-byte request evidence.""" + + def __init__(self, upstream, root, port=0): + body_client.endpoint(upstream) + if type(port) is not int or not 0 <= port <= 65535: + raise protocol.Refusal("relay port must be a bounded integer") + self.upstream, self.root, self.port = upstream, protocol.directory(root, create=True), port + if any(self.root.iterdir()): + raise protocol.Refusal("health relay needs a fresh owned directory") + (self.root / "requests").mkdir(mode=0o700) + self.control_path = self.root.parent / "health-relay-control.json" + self.lock, self.stopping = threading.Lock(), threading.Event() + self.sequence = self.active = self.peak = self.finished = 0 + self.server = self.thread = self.origin = None + + def delay_for(self, tagged, path): + if not tagged: + return 0.0 + enabled, paths = True, ["/identity", "/state"] + if self.control_path.exists(): + control = protocol.decode(protocol.read_bytes(self.control_path)) + protocol.fields(control, ("enabled", "paths"), "owned health relay control") + if (type(control["enabled"]) is not bool or not isinstance(control["paths"], list) or + any(value not in ("/identity", "/state") for value in control["paths"]) or + len(set(control["paths"])) != len(control["paths"])): + raise protocol.Refusal("invalid owned health relay control") + enabled, paths = control["enabled"], control["paths"] + return DELAY if enabled and path in paths else 0.0 + + def start(self): + owner = self + + class Server(ThreadingHTTPServer): + daemon_threads = False + block_on_close = True + + def __init__(self, address, handler): + self.capacity = threading.BoundedSemaphore(MAX_WORKERS) + super().__init__(address, handler) + + def process_request(self, request, client_address): + if not self.capacity.acquire(blocking=False): + self.shutdown_request(request) + return + try: + super().process_request(request, client_address) + except BaseException: + self.capacity.release() + raise + + def process_request_thread(self, request, client_address): + try: + super().process_request_thread(request, client_address) + finally: + self.capacity.release() + + class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def log_message(self, *_): + pass + + def do_GET(self): + self.forward() + + def do_POST(self): + self.forward() + + def forward(self): + started_wall, started_mono = time.time(), time.monotonic() + directory, record = None, None + with owner.lock: + owner.active += 1 + owner.peak = max(owner.peak, owner.active) + try: + self.connection.settimeout(30) + parsed = urlsplit(self.path) + probes = self.headers.get_all("X-Footwork-Health-Probe", []) + tagged = probes == ["1"] + if (self.headers.get_all("Host") != [owner.origin.removeprefix("http://")] or + self.headers.get_all("Origin") or self.headers.get_all("Transfer-Encoding") or + parsed.scheme or parsed.netloc or parsed.fragment or not self.path.startswith("/") or + probes and (not tagged or self.command != "GET" or self.path not in ("/identity", "/state"))): + raise protocol.Refusal("relay requires a closed loopback route and exact auxiliary probe tag") + reads = parsed.path in ("/identity", "/state") or parsed.path.startswith(("/images/", "/commands/")) + if self.command == "GET" and not reads or self.command == "POST" and self.path not in ("/lease", "/commands"): + raise protocol.Refusal("unsupported relay route") + lengths = self.headers.get_all("Content-Length", []) + if len(lengths) > 1 or lengths and (not lengths[0].isascii() or not lengths[0].isdigit() or int(lengths[0]) > protocol.MAX_BYTES): + raise protocol.Refusal("relay payload exceeds its bounded size") + length = int(lengths[0]) if lengths else 0 + if self.command == "GET" and length or self.command == "POST" and (length < 1 or self.headers.get_all("Content-Type") != ["application/json"]): + raise protocol.Refusal("relay requires exact bounded JSON framing") + headers = {key: value for key, value in self.headers.items() if key.lower() not in + {"host", "connection", "content-length", "transfer-encoding"}} + if len(headers) != len([key for key in self.headers if key.lower() not in + {"host", "connection", "content-length", "transfer-encoding"}]): + raise protocol.Refusal("duplicate relay headers are refused") + payload = self.rfile.read(length) + if len(payload) != length: + raise protocol.Refusal("relay payload is truncated") + operation = command_id = None + if self.command == "POST": + request = protocol.decode(payload) + operation = request.get("operation") if self.path == "/commands" else "lease" + command_id = request.get("command_id") if self.path == "/commands" else None + delay = owner.delay_for(tagged, self.path) + with owner.lock: + if owner.sequence >= MAX_REQUESTS: + raise protocol.Refusal("relay request evidence budget exhausted") + owner.sequence += 1 + sequence = owner.sequence + directory = owner.root / "requests" / f"{sequence:06d}" + directory.mkdir(mode=0o700) + record = {"sequence": sequence, "method": self.command, "path": self.path, + "tagged_health": tagged, "operation": operation, "command_id": command_id, + "headers": headers, "payload_sha256": protocol.sha(payload), + "started_wall": started_wall, "started_monotonic": started_mono, + "injected_delay_s": delay, "upstream_sends": 1} + protocol.create_file(directory / "request.bin", payload) + retain(directory / "request.json", record) + upstream_started = time.monotonic() + status, content = body_client.http_call(owner.upstream, self.command, self.path, headers, + payload if self.command == "POST" else None, timeout=30, maximum=body_client.MAX_IMAGE) + upstream_finished = time.monotonic() + protocol.create_file(directory / "response.bin", content) + retain(directory / "upstream.json", {"http_status": status, "response_sha256": protocol.sha(content), + "started_monotonic": upstream_started, "finished_monotonic": upstream_finished, + "wall": time.time(), "upstream_elapsed_s": upstream_finished - upstream_started}) + delay_started = time.monotonic() + interrupted = owner.stopping.wait(delay) if delay else False + delay_finished = time.monotonic() + delivered = False + if not interrupted: + self.send_response(status) + self.send_header("Content-Type", "image/png" if self.path.startswith("/images/") else "application/json") + self.send_header("Content-Length", str(len(content))) + self.send_header("Connection", "close") + self.end_headers() + self.wfile.write(content) + delivered = True + self.close_connection = True + retain(directory / "result.json", {"completed": True, "delivered": delivered, + "delay_interrupted": interrupted, "delay_elapsed_s": delay_finished - delay_started, + "finished_monotonic": time.monotonic(), "finished_wall": time.time(), + "elapsed_s": time.monotonic() - started_mono}) + except (OSError, http.client.HTTPException, protocol.Refusal, ValueError, TypeError, AttributeError) as exc: + if directory is not None and not (directory / "result.json").exists(): + retain(directory / "result.json", {"completed": False, "error": type(exc).__name__ + ": " + str(exc), + "finished_monotonic": time.monotonic(), "finished_wall": time.time()}) + self.close_connection = True + with contextlib.suppress(OSError): + self.connection.shutdown(socket.SHUT_RDWR) + finally: + with owner.lock: + owner.active -= 1 + owner.finished += 1 + + self.server = Server(("127.0.0.1", self.port), Handler) + self.origin = f"http://127.0.0.1:{self.server.server_port}" + self.thread = threading.Thread(target=self.server.serve_forever, kwargs={"poll_interval": .1}, name="health-relay") + self.thread.start() + retain(self.root / "ready.json", {"origin": self.origin, "upstream": self.upstream, + "delay_s": DELAY, "worker_bound": MAX_WORKERS, "source_sha256": protocol.sha(protocol.read_bytes(__file__))}) + return self + + def close(self): + self.stopping.set() + if self.server: + self.server.shutdown() + self.thread.join(3) + self.server.server_close() # Joins all non-daemon bounded request workers. + retain(self.root / "cleanup.json", {"server_thread_exited": not self.thread or not self.thread.is_alive(), + "active_workers": self.active, "peak_workers": self.peak, "completed_workers": self.finished, + "request_count": self.sequence, "worker_bound": MAX_WORKERS}) + + +def freeze(root, commit): + if subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=REPO, text=True).strip() != commit: + raise protocol.Refusal("trial must start at its named source commit") + files = {} + for path in SOURCES: + name = str(path.relative_to(REPO)) + content = protocol.read_bytes(path) + if subprocess.check_output(["git", "show", f"{commit}:{name}"], cwd=REPO) != content: + raise protocol.Refusal("trial source differs from its committed freeze: " + name) + files[name] = protocol.sha(content) + retain(root / "source-freeze.json", {"commit": commit, "files": files}) + + +def run(root, commit, body_port=0, browser_port=0, relay_port=0): + root = protocol.directory(root, create=True) + freeze(root, commit) + stopped = threading.Event() + signal.signal(signal.SIGTERM, lambda *_: stopped.set()) + signal.signal(signal.SIGINT, lambda *_: stopped.set()) + processes, logs = [], [] + ledger = {"source_commit": commit, "planner": "actual_codex_cli_images_over_TCP", "simulation": True, + "processes": [], "ready": False, "health_delay_s": DELAY, "limit_s": 2700} + + def launch(name, arguments): + log = (root / (name + ".log")).open("xb") + logs.append(log) + process = subprocess.Popen([str(BASE / ".venv/bin/python"), *arguments], cwd=REPO, stdout=log, stderr=log) + processes.append(process) + ledger["processes"].append({"role": name, "pid": process.pid}) + return process + + try: + body_port = body_port or previous.available_port() + browser_port = browser_port or previous.available_port() + if body_port == browser_port or relay_port and relay_port in (body_port, browser_port): + raise protocol.Refusal("trial services need distinct ports") + body_origin, browser_origin = f"http://127.0.0.1:{body_port}", f"http://127.0.0.1:{browser_port}" + launch("body", [str(BASE / "body_network.py"), "--root", str(root / "body"), "--port", str(body_port), "--initialize", "--diagnostic"]) + identity = previous.wait_json(body_origin, "/identity", processes) + retain(root / "identity.json", identity) + launch("health-relay", [str(BASE / "qualify_workbench_health.py"), "relay", "--root", str(root / "health-relay"), + "--upstream", body_origin, "--port", str(relay_port)]) + until = time.monotonic() + 20 + while not (root / "health-relay" / "ready.json").exists(): + if any(p.poll() is not None for p in processes) or time.monotonic() >= until: + raise protocol.Refusal("owned health relay failed to start") + stopped.wait(.1) + relay_ready = protocol.decode(protocol.read_bytes(root / "health-relay" / "ready.json")) + initialization = launch("initialize", [str(BASE / "network_bench_operator.py"), "init", "--root", str(root / "operator"), + "--body-origin", relay_ready["origin"]]) + if initialization.wait(timeout=30): + raise protocol.Refusal("network workbench initialization failed; evidence retained") + launch("workbench", [str(BASE / "network_bench_operator.py"), "serve", "--root", str(root / "operator"), + "--port", str(browser_port), "--backend", "codex"]) + running = [p for p, entry in zip(processes, ledger["processes"], strict=True) if entry["role"] != "initialize"] + initial = previous.wait_json(browser_origin, "/api/state", running) + retain(root / "initial-public.json", initial) + ledger.update(body_origin=body_origin, browser_origin=browser_origin, relay_origin=relay_ready["origin"], + ready=True, app_id=initial["app_id"], body_id=identity["body_id"]) + retain(root / "ready.json", ledger) + print(json.dumps(ledger), flush=True) + deadline = time.monotonic() + 2700 + while not stopped.is_set() and not (root / "stop-request.json").exists(): + if any(p.poll() is not None for p in running): + raise protocol.Refusal("owned trial service exited before requested cleanup") + if time.monotonic() >= deadline: + raise protocol.Refusal("health qualification exceeded its 45-minute bound") + stopped.wait(.2) + retain(root / "final-public.json", previous.wait_json(browser_origin, "/api/state", running)) + except BaseException as exc: + ledger["error"] = type(exc).__name__ + ": " + str(exc) + raise + finally: + for process in reversed(processes): + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=45) + except subprocess.TimeoutExpired: + process.kill() + process.wait(timeout=5) + for entry, process in zip(ledger["processes"], processes, strict=True): + entry.update(exit_observed=process.poll() is not None, returncode=process.returncode) + for log in logs: + log.close() + ledger["owned_processes_exited"] = all(p.poll() is not None for p in processes) + retain(root / "process-ledger.json", ledger) + return ledger + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + commands = parser.add_subparsers(dest="command", required=True) + relay = commands.add_parser("relay") + relay.add_argument("--root", type=Path, required=True) + relay.add_argument("--upstream", required=True) + relay.add_argument("--port", type=int, default=0) + trial = commands.add_parser("trial") + trial.add_argument("--root", type=Path, required=True) + trial.add_argument("--source-commit", required=True) + for name in ("body-port", "browser-port", "relay-port"): + trial.add_argument("--" + name, type=int, default=0) + args = parser.parse_args() + if args.command == "relay": + stopped = threading.Event() + signal.signal(signal.SIGTERM, lambda *_: stopped.set()) + signal.signal(signal.SIGINT, lambda *_: stopped.set()) + owned = HealthRelay(args.upstream, args.root, args.port) + try: + owned.start() + print(json.dumps({"origin": owned.origin}), flush=True) + stopped.wait(2700) + finally: + owned.close() + else: + if any(not 0 <= value <= 65535 for value in (args.body_port, args.browser_port, args.relay_port)): + raise protocol.Refusal("trial ports must be in the TCP range") + print(json.dumps(run(args.root, args.source_commit, args.body_port, args.browser_port, args.relay_port)), flush=True) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/qualify_workbench_recovery.py b/tools/physical-workcell/qualify_workbench_recovery.py new file mode 100644 index 0000000..df998ff --- /dev/null +++ b/tools/physical-workcell/qualify_workbench_recovery.py @@ -0,0 +1,510 @@ +"""SIM14 owned event-publication and active-planner SIGKILL browser trials.""" +import argparse +import contextlib +import http.client +import json +import os +import signal +import subprocess +import threading +import time +from pathlib import Path + +import body_client +import order_protocol as protocol +import qualify_workbench_crashes as sim13 +import qualify_workbench_restarts as previous + +BASE, REPO = previous.BASE, previous.REPO +CASES = {"host-publication": "workbench", "host-planning": "workbench"} +SOURCES = list(dict.fromkeys(sim13.SOURCES + [BASE / name for name in ( + "crash_body.py", "qualify_workbench_crashes.py", "score_workbench_crashes.py", + "test_workbench_crash_harness.py", "crash_operator.py", "qualify_workbench_recovery.py", + "score_workbench_recovery.py", "test_workbench_recovery_trials.py", "planner_guard.py", "body_contract.py", "bench_body_contract.py", "persistent_body_client.py")])) +read, retain, get = previous.read, previous.retain, previous.get + + +descendants, indexed_command, HeldReplyRelay = sim13.descendants, sim13.indexed_command, sim13.HeldReplyRelay + + +def freeze(root, commit): + if subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=REPO, text=True).strip() != commit: + raise protocol.Refusal("actual trial requires its committed source head") + files = {} + for path in SOURCES: + name, content = str(path.relative_to(REPO)), protocol.read_bytes(path) + if subprocess.check_output(["git", "show", f"{commit}:{name}"], cwd=REPO) != content: + raise protocol.Refusal("source is not frozen: " + name) + files[name] = protocol.sha(content) + retain(root / "source-freeze.json", {"commit": commit, "files": files}) + + +def expectation(public): + return {"app_id": public["app_id"], "body_id": public["connection"]["body_id"], + "epoch": public["connection"]["epoch"], "runner_status": public["runner_status"], + "active_job_id": public["active_job_id"], "command_id": public["recovery"]["command_id"], + "model_request_pending": public["model_request_pending"]} + + +def validate_control(value): + protocol.fields(value, ("sequence", "action", "role", "expect", "requested_time"), "crash control") + if type(value["sequence"]) is not int or not 1 <= value["sequence"] <= 64: + raise protocol.Refusal("control sequence must be bounded") + if value["action"] not in ("crash-role", "start-role", "finish") or value["role"] not in (None, "body", "workbench"): + raise protocol.Refusal("unsupported owned crash action") + if (value["action"] == "finish") != (value["role"] is None): + raise protocol.Refusal("role must match this control action") + protocol.finite(value["requested_time"]) + if value["action"] == "crash-role": + protocol.fields(value["expect"], ("app_id", "body_id", "epoch", "runner_status", "active_job_id", "command_id", "model_request_pending"), "crash expectation") + if value["expect"]["runner_status"] != "blocked" or value["expect"]["model_request_pending"] is not False: + raise protocol.Refusal("crash requires the exact uncertain idle-model boundary") + for key in ("app_id", "body_id", "active_job_id", "command_id"): + protocol.identity(value["expect"][key]) + protocol.revision(value["expect"]["epoch"]) + elif value["expect"] is not None: + raise protocol.Refusal("only crashes carry an exact fresh expectation") + return value + + +def request(root, action, role=None): + root = protocol.directory(root) + if action not in ("start-role", "finish") or (role == "workbench") != (action == "start-role"): + raise protocol.Refusal("only explicit workbench restart or finish is allowed") + sequence = max((int(p.name.split(".")[0]) for p in (root / "control").glob("*.request.json")), default=0) + 1 + value = validate_control({"sequence": sequence, "action": action, "role": role, "expect": None, "requested_time": time.time()}) + retain(root / "control" / f"{sequence:06d}.request.json", value) + return value + + +def process_inventory(): + rows = subprocess.check_output(["ps", "-axo", "pid=,ppid=,pgid=,uid=,lstart=,comm="], text=True) + result = [] + for line in rows.splitlines(): + parts = line.strip().split(None, 9) + if len(parts) != 10: + raise protocol.Refusal("process inventory has incomplete start identity") + result.append({"pid": int(parts[0]), "parent": int(parts[1]), "group": int(parts[2]), + "uid": int(parts[3]), "started": " ".join(parts[4:9]), "command": parts[9]}) + return result + + +def process_identity(rows, pid): + found = [row for row in rows if row["pid"] == pid] + if len(found) != 1: + raise protocol.Refusal("exact live process identity is missing or duplicated") + return found[0] + + +def same_process(row, old): + # Reparenting or changing group is not proof of process exit. + return all(row.get(key) == old.get(key) for key in ("pid", "uid", "started")) + + +def captured_descendants(rows, host_pid): + children, parents = [], {host_pid} + while True: + new = [row for row in rows if row["parent"] in parents and row["pid"] not in parents] + if not new: + return sorted(children, key=lambda row: row["pid"]) + children.extend(new) + parents.update(row["pid"] for row in new) + + +def merge_captures(earlier, latest): + # Retain every sampled group, including a prior group after exec/setsid. + observed = {tuple(row.get(key) for key in ("pid", "uid", "started", "group")): row + for row in earlier + latest} + return sorted(observed.values(), key=lambda row: (row["pid"], row["group"])) + + +def captured_absence(rows, identities): + groups = sorted({row["group"] for row in identities}) + evidence = absence_evidence(rows, identities, groups[0]) + evidence.update(captured_groups=groups, every_captured_group_absent=not any(row["group"] in groups for row in rows), + captured_group_current_rows=[row for row in rows if row["group"] in groups]) + return evidence + + +def absence_evidence(rows, identities, group): + return {"old_processes_absent": all(not any(same_process(row, old) for row in rows) for old in identities), + "old_group_absent": not any(row["group"] == group for row in rows), + "same_pid_current_rows": [row for row in rows if row["pid"] in {old["pid"] for old in identities}], + "same_group_current_rows": [row for row in rows if row["group"] == group]} + + +def retired_planner(root, boundary, timeout=20): + gate = boundary["gate"] + old = gate["captured_descendants"] + directory = root / "operator/calls" / gate["call_directory"] + until = time.monotonic() + timeout + while time.monotonic() < until: + proof = captured_absence(process_inventory(), old) + if proof["old_processes_absent"] and proof["every_captured_group_absent"]: + break + time.sleep(.05) + else: + raise protocol.Refusal("prior guardian/planner identity or group remained live after restart") + host = read(root / "operator/operator.json") + if host["planner_owner"] == gate["planner"]["owner"]: + raise protocol.Refusal("restarted planner incarnation did not change") + proof.update(time=time.time(), captured_descendants=old, old_identities=gate["process_identities"], old_owner=gate["planner"]["owner"], + new_owner=host["planner_owner"], old_call_files={path.name: protocol.sha(protocol.read_bytes(path, maximum=16 * 1024 * 1024)) + for path in sorted(directory.iterdir()) if path.is_file() and path.name != "planner.lock"}) + retain(root / "planner-retirement.json", proof) + return proof + + +def validate_planning_gate(root, gate): + directory = protocol.directory(root / "operator/calls" / gate["call_directory"]) + intent, live = read(directory / "planner-intent.json"), read(directory / "planner-process.json") + if live != gate["planner"] or intent["owner"] != live["owner"] or intent["host_pid"] != gate["pid"] or live["host_pid"] != gate["pid"]: + raise protocol.Refusal("planning gate differs from exact persistent ownership") + if live["process_exit_observed"] or live["group_closed"] or live["planner_pid"] != live["planner_pgid"]: + raise protocol.Refusal("planning gate has no live independent planner") + for pid in (live["guardian_pid"], live["planner_pid"]): + os.kill(pid, 0) + if os.getpgid(live["planner_pid"]) != live["planner_pgid"]: + raise protocol.Refusal("live planner group differs from recorded ownership") + if (directory / "planner-closure.json").exists(): + raise protocol.Refusal("planner was already closed before crash") + return live + + +class Trial(previous.Trial): + def __init__(self, root, commit, case, body_port=0, browser_port=0): + if case not in CASES: + raise protocol.Refusal("unsupported hard crash case") + super().__init__(root, commit, body_port, browser_port) + self.case, self.relay = case, None + self.boundary = None + self.ledger["case"] = case + self.snapshot_sequence = 0 + + def launch(self, role, initialize=False): + first = role == "workbench" and not any(r["role"] == role for r in self.ledger["processes"]) + if not first: + process = super().launch(role, initialize) + else: + log = (self.root / "workbench-01.log").open("xb") + process = subprocess.Popen([str(BASE / ".venv/bin/python"), str(BASE / "crash_operator.py"), + "--root", str(self.root / "operator"), "--port", str(self.browser_port), "--gate", + "publication" if self.case == "host-publication" else "planning"], cwd=REPO, + stdout=log, stderr=log, start_new_session=True) + entry = {"role": role, "boot": 1, "pid": process.pid, "root": str(self.root / "operator"), + "port": self.browser_port, "initialized": False, "log": "workbench-01.log"} + self.processes.append((process, entry)) + self.logs.append(log) + self.ledger["processes"].append(entry) + self.active[role] = process + entry = next(r for p, r in self.processes if p is process) + entry["group"] = os.getpgid(process.pid) + if entry["group"] != process.pid: + raise protocol.Refusal("owned actor did not create its own process group") + return process + + def wait_public(self, origin, path): + deadline = time.monotonic() + 30 + while time.monotonic() < deadline: + try: + public = get(origin, path) + if origin == self.body_origin: + pinned = read(self.root / "body" / "identity.json") + epoch = read(self.root / "body" / "service.json")["epoch"] + expected = {**pinned, "epoch": epoch} + else: + expected = read(self.root / "operator" / "identity.json") + if origin == self.body_origin and public != expected: + raise protocol.Refusal("endpoint identity does not match this owned body root and boot") + if origin == self.browser_origin and public["app_id"] != expected["app_id"]: + raise protocol.Refusal("endpoint application does not match this owned workbench root") + if any(process is not None and process.poll() is not None for process in self.active.values()): + raise protocol.Refusal("owned role exited while endpoint identity was being checked") + return public + except (OSError, http.client.HTTPException): + if any(process is not None and process.poll() is not None for process in self.active.values()): + raise protocol.Refusal("owned role exited during readiness") + time.sleep(.05) + raise protocol.Refusal("owned endpoint readiness exceeded thirty seconds") + + def snapshot(self, label, token=None, body_reads=True): + self.snapshot_sequence += 1 + value = {"label": label, "time": time.time(), "public": None, "body_identity": None, "body_state": None, + "processes": {role: p.pid if p is not None and p.poll() is None else None for role, p in self.active.items()}} + for key, origin, path in (("public", self.browser_origin, "/api/state"), + ("body_identity", self.body_origin, "/identity"), ("body_state", self.body_origin, "/state")): + if key != "public" and not body_reads: + continue + if key == "public" and (self.active.get("workbench") is None or + self.boundary is None and (self.root / "operator/barrier.json").exists()): + continue + try: + value[key] = get(origin, path, self.identity if key == "body_state" else None) + except (OSError, ValueError, http.client.HTTPException) as exc: + value[key + "_error"] = str(exc) + host = read(self.root / "operator" / "operator.json") + client = body_client.read_journal(self.root / "operator" / "client" / "client.json") + value["host"] = {key: host[key] for key in ("app_id", "body_identity", "stage", "frame", "runner_status", "sequence", "usage", "planner_owner")} + commands = {} + for command_id, entry in client["commands"].items(): + content = protocol.read_bytes(self.root / "operator" / "client" / "requests" / (command_id + ".json")) + if protocol.sha(content) != entry["request_sha256"]: + raise protocol.Refusal("retained client request changed") + commands[command_id] = {"request_sha256": entry["request_sha256"], "send_count": entry["send_count"], + "state": entry["state"], "operation": protocol.decode(content)["operation"]} + value["client"] = {"commands": commands, "pending_command_ids": client["pending_command_ids"], + "lease": client["lease"], "identity": client["identity"]} + value["posts"] = [] + for path in sorted((self.root / "operator" / "link" / "events").glob("*.json")): + event = read(path) + if event["kind"] == "received" and event.get("method") == "POST" and event.get("command_id"): + value["posts"].append({key: event[key] for key in ("sequence", "command_id", "payload_sha256")}) + value["body_command"] = indexed_command(self.root, token) if token else None + name = f"{self.snapshot_sequence:06d}-{label}.json" + retain(self.root / "snapshots" / name, value) + return name, value + + def crash_gate(self, gate): + process = self.active["workbench"] + if self.boundary or process.poll() is not None or gate["pid"] != process.pid or os.getpgid(process.pid) != process.pid: + raise protocol.Refusal("gate does not match this exact live owned host") + token = gate.get("command_id") + if self.case == "host-publication": + receiver = indexed_command(self.root, token) + if receiver["intent"]["request_sha256"] != gate["request_sha256"] or not receiver["terminal"] or receiver["terminal"]["result"]["outcome"] != "executed": + raise protocol.Refusal("publication gate lacks exact indexed executed dispatch") + else: + validate_planning_gate(self.root, gate) + rows = process_inventory() + gate["process_identities"] = {"host": process_identity(rows, process.pid), + "guardian": process_identity(rows, gate["planner"]["guardian_pid"]), + "planner": process_identity(rows, gate["planner"]["planner_pid"])} + gate["captured_descendants"] = captured_descendants(rows, process.pid) + if (gate["process_identities"]["guardian"]["parent"] != process.pid or + gate["process_identities"]["planner"]["parent"] != gate["planner"]["guardian_pid"]): + raise protocol.Refusal("planner process ancestry differs from recorded ownership") + before_name, _ = self.snapshot("before", token) + children = descendants(process.pid) + entry = next(r for p, r in self.processes if p is process) + if self.case == "host-planning": + validate_planning_gate(self.root, gate) + rows = process_inventory() + if any(process_identity(rows, old["pid"]) != old for old in gate["process_identities"].values()): + raise protocol.Refusal("exact process start identity changed immediately before crash") + latest = captured_descendants(rows, process.pid) + gate["captured_descendants"] = merge_captures(gate["captured_descendants"], latest) + children = gate["captured_descendants"] + os.kill(process.pid, signal.SIGKILL) # ONLY host; guardian must reap its independent child. + process.wait(timeout=5) + if process.returncode != -9: + raise protocol.Refusal("owned host was not reaped with SIGKILL") + entry.update(exit_observed=True, returncode=-9, stopped_time=time.time(), signal="SIGKILL", + signal_target="host_pid_only", children_before=children) + self.active["workbench"] = None + offline_name, _ = self.snapshot("offline", token) + self.boundary = {"case": self.case, "role": "workbench", "command_id": token, + "request_sha256": gate.get("request_sha256"), "gate": gate, "pre_snapshot": before_name, + "offline_snapshot": offline_name, "restored_snapshot": None, "process": dict(entry), + "old_epoch": self.identity["epoch"], "new_epoch": None} + self.stops["workbench"] = True + protocol.persist(self.root / "crash-ledger.json", {"case": self.case, "boundaries": [self.boundary]}) + + def control(self, command): + validate_control(command) + action, role = command["action"], command["role"] + if action == "crash-role": + raise protocol.Refusal("SIM14 crashes only through its exact automatic diagnostic gate") + elif action == "start-role": + if not self.boundary or role != self.boundary["role"] or role not in self.stops or self.active.get(role) is not None: + raise protocol.Refusal("restart requires this case's exact reaped role") + self.launch(role) + self.wait_public(self.body_origin if role == "body" else self.browser_origin, "/identity" if role == "body" else "/api/state") + body_current = self.wait_public(self.body_origin, "/identity") + if any(body_current[key] != self.identity[key] for key in body_client.IDENTITY_KEYS - {"epoch"}): + raise protocol.Refusal("restarted endpoint belongs to another pinned body") + until = time.monotonic() + 15 + while time.monotonic() < until: + public = get(self.browser_origin, "/api/state") + if (public["runner_status"] != "running" and public["recovery"]["command_id"] == self.boundary["command_id"] and + not public["model_request_pending"] and public["connection"]["epoch"] == body_current["epoch"] and + public["connection"]["body_id"] == self.identity["body_id"] and public["connection"]["adapter_id"] == self.identity["adapter_id"]): + break + time.sleep(.05) + else: + raise protocol.Refusal("restart silently lost uncertainty or resumed planning") + if self.case == "host-planning": + retired_planner(self.root, self.boundary) + name, snapshot = self.snapshot("restored", self.boundary["command_id"]) + self.boundary.update(restored_snapshot=name, new_epoch=snapshot["body_identity"]["epoch"]) + self.stops.pop(role) + protocol.persist(self.root / "crash-ledger.json", {"case": self.case, "boundaries": [self.boundary]}) + else: + if not self.boundary or self.boundary["restored_snapshot"] is None: + raise protocol.Refusal("finish requires the recorded crash and explicit restart") + name, _ = self.snapshot("finished", self.boundary["command_id"]) + self.boundary["finished_snapshot"] = name + protocol.persist(self.root / "crash-ledger.json", {"case": self.case, "boundaries": [self.boundary]}) + retain(self.root / "control" / f"{command['sequence']:06d}.result.json", {"request": command, "passed": True, "time": time.time()}) + return action == "finish" + + def run(self): + freeze(self.root, self.commit) + for name in ("control", "snapshots"): + (self.root / name).mkdir(mode=0o700) + self.body_port = self.body_port or previous.previous.available_port() + self.browser_port = self.browser_port or previous.previous.available_port() + if self.body_port == self.browser_port or any(type(port) is not int or not 1 <= port <= 65535 for port in (self.body_port, self.browser_port)): + raise protocol.Refusal("body and browser ports must be distinct bounded owned endpoints") + self.body_origin, self.browser_origin = f"http://127.0.0.1:{self.body_port}", f"http://127.0.0.1:{self.browser_port}" + stopped = threading.Event() + signal.signal(signal.SIGTERM, lambda *_: stopped.set()) + signal.signal(signal.SIGINT, lambda *_: stopped.set()) + try: + self.launch("body", initialize=True) + self.identity = self.wait_public(self.body_origin, "/identity") + retain(self.root / "identity.json", self.identity) + upstream = self.body_origin + self.relay = HeldReplyRelay(self.body_origin, self.root / "relay", hold_first_dispatch=False).start() + upstream = self.relay.origin + log = (self.root / "initialization.log").open("xb") + self.logs.append(log) + init = subprocess.Popen([str(BASE / ".venv/bin/python"), str(BASE / "network_bench_operator.py"), "init", + "--root", str(self.root / "operator"), "--body-origin", upstream], cwd=REPO, stdout=log, stderr=log, start_new_session=True) + record = {"role": "initialization", "pid": init.pid, "group": init.pid} + self.processes.append((init, record)) + self.ledger["processes"].append(record) + if init.wait(timeout=30): + raise protocol.Refusal("owned workbench initialization failed") + self.launch("workbench") + public = self.wait_public(self.browser_origin, "/api/state") + retain(self.root / "initial-public.json", public) + self.ledger.update(ready=True, body_origin=self.body_origin, browser_origin=self.browser_origin, + app_id=public["app_id"], body_id=self.identity["body_id"], case=self.case) + retain(self.root / "ready.json", self.ledger) + print(json.dumps(self.ledger), flush=True) + deadline, handled, done = time.monotonic() + 1200, set(), False + while not stopped.is_set() and not done: + if time.monotonic() >= deadline: + raise protocol.Refusal("hard crash case exceeded its twenty-minute bound") + if any(process is not None and process.poll() is not None for process in self.active.values()): + raise protocol.Refusal("owned role exited without its exact crash control") + if not self.boundary and (self.root / "operator/barrier.json").exists(): + self.crash_gate(read(self.root / "operator/barrier.json")) + for path in sorted((self.root / "control").glob("*.request.json")): + if path.name in handled: + continue + command = read(path) + if path.name != f"{command['sequence']:06d}.request.json": + raise protocol.Refusal("control filename and sequence disagree") + handled.add(path.name) + done = self.control(command) + if done: + break + stopped.wait(.05) + retain(self.root / "final-public.json", get(self.browser_origin, "/api/state")) + except BaseException as exc: + self.ledger["error"] = type(exc).__name__ + ": " + str(exc) + raise + finally: + for role in ("workbench",): + if self.active.get(role) is not None and self.active[role].poll() is None: + with contextlib.suppress(OSError, protocol.Refusal, subprocess.SubprocessError): + self.stop(role) + if self.relay: + self.relay.close() + if self.active.get("body") is not None and self.active["body"].poll() is None: + with contextlib.suppress(OSError, protocol.Refusal, subprocess.SubprocessError): + self.stop("body") + for process, record in self.processes: + if process.poll() is None: + os.killpg(process.pid, signal.SIGKILL) + process.wait(timeout=5) + self.ledger["cleanup_error"] = "a role required forced cleanup after its ordinary stop" + record.update(exit_observed=process.poll() is not None, returncode=process.returncode) + for log in self.logs: + log.close() + self.ledger["owned_processes_exited"] = all(process.poll() is not None for process, _ in self.processes) + retain(self.root / "process-ledger.json", self.ledger) + return self.ledger + + + +def selected_public(value): + return {key: value[key] for key in ("app_id", "runner_status", "jobs", "connection", "recovery", + "model_request_pending", "active_job_id", "current_frame")} + + +def record(root, name, text_file, action, screenshot=None): + root = protocol.directory(root) + protocol.identity(name) + ready = read(root / "ready.json") + text_path = Path(text_file).resolve() + if root not in text_path.parents: + raise protocol.Refusal("browser evidence must be retained inside its owned trial root") + visible = protocol.read_bytes(text_path, maximum=16000).decode("utf-8") + screenshot_path = Path(screenshot).resolve() if screenshot else None + if screenshot_path and (root not in screenshot_path.parents or not screenshot_path.is_file()): + raise protocol.Refusal("screenshot evidence must be owned by this trial") + step = {"name": name, "time": time.time(), "browser_action": action, "visible_text": visible, + "screenshot": str(screenshot_path) if screenshot_path else None, "offline": False, "cached_public": None} + try: + public = get(ready["browser_origin"], "/api/state") + if public["app_id"] != ready["app_id"] or public["connection"]["body_id"] != ready["body_id"]: + raise protocol.Refusal("browser evidence belongs to another application or body") + client = body_client.read_journal(root / "operator" / "client" / "client.json") + step.update(state=selected_public(public), client_commands=len(client["commands"]), pending_command_ids=client["pending_command_ids"]) + except (OSError, http.client.HTTPException) as exc: + ledger = read(root / "crash-ledger.json") + boundary = ledger["boundaries"][0] + if boundary["role"] != "workbench" or boundary["restored_snapshot"] is not None: + raise protocol.Refusal("offline evidence requires this trial's crashed, unrestored host") from exc + snapshot_path = root / "snapshots" / boundary["offline_snapshot"] + snapshot, before = read(snapshot_path), read(root / "snapshots" / boundary["pre_snapshot"]) + step.update(state=None, offline=True, fetch_error=str(exc), cached_public=selected_public(before["public"]) if before["public"] else None, + pins={key: ready[key] for key in ("app_id", "body_id")}, + snapshot_provenance={"name": boundary["offline_snapshot"], "sha256": protocol.sha(protocol.read_bytes(snapshot_path))}, + client_commands=len(snapshot["client"]["commands"]), pending_command_ids=snapshot["client"]["pending_command_ids"]) + path = root / "ui-ledger.json" + ledger = read(path) if path.exists() else {"steps": []} + if any(item["name"] == name for item in ledger["steps"]): + raise protocol.Refusal("browser evidence step already retained") + ledger["steps"].append(step) + if len(protocol.encoded(ledger)) > protocol.MAX_BYTES: + raise protocol.Refusal("browser evidence aggregate exceeded its 64 KiB bound") + protocol.persist(path, ledger) + return {"name": name, "offline": step["offline"], "client_commands": step["client_commands"]} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + sub = parser.add_subparsers(dest="mode", required=True) + run = sub.add_parser("run") + run.add_argument("--root", type=Path, required=True) + run.add_argument("--source-commit", required=True) + run.add_argument("--case", choices=tuple(CASES), required=True) + run.add_argument("--body-port", type=int, default=0) + run.add_argument("--browser-port", type=int, default=0) + control = sub.add_parser("request") + control.add_argument("--root", type=Path, required=True) + control.add_argument("--action", choices=("crash-role", "start-role", "finish"), required=True) + control.add_argument("--role", choices=("body", "workbench")) + capture = sub.add_parser("record") + capture.add_argument("--root", type=Path, required=True) + capture.add_argument("--name", required=True) + capture.add_argument("--text-file", type=Path, required=True) + capture.add_argument("--action", required=True) + capture.add_argument("--screenshot") + args = parser.parse_args() + if args.mode == "run": + root = protocol.directory(args.root, create=True) + with protocol.locked(root): + value = Trial(root, args.source_commit, args.case, args.body_port, args.browser_port).run() + elif args.mode == "request": + value = request(args.root, args.action, args.role) + else: + value = record(args.root, args.name, args.text_file, args.action, args.screenshot) + print(json.dumps(value), flush=True) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/qualify_workbench_restarts.py b/tools/physical-workcell/qualify_workbench_restarts.py new file mode 100644 index 0000000..2e708d8 --- /dev/null +++ b/tools/physical-workcell/qualify_workbench_restarts.py @@ -0,0 +1,370 @@ +"""Frozen owned-process restart trial; all product actions come from the browser. + +run --root PATH --source-commit SHA +request --root PATH --action stop-role --role workbench --predicate planning --wait-s 40 +request --root PATH --action start-role --role workbench +request --root PATH --action finish +Numbered control requests capture fresh expectations; mismatches never signal a PID. +""" +import argparse +import json +import signal +import subprocess +import threading +import time +from pathlib import Path + +import body_client +import order_protocol as protocol +import qualify_network_workbench as previous + +BASE, REPO = previous.BASE, previous.REPO +SOURCES = list(dict.fromkeys(previous.SOURCES + [BASE / "qualify_workbench_restarts.py", BASE / "score_workbench_restarts.py"])) + + +def retain(path, value): + protocol.create_file(path, protocol.encoded(value)) + + +def read(path): + return body_client.read_journal(path) + + +def get(origin, path, pinned=None): + status, content = body_client.http_call(origin, "GET", path, body_client.pinned_headers(pinned) if pinned else {}, timeout=1) + if status != 200: + raise protocol.Refusal("public read refused") + return protocol.decode(content) + + +def expected(public): + return {"app_id": public["app_id"], "body_id": public["connection"]["body_id"], + "runner_status": public["runner_status"], "model_request_pending": public["model_request_pending"], + "active_job_id": public["active_job_id"], "usage_calls": public["usage"]["calls"], + "recovery_command_id": public["recovery"]["command_id"]} + + +def matches(public, predicate): + if predicate == "planning": + return public["model_request_pending"] is True and public["runner_status"] == "running" + if predicate == "uncertain": + return bool(public["recovery"]["command_id"] and public["runner_status"] == "blocked") + return predicate == "none" + + +def validate_control(command): + protocol.fields(command, ("sequence", "action", "role", "predicate", "expect", "requested_time"), "restart control") + if type(command["sequence"]) is not int or not 1 <= command["sequence"] <= 4096: + raise protocol.Refusal("control sequence must be a bounded positive integer") + action, role, predicate = command["action"], command["role"], command["predicate"] + if (not isinstance(action, str) or action not in {"stop-role", "start-role", "finish"} or + role is not None and (not isinstance(role, str) or role not in {"body", "workbench"}) or + not isinstance(predicate, str) or predicate not in {"planning", "uncertain", "none"} or + action != "finish" and role is None or action == "finish" and role is not None or + action == "stop-role" and (predicate == "none" or command["expect"] is None) or + action != "stop-role" and predicate != "none"): + raise protocol.Refusal("control action, role or predicate is invalid") + protocol.finite(command["requested_time"]) + if command["expect"] is not None: + protocol.fields(command["expect"], ("app_id", "body_id", "runner_status", "model_request_pending", + "active_job_id", "usage_calls", "recovery_command_id"), "public expectation") + for key in ("app_id", "body_id"): + protocol.identity(command["expect"][key]) + for key in ("active_job_id", "recovery_command_id"): + if command["expect"][key] is not None: + protocol.identity(command["expect"][key]) + if (type(command["expect"]["model_request_pending"]) is not bool or + type(command["expect"]["usage_calls"]) is not int or not 0 <= command["expect"]["usage_calls"] <= 96 or + not isinstance(command["expect"]["runner_status"], str)): + raise protocol.Refusal("public expectation has invalid types") + return command + + +def request(root, action, role=None, predicate="none", wait_s=0): + root = protocol.directory(root) + ready = read(root / "ready.json") + if action not in {"stop-role", "start-role", "finish"} or role not in {None, "body", "workbench"}: + raise protocol.Refusal("unsupported process control") + if action != "finish" and role is None or action == "stop-role" and predicate not in {"planning", "uncertain"}: + raise protocol.Refusal("stop requires an exact planning or uncertain predicate") + wait_s = protocol.finite(wait_s) + if not 0 <= wait_s <= 40: + raise protocol.Refusal("predicate wait exceeds forty seconds") + deadline = time.monotonic() + wait_s + while True: + public = None if action == "start-role" and role == "workbench" else get(ready["browser_origin"], "/api/state") + if public is None or matches(public, predicate): + break + if time.monotonic() >= deadline: + raise protocol.Refusal("public state does not match the requested interruption predicate") + time.sleep(.05) + paths = list((root / "control").glob("*.request.json")) + sequence = max((int(path.name.split(".")[0]) for path in paths), default=0) + 1 + value = {"sequence": sequence, "action": action, "role": role, "predicate": predicate, + "expect": expected(public) if public else None, "requested_time": time.time()} + validate_control(value) + retain(root / "control" / f"{sequence:06d}.request.json", value) + return value + + +def freeze(root, commit): + if subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=REPO, text=True).strip() != commit: + raise protocol.Refusal("trial requires its committed source head") + files = {} + for path in SOURCES: + name = str(path.relative_to(REPO)) + content = protocol.read_bytes(path) + if subprocess.check_output(["git", "show", f"{commit}:{name}"], cwd=REPO) != content: + raise protocol.Refusal("source is not frozen: " + name) + files[name] = protocol.sha(content) + retain(root / "source-freeze.json", {"commit": commit, "files": files}) + + +class Trial: + def __init__(self, root, commit, body_port=0, browser_port=0): + self.root, self.commit = protocol.directory(root, create=True), commit + self.body_port, self.browser_port = body_port, browser_port + self.action_performed = False + self.processes, self.logs, self.active, self.stops = [], [], {}, {} + self.ledger = {"source_commit": commit, "planner": "actual_codex_cli_images_over_TCP", "simulation": True, + "processes": [], "ready": False} + self.restart_ledger = {"boundaries": []} + + def launch(self, role, initialize=False): + boot = sum(record["role"] == role for record in self.ledger["processes"]) + 1 + if role == "body": + args = [str(BASE / "body_network.py"), "--root", str(self.root / "body"), "--port", str(self.body_port)] + if initialize: + args += ["--initialize", "--diagnostic"] + else: + args = [str(BASE / "network_bench_operator.py"), "serve", "--root", str(self.root / "operator"), + "--port", str(self.browser_port), "--backend", "codex"] + log_name = f"{role}-{boot:02d}.log" + log = (self.root / log_name).open("xb") + log_path = self.root / log_name + log_path.chmod(0o600) + process = subprocess.Popen([str(BASE / ".venv/bin/python"), *args], cwd=REPO, stdout=log, stderr=log, + start_new_session=True) + record = {"role": role, "boot": boot, "pid": process.pid, "root": str(self.root / ("body" if role == "body" else "operator")), + "port": self.body_port if role == "body" else self.browser_port, "initialized": initialize, "log": log_name} + self.processes.append((process, record)) + self.logs.append(log) + self.ledger["processes"].append(record) + self.active[role] = process + return process + + def stop(self, role): + process = self.active.get(role) + if process is None or process.poll() is not None: + raise protocol.Refusal("requested role has no live owned process") + record = next((record for proc, record in self.processes if proc is process), None) + if record is None or record["role"] != role or record["pid"] != process.pid: + raise protocol.Refusal("process handle is not registered to this trial role") + process.terminate() + try: + process.wait(timeout=40) + except subprocess.TimeoutExpired as exc: + process.kill() + process.wait(timeout=5) + self.ledger["error"] = "owned graceful stop exceeded forty seconds; forced cleanup is not qualification" + raise protocol.Refusal(self.ledger["error"]) from exc + self.active[role] = None + record.update(exit_observed=True, returncode=process.returncode, stopped_time=time.time()) + return dict(record) + + def wait_public(self, origin, path): + deadline = time.monotonic() + 30 + while time.monotonic() < deadline: + try: + return get(origin, path) + except (OSError, ValueError): + if any(p is not None and p.poll() is not None for p in self.active.values()): + raise protocol.Refusal("owned role exited during readiness") + time.sleep(.05) + raise protocol.Refusal("owned endpoint readiness timed out") + + def snapshot(self, sequence, label): + value = {"time": time.time(), "public": None, "body_identity": None, "body_state": None, + "processes": {role: p.pid if p is not None and p.poll() is None else None for role, p in self.active.items()}} + for key, origin, path, pinned in (("public", self.browser_origin, "/api/state", None), + ("body_identity", self.body_origin, "/identity", None), + ("body_state", self.body_origin, "/state", self.identity)): + try: + value[key] = get(origin, path, pinned) + except (OSError, ValueError) as exc: + value[key + "_error"] = str(exc) + host = read(self.root / "operator" / "operator.json") + client = read(self.root / "operator" / "client" / "client.json") + commands = {} + for token, index in client["commands"].items(): + content = protocol.read_bytes(self.root / "operator" / "client" / "requests" / (token + ".json")) + if protocol.sha(content) != index["request_sha256"]: + raise protocol.Refusal("client immutable request changed during snapshot") + commands[token] = {"request_sha256": index["request_sha256"], "send_count": index["send_count"], + "state": index["state"], "operation": protocol.decode(content)["operation"]} + value["client"] = {"commands": commands, "pending_command_ids": client["pending_command_ids"], + "lease": client["lease"], "identity": client["identity"]} + value["host"] = {key: host[key] for key in ("app_id", "body_identity", "stage", "frame", "runner_status", "sequence", "usage")} + value["posts"] = [] + for path in sorted((self.root / "operator" / "link" / "events").glob("*.json")): + event = read(path) + if event["kind"] == "received" and event.get("method") == "POST" and event.get("command_id"): + value["posts"].append({key: event[key] for key in ("sequence", "command_id", "payload_sha256")}) + name = f"{sequence:06d}-{label}.json" + retain(self.root / "snapshots" / name, value) + return name, value + + def control(self, command): + validate_control(command) + sequence, action, role = command["sequence"], command["action"], command["role"] + result = {"request": command, "passed": False} + before_name, before = self.snapshot(sequence, "before") + result["before"] = before_name + if command["expect"] is not None and (before["public"] is None or expected(before["public"]) != command["expect"]): + raise protocol.Refusal("public state changed since this exact interruption was requested") + if action == "stop-role": + if role not in {"body", "workbench"} or not matches(before["public"], command["predicate"]): + raise protocol.Refusal("interruption predicate no longer matches") + fresh = get(self.browser_origin, "/api/state") + if expected(fresh) != command["expect"] or not matches(fresh, command["predicate"]): + raise protocol.Refusal("predicate changed immediately before owned process signal") + self.action_performed = True + result["process"] = self.stop(role) + self.stops[role] = {"sequence": sequence, "predicate": command["predicate"]} + elif action == "start-role": + if role not in self.stops or self.active.get(role) is not None: + raise protocol.Refusal("restart requires this trial's stopped role") + self.action_performed = True + process = self.launch(role) + ready = self.wait_public(self.body_origin if role == "body" else self.browser_origin, + "/identity" if role == "body" else "/api/state") + result.update(stopped_sequence=self.stops[role]["sequence"], predicate=self.stops[role]["predicate"], pid=process.pid) + # Give real health/epoch handling a bounded opportunity; never mutate host state. + deadline = time.monotonic() + 10 + while time.monotonic() < deadline: + public = get(self.browser_origin, "/api/state") + epoch_seen = role != "body" or public["connection"]["epoch"] == ready["epoch"] + if public["runner_status"] != "running" and not public["model_request_pending"] and epoch_seen: + break + time.sleep(.05) + if public["runner_status"] == "running" or public["model_request_pending"] or not epoch_seen: + raise protocol.Refusal("restarted body or workbench did not pause its old planning") + self.stops.pop(role) + elif action != "finish": + raise protocol.Refusal("unsupported restart action") + after_name, after = self.snapshot(sequence, "after") + result.update(after=after_name, passed=True, action_performed=self.action_performed) + self.restart_ledger["boundaries"].append(result) + protocol.persist(self.root / "restart-ledger.json", self.restart_ledger) + retain(self.root / "control" / f"{sequence:06d}.result.json", result) + return action == "finish", after + + def run(self): + freeze(self.root, self.commit) + for name in ("control", "snapshots"): + (self.root / name).mkdir(mode=0o700) + self.body_port = self.body_port or previous.available_port() + self.browser_port = self.browser_port or previous.available_port() + if self.body_port == self.browser_port or any(type(port) is not int or not 1 <= port <= 65535 for port in (self.body_port, self.browser_port)): + raise protocol.Refusal("body and browser need distinct bounded owned ports") + self.body_origin, self.browser_origin = f"http://127.0.0.1:{self.body_port}", f"http://127.0.0.1:{self.browser_port}" + stopped = threading.Event() + signal.signal(signal.SIGTERM, lambda *_: stopped.set()) + signal.signal(signal.SIGINT, lambda *_: stopped.set()) + try: + self.launch("body", initialize=True) + self.identity = self.wait_public(self.body_origin, "/identity") + retain(self.root / "identity.json", self.identity) + initialization = subprocess.Popen([str(BASE / ".venv/bin/python"), str(BASE / "network_bench_operator.py"), "init", + "--root", str(self.root / "operator"), "--body-origin", self.body_origin], cwd=REPO, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + record = {"role": "initialization", "pid": initialization.pid} + self.processes.append((initialization, record)) + self.ledger["processes"].append(record) + try: + stdout, stderr = initialization.communicate(timeout=30) + except subprocess.TimeoutExpired: + initialization.kill() + stdout, stderr = initialization.communicate(timeout=5) + self.ledger["error"] = "initialization exceeded its bounded deadline" + protocol.create_file(self.root / "initialization.stdout", stdout) + protocol.create_file(self.root / "initialization.stderr", stderr) + if initialization.returncode: + raise protocol.Refusal("workbench initialization failed") + self.launch("workbench") + initial = self.wait_public(self.browser_origin, "/api/state") + retain(self.root / "initial-public.json", initial) + self.ledger.update(ready=True, body_origin=self.body_origin, browser_origin=self.browser_origin, + app_id=initial["app_id"], body_id=self.identity["body_id"]) + retain(self.root / "ready.json", self.ledger) + print(json.dumps(self.ledger), flush=True) + deadline, handled, finished = time.monotonic() + 3600, set(), False + while not stopped.is_set() and not finished: + if time.monotonic() >= deadline: + raise protocol.Refusal("restart qualification exceeded sixty minutes") + if any(p is not None and p.poll() is not None for p in self.active.values()): + raise protocol.Refusal("owned process exited without a matching control request") + for path in sorted((self.root / "control").glob("*.request.json")): + if path.name in handled: + continue + handled.add(path.name) + command = read(path) + if path.name != f"{command['sequence']:06d}.request.json": + raise protocol.Refusal("request file sequence disagrees with its exact payload") + self.action_performed = False + try: + finished, _ = self.control(command) + except protocol.Refusal as exc: + retain(self.root / "control" / path.name.replace("request", "result"), + {"request": command, "passed": False, "reason": str(exc), "action_performed": self.action_performed}) + if self.action_performed or self.ledger.get("error"): + raise + if finished: + break + stopped.wait(.05) + retain(self.root / "final-public.json", get(self.browser_origin, "/api/state")) + except Exception as exc: + self.ledger["error"] = type(exc).__name__ + ": " + str(exc) + raise + finally: + for role in ("workbench", "body"): + if self.active.get(role) is not None and self.active[role].poll() is None: + try: + self.stop(role) + except (OSError, protocol.Refusal, subprocess.SubprocessError) as exc: + self.ledger["cleanup_error"] = str(exc) + for process, record in self.processes: + record.update(exit_observed=process.poll() is not None, returncode=process.poll()) + for log in self.logs: + log.close() + self.ledger["owned_processes_exited"] = all(process.poll() is not None for process, _ in self.processes) + retain(self.root / "process-ledger.json", self.ledger) + return self.ledger + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + sub = parser.add_subparsers(dest="mode", required=True) + run_parser = sub.add_parser("run") + run_parser.add_argument("--root", required=True, type=Path) + run_parser.add_argument("--source-commit", required=True) + run_parser.add_argument("--browser-port", type=int, default=0) + run_parser.add_argument("--body-port", type=int, default=0) + control = sub.add_parser("request") + control.add_argument("--root", required=True, type=Path) + control.add_argument("--action", required=True, choices=("stop-role", "start-role", "finish")) + control.add_argument("--role", choices=("body", "workbench")) + control.add_argument("--predicate", default="none", choices=("none", "planning", "uncertain")) + control.add_argument("--wait-s", type=float, default=0) + args = parser.parse_args() + if args.mode == "run": + root = protocol.directory(args.root, create=True) + with protocol.locked(root): + result = Trial(root, args.source_commit, args.body_port, args.browser_port).run() + else: + result = request(args.root, args.action, args.role, args.predicate, args.wait_s) + print(json.dumps(result, sort_keys=True), flush=True) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/record_network_workbench.py b/tools/physical-workcell/record_network_workbench.py new file mode 100644 index 0000000..c48f794 --- /dev/null +++ b/tools/physical-workcell/record_network_workbench.py @@ -0,0 +1,53 @@ +"""Retain a browser observation with a read-only public-state and journal readback.""" + +import argparse +import time +import urllib.request +from pathlib import Path + +import body_client +import order_protocol as protocol + + +def record(root, name, text_file, action, screenshot=None): + root = protocol.directory(root) + ready = protocol.decode(protocol.read_bytes(root / "ready.json")) + with urllib.request.urlopen(ready["browser_origin"] + "/api/state", timeout=20) as response: + state = protocol.decode(response.read(protocol.MAX_BYTES + 1)) + if state["app_id"] != ready["app_id"]: + raise protocol.Refusal("browser trial application identity changed") + selected = {key: state[key] for key in ("app_id", "runner_status", "jobs", "connection", "recovery", + "model_request_pending", "active_job_id", "current_frame")} + text_file = Path(text_file).resolve() + if root not in text_file.parents: + raise protocol.Refusal("browser observation must be inside the trial root") + visible_text = protocol.read_bytes(text_file, maximum=16000).decode("utf-8") + client = body_client.read_journal(root / "operator" / "client" / "client.json") + path = root / "ui-ledger.json" + ledger = protocol.decode(protocol.read_bytes(path)) if path.exists() else {"steps": []} + if any(step["name"] == name for step in ledger["steps"]): + raise protocol.Refusal("browser observation identity already retained") + step = {"name": name, "time": time.time(), "browser_action": action, + "visible_text": visible_text, "state": selected, "client_commands": len(client["commands"]), + "pending_command_ids": client["pending_command_ids"], "screenshot": screenshot} + ledger["steps"].append(step) + if len(protocol.encoded(ledger)) > protocol.MAX_BYTES: + raise protocol.Refusal("browser observation ledger exceeded its 64 KiB bound") + protocol.persist(path, ledger) + print(name, selected["runner_status"], selected["connection"]["state"], selected["recovery"]["state"], + "commands=" + str(step["client_commands"])) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--name", required=True) + parser.add_argument("--text-file", type=Path, required=True) + parser.add_argument("--action", required=True) + parser.add_argument("--screenshot") + args = parser.parse_args() + record(args.root, args.name, args.text_file, args.action, args.screenshot) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/requirements.txt b/tools/physical-workcell/requirements.txt new file mode 100644 index 0000000..0a4f958 --- /dev/null +++ b/tools/physical-workcell/requirements.txt @@ -0,0 +1 @@ +mujoco==3.15.0 diff --git a/tools/physical-workcell/scene.xml b/tools/physical-workcell/scene.xml new file mode 100644 index 0000000..ff64a43 --- /dev/null +++ b/tools/physical-workcell/scene.xml @@ -0,0 +1,31 @@ + + + diff --git a/tools/physical-workcell/score_agent_bench.py b/tools/physical-workcell/score_agent_bench.py new file mode 100644 index 0000000..93a3afc --- /dev/null +++ b/tools/physical-workcell/score_agent_bench.py @@ -0,0 +1,85 @@ +"""Separate post-trial native/SQLite scoring; never supplies AI action guidance.""" + +import argparse +import json +import subprocess +from pathlib import Path + +import bench +import qualify_bench +import workcell as body + +BASE = Path(__file__).resolve().parent +REPO = BASE.parents[1] +SOURCES = [BASE / name for name in ( + "agent_bench.py", "score_agent_bench.py", "bench.py", "measurement.py", "workcell.py", + "image_guided.py", "episode_cases.py", "order_protocol.py", "scene.xml", "qualify_bench.py")] +SOURCES.append(BASE.parent / "camera-capture-experiment" / "capture.py") + + +def freeze(root, commit): + root = body.directory(root, create=True) + if subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=REPO, text=True).strip() != commit: + raise body.Refusal("trial must start at the named source commit") + files = {} + for path in SOURCES: + relative = str(path.relative_to(REPO)) + content = body.read_bytes(path) + if subprocess.check_output(["git", "show", f"{commit}:{relative}"], cwd=REPO) != content: + raise body.Refusal("measured source differs from its frozen commit") + files[relative] = body.sha(content) + body.create_file(root / "source-freeze.json", body.encoded({"commit": commit, "files": files})) + initial = bench.initialize(root / "bench", diagnostic_enabled=True) + body.create_file(root / "initial.json", body.encoded(initial)) + return {"source_commit": commit, "bench_id": initial["bench_id"], "native_time_s": initial["native_time_s"]} + + +def score(root): + root = body.directory(root) + frozen = body.decoded(body.read_bytes(root / "source-freeze.json")) + assert all(body.sha(body.read_bytes(REPO / name)) == digest for name, digest in frozen["files"].items()) + initial = body.decoded(body.read_bytes(root / "initial.json")) + status = bench.Bench(root / "bench").status() + database = qualify_bench.sqlite_score(root / "bench") + passed = status["state"] == "ready" and status["active_job"] is None and len(status["jobs"]) == database["count"] == 2 + scores = [] + for index, name in enumerate(("ai-ordinary", "ai-disturbed")): + job = status["jobs"].get(name) + if job is None: + passed = False + continue + measurement = job["measurement_receipt"] + reset = job["reset_receipt"] + stage = qualify_bench.native_score(root / "bench", measurement["generation"], + measurement["state_sha256"], job["target_zone"]) + home = qualify_bench.native_score(root / "bench", reset["generation"], reset["state_sha256"], bench.HOME_ZONE) + row = next(row for row in database["rows"] if row["job_id"] == name) + previous_generation = 0 if index == 0 else status["jobs"]["ai-ordinary"]["completed_generation"] + previous_time = initial["native_time_s"] if index == 0 else status["jobs"]["ai-ordinary"]["completed_native_time_s"] + ok = bool(stage["block_center_in_zone"] and home["block_center_in_zone"] and home["pusher_home"] and + stage["external_force_cleared"] and home["external_force_cleared"] and + row["receipt"] == measurement and reset["reset_contact_samples"] > 0 and reset["reset_native_steps"] > 0 and + reset["bench_id"] == measurement["bench_id"] == initial["bench_id"] == status["bench_id"] and + job["started_generation"] == previous_generation and job["started_native_time_s"] == previous_time) + scores.append({"job_id": name, "passed": ok, "measurement_state": stage, "reset_state": home, + "measurement": measurement, "reset": reset, "motion_count": job["moves"]}) + passed &= ok + result = {"version": 1, "passed": passed, "source_commit": frozen["commit"], "source_unchanged": True, + "bench_id": initial["bench_id"], "completed_cycles": len(scores), "scores": scores, + "sqlite": database, "current_status": status, "attempt": 1, + "scope": "native/SQLite post-trial scoring; AI image consumption/proposals are separately recorded in the session"} + body.create_file(root / "native-sqlite-score.json", body.encoded(result)) + return {k: result[k] for k in ("passed", "completed_cycles", "source_unchanged", "bench_id")} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("command", choices=("freeze", "score")) + parser.add_argument("--run-dir", required=True, type=Path) + parser.add_argument("--source-commit") + args = parser.parse_args() + print(json.dumps(freeze(args.run_dir, args.source_commit) if args.command == "freeze" else score(args.run_dir), sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/score_network_body.py b/tools/physical-workcell/score_network_body.py new file mode 100644 index 0000000..a45d472 --- /dev/null +++ b/tools/physical-workcell/score_network_body.py @@ -0,0 +1,151 @@ +"""Independent post-trial native, SQLite, model-input and network receipt checks.""" + +import argparse +import json +from pathlib import Path + +import agent_bench +import bench +import body_client +import order_protocol as protocol +import qualify_bench + +BASE = Path(__file__).resolve().parent +REPO = BASE.parents[1] + + +def read(path): + return protocol.decode(protocol.read_bytes(path)) + + +def private_fields(value): + if isinstance(value, dict): + return bool(set(value) & (agent_bench.DERIVED_LOCATION_FIELDS | {"qpos", "qvel", "values"})) or any( + private_fields(item) for item in value.values()) + if isinstance(value, list): + return any(private_fields(item) for item in value) + return False + + +def score(root): + root = protocol.directory(root) + frozen = read(root / "source-freeze.json") + unchanged = all(protocol.sha(protocol.read_bytes(REPO / name)) == digest for name, digest in frozen["files"].items()) + attempt = read(root / "attempt.json") + identity = read(root / "identity.json") + native_root = root / "body" / "bench" + status = bench.Bench(native_root).status() + initial_content = protocol.read_bytes(bench.body.state_path(native_root, 0)) + initial = qualify_bench.native_score(native_root, 0, protocol.sha(initial_content), bench.HOME_ZONE) + database = qualify_bench.sqlite_score(native_root) + agent_root = root / "body" / "agent" + agent_manifest = read(agent_root / "agent.json") + client_manifest = body_client.read_journal(root / "client" / "client.json") + proxy_events = [read(path) for path in sorted((root / "proxy" / "events").glob("*.json"))] + transport = [] + for directory in (root / "body" / "commands").iterdir(): + record = read(directory / "intent.json") + request = record["request"] + content = protocol.read_bytes(directory / "result.json") + result = protocol.decode(content) + valid = (record["request_sha256"] == protocol.sha(protocol.encoded(request)) == result["request_sha256"] and + record["result_sha256"] == protocol.sha(content) and + directory.name == protocol.sha(request["command_id"].encode()) and + all(result[key] == request[key] for key in ("protocol", "adapter_id", "body_id", "source_sha256", "command_id"))) + transport.append({"request": request, "result": result, "valid": valid}) + jobs, calls = [], [] + previous_generation, previous_time = 0, initial["native_time_s"] + for item in attempt["jobs"]: + job = status["jobs"].get(item["job_id"]) + if not job or job["phase"] != "completed": + jobs.append({"job_id": item["job_id"], "passed": False}) + continue + measurement, reset = job["measurement_receipt"], job["reset_receipt"] + stage = qualify_bench.native_score(native_root, measurement["generation"], measurement["state_sha256"], job["target_zone"]) + home = qualify_bench.native_score(native_root, reset["generation"], reset["state_sha256"], bench.HOME_ZONE) + rows = [row for row in database["rows"] if row["job_id"] == item["job_id"]] + ok = bool(item["completed"] and len(rows) == 1 and rows[0]["receipt"] == measurement and + stage["block_center_in_zone"] and home["block_center_in_zone"] and home["pusher_home"] and + stage["external_force_cleared"] and home["external_force_cleared"] and + reset["reset_contact_samples"] > 0 and reset["reset_native_steps"] > 0 and + reset["bench_id"] == measurement["bench_id"] == identity["body_id"] == status["bench_id"] and + job["started_generation"] == previous_generation and + job["started_native_time_s"] == previous_time) + jobs.append({"job_id": item["job_id"], "passed": ok, "measurement_state": stage, + "reset_state": home, "measurement_receipt": measurement, "reset_receipt": reset, + "motion_count": job["moves"], "model_calls": item["calls"]}) + previous_generation, previous_time = job["completed_generation"], job["completed_native_time_s"] + for directory in sorted((root / "calls").iterdir()): + reservation = read(directory / "reservation.json") + challenge = reservation["challenge"] + image_sha = challenge["observation"]["image_sha256"] + image_path = Path(challenge["image_path"]) + downloaded = root / "client" in image_path.parents + exact = downloaded and protocol.sha(protocol.read_bytes(image_path, maximum=1024 * 1024)) == image_sha + exact &= protocol.sha(protocol.read_bytes(directory / "input.png", maximum=1024 * 1024)) == image_sha + image_requests = [event for event in proxy_events if event["kind"] == "received" and + event.get("path") == "/images/" + challenge["challenge_id"] and event.get("method") == "GET"] + exact &= len(image_requests) == 1 + prompt = json.loads(protocol.read_bytes(directory / "prompt.txt").decode().split("\n", 1)[1]) + proposal = read(directory / "proposal.json") + content = protocol.read_bytes(agent_root / "proposals" / (proposal["proposal_id"] + ".json")) + lower_proposal = protocol.decode(content) + bound = protocol.sha(content) == agent_manifest["proposals"][proposal["proposal_id"]] and lower_proposal == proposal + content = protocol.read_bytes(agent_root / "results" / (proposal["proposal_id"] + ".json")) + lower_result = protocol.decode(content) + bound &= protocol.sha(content) == agent_manifest["results"][proposal["proposal_id"]] + bound &= agent_bench.public_response(lower_result) == read(directory / "dispatch.json") + decision = read(directory / "decision.json")["action"] + bound &= all(proposal[key] == decision[key] for key in ("action", "intent", "target_x", "target_y")) + bound &= proposal["challenge_id"] == challenge["challenge_id"] and proposal["observation_sha256"] == challenge["observation_sha256"] + dispatch_commands = [t for t in transport if t["request"]["operation"] == "dispatch" and + t["request"]["arguments"]["proposal_id"] == proposal["proposal_id"]] + bound &= len(dispatch_commands) == 1 and dispatch_commands[0]["valid"] and ( + dispatch_commands[0]["result"]["result"] == agent_bench.public_response(lower_result)) + network_bound = False + if len(dispatch_commands) == 1: + request = dispatch_commands[0]["request"] + command_id = request["command_id"] + received = [event for event in proxy_events if event["kind"] == "received" and event.get("command_id") == command_id] + local_request = read(root / "client" / "requests" / (command_id + ".json")) + network_bound = len(received) == 1 and local_request == request and client_manifest["commands"][command_id]["send_count"] == 1 + if received: + sent = protocol.read_bytes(root / "proxy" / "requests" / f"{received[0]['sequence']:06d}.bin") + network_bound &= protocol.decode(sent) == request and protocol.sha(sent) == received[0]["payload_sha256"] + model = read(directory / "outcome.json") + reconciled = None + if (directory / "reconciliation.json").exists(): + reconciled = read(directory / "reconciliation.json") + bound &= reconciled["state"] == "completed" and reconciled["result"] == agent_bench.public_response(lower_result) + bound &= len(dispatch_commands) == 1 and dispatch_commands[0]["result"] == reconciled + calls.append({"job_id": reservation["job_id"], "call": reservation["call"], "exact_TCP_image": bool(exact), + "no_private_pose": not private_fields(reservation) and not private_fields(prompt), + "model_process_reaped": model.get("process_exit_observed", False), "core_receipt_bound": bool(bound), + "one_client_send_bound": bool(network_bound), + "action": decision["action"], "outcome": lower_result["outcome"], + "usage": model.get("usage"), "reconciled_command_id": reconciled["command_id"] if reconciled else None}) + model_ok = bool(calls and all(c["exact_TCP_image"] and c["no_private_pose"] and c["model_process_reaped"] and + c["core_receipt_bound"] and c["one_client_send_bound"] for c in calls)) + faults_ok = any(f["mode"] == "drop_after" and f.get("read_only") and f["reconciled_state"] == "completed" for f in attempt["faults"]) + passed = bool(unchanged and attempt["passed"] and attempt["service_exit_observed"] and + len(jobs) == database["count"] == 2 and all(j["passed"] for j in jobs) and model_ok and faults_ok and + transport and all(t["valid"] for t in transport) and not client_manifest["pending_command_ids"] and + status["state"] == "ready" and status["active_job"] is None) + result = {"passed": passed, "source_unchanged": unchanged, "source_commit": frozen["commit"], + "scope": "loopback TCP simulation, actual Codex image policy, independent post-trial native/SQLite checks", + "body_id": identity["body_id"], "completed_cycles": sum(j["passed"] for j in jobs), + "model_boundaries_passed": model_ok, "lost_reply_reconciled": faults_ok, + "transport_journal_bound": all(t["valid"] for t in transport), "transport_command_count": len(transport), + "scores": jobs, "sqlite": database, "calls": calls, "faults": attempt["faults"]} + protocol.create_file(root / "qualification-score.json", protocol.encoded(result)) + return {key: result[key] for key in ("passed", "source_unchanged", "completed_cycles", "model_boundaries_passed", "lost_reply_reconciled")} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", required=True, type=Path) + print(json.dumps(score(parser.parse_args().root), sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/score_network_workbench.py b/tools/physical-workcell/score_network_workbench.py new file mode 100644 index 0000000..ef65692 --- /dev/null +++ b/tools/physical-workcell/score_network_workbench.py @@ -0,0 +1,199 @@ +"""Post-trial checks independent of the browser planner and displayed verdicts.""" + +import argparse +import json +from pathlib import Path + +import agent_bench +import bench +import body_client +import order_protocol as protocol +import qualify_bench +from score_network_body import private_fields + +BASE = Path(__file__).resolve().parent +REPO = BASE.parents[1] + + +def read(path): + return protocol.decode(protocol.read_bytes(path)) + + +def score(root): + root = protocol.directory(root) + host_root = root / "operator" + frozen = read(root / "source-freeze.json") + unchanged = all(protocol.sha(protocol.read_bytes(REPO / name)) == digest + for name, digest in frozen["files"].items()) + process_ledger = read(root / "process-ledger.json") + public = read(root / "final-public.json") + identity = read(root / "identity.json") + native_root = root / "body" / "bench" + native = bench.Bench(native_root).status() + sqlite = qualify_bench.sqlite_score(native_root) + client = body_client.read_journal(host_root / "client" / "client.json") + agent_root = root / "body" / "agent" + agent_manifest = read(agent_root / "agent.json") + events = [read(path) for path in sorted((host_root / "link" / "events").glob("*.json"))] + transport = {} + for directory in (root / "body" / "commands").iterdir(): + record = read(directory / "intent.json") + request = record["request"] + content = protocol.read_bytes(directory / "result.json") + result = protocol.decode(content) + valid = (record["request_sha256"] == protocol.sha(protocol.encoded(request)) == result["request_sha256"] and + record["result_sha256"] == protocol.sha(content) and + directory.name == protocol.sha(request["command_id"].encode()) and + all(result[key] == request[key] for key in ( + "protocol", "adapter_id", "body_id", "source_sha256", "command_id"))) + transport[request["command_id"]] = {"request": request, "result": result, "valid": valid} + + jobs = [] + previous_generation = 0 + initial_content = protocol.read_bytes(bench.body.state_path(native_root, 0)) + previous_time = qualify_bench.native_score(native_root, 0, protocol.sha(initial_content), bench.HOME_ZONE)["native_time_s"] + for item in public["jobs"]: + job_id = item["job_id"] + job = native["jobs"].get(job_id) + if not job or job["phase"] != "completed": + jobs.append({"job_id": job_id, "passed": False}) + continue + measurement, reset = job["measurement_receipt"], job["reset_receipt"] + stage = qualify_bench.native_score(native_root, measurement["generation"], + measurement["state_sha256"], job["target_zone"]) + home = qualify_bench.native_score(native_root, reset["generation"], reset["state_sha256"], bench.HOME_ZONE) + rows = [row for row in sqlite["rows"] if row["job_id"] == job_id] + ok = bool(item["status"] == "completed" and len(rows) == 1 and rows[0]["receipt"] == measurement and + item["measurement"] == agent_bench.public_response(measurement["measurement"]) and + stage["block_center_in_zone"] and home["block_center_in_zone"] and home["pusher_home"] and + stage["external_force_cleared"] and home["external_force_cleared"] and + reset["reset_contact_samples"] > 0 and reset["reset_native_steps"] > 0 and + reset["bench_id"] == measurement["bench_id"] == identity["body_id"] == native["bench_id"] and + job["started_generation"] == previous_generation and job["started_native_time_s"] == previous_time) + jobs.append({"job_id": job_id, "passed": ok, "measurement_state": stage, "reset_state": home, + "measurement_receipt": measurement, "reset_receipt": reset, + "motion_count": job["moves"], "model_calls": item["call_count"]}) + previous_generation, previous_time = job["completed_generation"], job["completed_native_time_s"] + + calls = [] + for directory in sorted(path for path in (host_root / "calls").iterdir() if path.is_dir()): + reservation = read(directory / "reservation.json") + challenge = reservation["challenge"] + digest = challenge["observation"]["image_sha256"] + image_path = Path(challenge["image_path"]) + exact = (host_root / "client" in image_path.parents and + protocol.sha(protocol.read_bytes(image_path, maximum=1024 * 1024)) == digest and + protocol.sha(protocol.read_bytes(directory / "input.png", maximum=1024 * 1024)) == digest) + image_requests = [e for e in events if e["kind"] == "received" and e.get("method") == "GET" and + e.get("path") == "/images/" + challenge["challenge_id"]] + exact &= bool(image_requests) + prompt = json.loads(protocol.read_bytes(directory / "prompt.txt").decode().split("\n", 1)[1]) + model = read(directory / "outcome.json") + bound, one_send, action = False, False, None + missing_dispatch = not (directory / "dispatch.json").exists() + associated_dispatches = [] + if (directory / "proposal.json").exists(): + candidate = read(directory / "proposal.json") + associated_dispatches = [t for t in transport.values() if t["request"]["operation"] == "dispatch" and + t["request"]["arguments"]["proposal_id"] == candidate["proposal_id"]] + withheld = missing_dispatch and model.get("outcome") == "cancelled" and not associated_dispatches + if not missing_dispatch: + proposal = read(directory / "proposal.json") + decision = read(directory / "decision.json")["action"] + action = decision["action"] + pbytes = protocol.read_bytes(agent_root / "proposals" / (proposal["proposal_id"] + ".json")) + rbytes = protocol.read_bytes(agent_root / "results" / (proposal["proposal_id"] + ".json")) + lower_result = read(agent_root / "results" / (proposal["proposal_id"] + ".json")) + bound = (protocol.sha(pbytes) == agent_manifest["proposals"][proposal["proposal_id"]] and + protocol.decode(pbytes) == proposal and + protocol.sha(rbytes) == agent_manifest["results"][proposal["proposal_id"]] and + agent_bench.public_response(lower_result) == read(directory / "dispatch.json") and + all(proposal[k] == decision[k] for k in ("action", "intent", "target_x", "target_y")) and + proposal["challenge_id"] == challenge["challenge_id"] and + proposal["observation_sha256"] == challenge["observation_sha256"]) + matches = [t for t in transport.values() if t["request"]["operation"] == "dispatch" and + t["request"]["arguments"]["proposal_id"] == proposal["proposal_id"]] + bound &= len(matches) == 1 and matches[0]["valid"] and ( + matches[0]["result"]["result"] == agent_bench.public_response(lower_result)) + if len(matches) == 1: + request = matches[0]["request"] + token = request["command_id"] + received = [e for e in events if e["kind"] == "received" and e.get("method") == "POST" and + e.get("command_id") == token] + one_send = (len(received) == 1 and read(host_root / "client" / "requests" / (token + ".json")) == request and + client["commands"][token]["send_count"] == 1) + if received: + sent = protocol.read_bytes(host_root / "link" / "requests" / f"{received[0]['sequence']:06d}.bin") + one_send &= protocol.decode(sent) == request and protocol.sha(sent) == received[0]["payload_sha256"] + calls.append({"job_id": reservation["job_id"], "call": reservation["call"], "action": action, + "withheld": withheld, "exact_TCP_image": bool(exact), + "no_private_pose": not private_fields(reservation) and not private_fields(prompt), + "model_process_reaped": model.get("process_exit_observed", False), + "core_receipt_bound": bool(bound), "one_client_send_bound": bool(one_send), + "usage": model.get("usage")}) + + boundaries = bool(calls and all(c["exact_TCP_image"] and c["no_private_pose"] and c["model_process_reaped"] and + (c["withheld"] or c["core_receipt_bound"] and c["one_client_send_bound"]) for c in calls)) + ui = read(root / "ui-ledger.json") + steps = {step["name"]: step for step in ui["steps"]} + required = {"uncertain", "disconnected", "offline_receipt", "reconnected", "recovered", "resumed", "completed", "reloaded"} + ui_ok = required <= steps.keys() + recovery_token = None + if ui_ok: + visible_expectations = {"uncertain": "uncertain", "disconnected": "Body disconnected", + "offline_receipt": "Check receipt", "reconnected": "Body connected", + "recovered": "Receipt recovered", "resumed": "Running", + "completed": "Completed", "reloaded": "Completed"} + for name, phrase in visible_expectations.items(): + ui_ok &= phrase.casefold() in steps[name]["visible_text"].casefold() + uncertain = steps["uncertain"]["state"] + recovery_token = uncertain["recovery"]["command_id"] + ui_ok &= uncertain["recovery"]["state"] == "uncertain" and uncertain["runner_status"] != "running" + ui_ok &= steps["disconnected"]["state"]["connection"]["state"] == "disconnected" + for name in ("disconnected", "offline_receipt", "reconnected"): + value = steps[name]["state"] + ui_ok &= value["runner_status"] != "running" and value["recovery"]["command_id"] == recovery_token + ui_ok &= value["recovery"]["state"] in {"uncertain", "blocked"} + ui_ok &= steps[name]["client_commands"] == steps["uncertain"]["client_commands"] + recovered = steps["recovered"]["state"] + ui_ok &= recovered["recovery"]["state"] == "recovered" and recovered["runner_status"] != "running" + ui_ok &= steps["recovered"]["client_commands"] == steps["uncertain"]["client_commands"] + ui_ok &= steps["resumed"]["state"]["runner_status"] == "running" + for name in required: + ui_ok &= steps[name]["state"]["app_id"] == public["app_id"] + ui_ok &= steps[name]["state"]["connection"]["body_id"] == identity["body_id"] + ui_ok &= bool(steps[name]["visible_text"]) and bool(steps[name]["browser_action"]) + ui_ok &= steps["completed"]["state"]["jobs"] == steps["reloaded"]["state"]["jobs"] + ui_ok &= steps["completed"]["client_commands"] == steps["reloaded"]["client_commands"] + dropped = [e for e in events if e["kind"] == "dropped_after" and e.get("command_id") == recovery_token] + reconciliation = [e for e in events if e["kind"] == "received" and e.get("method") == "GET" and + e.get("path", "").startswith("/commands/" + str(recovery_token) + "?")] + received = [e for e in events if e["kind"] == "received" and e.get("method") == "POST" and + e.get("command_id") == recovery_token] + fault_ok = len(dropped) == len(received) == 1 and bool(reconciliation) and recovery_token in transport + fault_ok &= bool(recovery_token in transport and transport[recovery_token]["result"]["state"] == "completed") + passed = bool(unchanged and process_ledger["owned_processes_exited"] and not process_ledger.get("error") and + len(jobs) == sqlite["count"] == 2 and all(j["passed"] for j in jobs) and boundaries and ui_ok and + fault_ok and transport and all(t["valid"] for t in transport.values()) and + not client["pending_command_ids"] and native["state"] == "ready" and native["active_job"] is None) + result = {"passed": passed, "source_commit": frozen["commit"], "source_unchanged": unchanged, + "scope": "browser UI and actual image policy over loopback TCP simulation", + "body_id": identity["body_id"], "completed_cycles": sum(j["passed"] for j in jobs), + "browser_transitions_passed": bool(ui_ok), "model_boundaries_passed": boundaries, + "lost_reply_reconciled_without_resend": bool(fault_ok), + "transport_journal_bound": all(t["valid"] for t in transport.values()), + "transport_command_count": len(transport), "calls": calls, "scores": jobs, "sqlite": sqlite, + "ui_steps": sorted(steps), "owned_processes_exited": process_ledger["owned_processes_exited"]} + protocol.create_file(root / "qualification-score.json", protocol.encoded(result)) + return {k: result[k] for k in ("passed", "completed_cycles", "browser_transitions_passed", "model_boundaries_passed", + "lost_reply_reconciled_without_resend", "source_unchanged")} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True) + print(json.dumps(score(parser.parse_args().root), sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/score_operator.py b/tools/physical-workcell/score_operator.py new file mode 100644 index 0000000..631bad4 --- /dev/null +++ b/tools/physical-workcell/score_operator.py @@ -0,0 +1,182 @@ +"""Post-trial native, SQLite and model-boundary scorer; never guides the policy.""" +import argparse +import json +import math +import subprocess +from pathlib import Path + +import agent_bench +import bench +import operator_runtime as runtime +import order_protocol as protocol +import qualify_bench + +BASE = Path(__file__).resolve().parent +REPO = BASE.parents[1] +NAMES = ("bench_operator.py", "bench_operator.html", "operator_runtime.py", "codex_policy.py", + "codex-policy.md", "codex-action.schema.json", "agent_bench.py", "bench.py", "measurement.py", + "workcell.py", "image_guided.py", "episode_cases.py", "order_protocol.py", "scene.xml", + "qualify_bench.py", "score_operator.py") + + +def freeze(root, commit): + if subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=REPO, text=True).strip() != commit: + raise protocol.Refusal("qualification must start at the named source commit") + files = {} + for path in [*(BASE / name for name in NAMES), BASE.parent / "camera-capture-experiment" / "capture.py"]: + relative = str(path.relative_to(REPO)) + content = protocol.read_bytes(path) + if subprocess.check_output(["git", "show", f"{commit}:{relative}"], cwd=REPO) != content: + raise protocol.Refusal("measured source differs from its frozen commit") + files[relative] = protocol.sha(content) + runtime.initialize(root) + root = protocol.directory(root) + protocol.create_file(root / "source-freeze.json", protocol.encoded({"commit": commit, "files": files, "qualification_version": 2})) + protocol.create_file(root / "initial.json", protocol.encoded(bench.Bench(root / "bench").status())) + return {"source_commit": commit, "root": str(root)} + + +def private_fields(value): + if isinstance(value, dict): + return bool(agent_bench.DERIVED_LOCATION_FIELDS.intersection(value)) or any(private_fields(v) for v in value.values()) + return isinstance(value, list) and any(private_fields(v) for v in value) + + +def fault_score(root, calls): + read = lambda path: protocol.decode(protocol.read_bytes(path)) + native, agent = read(root / "bench/bench.json"), read(root / "agent/agent.json") + forces = [] + for path in (root / "bench/operations").glob("*.json"): + content = protocol.read_bytes(path) + value = protocol.decode(content) + if value["request"].get("kind") == "disturb": + receipt = value["receipt"] + if native["operations"].get(receipt["operation_id"]) != protocol.sha(content): + raise protocol.Refusal("force receipt differs from retained native operation") + forces.append(value) + if len(forces) != 1: + return {"passed": False, "reason": "expected exactly one native force intervention"} + force = forces[0] + receipt = force["receipt"] + stale = next((c for c in calls if c["job_id"] == receipt["job_id"] and c.get("outcome") == "refused" and + c.get("decision", {}).get("action") == "measure" and + c.get("reason") == "observation is stale or foreign; no requested effect dispatched"), None) + if stale is None: + return {"passed": False, "reason": "exact stale measurement refusal absent"} + directory = Path(stale["call_dir"]) + proposal, dispatch = read(directory / "proposal.json"), read(directory / "dispatch.json") + challenge = read(directory / "reservation.json")["challenge"] + after = dispatch["next_observation"]["observation"] + before = challenge["observation"] + bound = proposal["challenge_id"] == challenge["challenge_id"] and proposal["observation_sha256"] == challenge["observation_sha256"] + for kind, token, value in (("proposals", proposal["proposal_id"], proposal), + ("challenges", challenge["challenge_id"], challenge), + ("results", proposal["proposal_id"], dispatch)): + content = protocol.read_bytes(root / "agent" / kind / (token + ".json")) + bound &= agent[kind].get(token) == protocol.sha(content) and agent_bench.public_response(protocol.decode(content)) == value + zone = challenge["task"]["zone"] + before_state = qualify_bench.native_score(root / "bench", before["generation"], before["state_sha256"], zone) + after_state = qualify_bench.native_score(root / "bench", receipt["generation"], receipt["state_sha256"], zone) + displacement = math.dist(before_state["block_xyz"], after_state["block_xyz"]) + no_measurement_effect = proposal["operation_id"] not in native["operations"] and not ( + root / "bench/operations" / (protocol.sha(proposal["operation_id"].encode()) + ".json")).exists() + fresh_move = next((c for c in calls if c["job_id"] == receipt["job_id"] and c["call"] > stale["call"] and + c.get("outcome") == "executed" and c.get("decision", {}).get("action") == "move"), None) + fresh_binding = False + if fresh_move: + fresh = read(Path(fresh_move["call_dir"]) / "reservation.json")["challenge"]["observation"] + fresh_binding = fresh == after and fresh_move["image_sha256"] == after["image_sha256"] + passed = bool(bound and no_measurement_effect and fresh_binding and displacement > .02 and + before_state["block_center_in_zone"] and not after_state["block_center_in_zone"] and + receipt["generation"] == before["generation"] + 1 == after["generation"] and + receipt["state_sha256"] == after["state_sha256"] and + receipt["bench_id"] == before["bench_id"] == after["bench_id"] == native["bench_id"] and + receipt["job_id"] == before["job_id"] == after["job_id"] and + force["request"] == {"kind": "disturb", "force_x_n": -1.5, "steps": 80} and + receipt["force_x_n"] == -1.5 and receipt["forced_steps"] == 80 and receipt["native_steps"] == 580 and + after_state["external_force_cleared"] and after_state["native_time_s"] > before_state["native_time_s"]) + return {"passed": passed, "force": force, "displacement_m": displacement, + "before": before_state, "after": after_state, "saved_proposal_id": proposal["proposal_id"], + "immutable_chain_bound": bool(bound), "no_measurement_effect": no_measurement_effect, + "fresh_model_move_bound_to_force_image": bool(fresh_binding), "stale_call": stale["call"]} + + +def score(root): + root = protocol.directory(root) + read = lambda path: protocol.decode(protocol.read_bytes(path)) + frozen, initial = read(root / "source-freeze.json"), read(root / "initial.json") + unchanged = all(protocol.sha(protocol.read_bytes(REPO / name)) == digest for name, digest in frozen["files"].items()) + operator = read(root / "operator.json") + status = bench.Bench(root / "bench").status() + database = qualify_bench.sqlite_score(root / "bench") + scores, calls = [], [] + previous_generation, previous_time = initial["generation"], initial["native_time_s"] + for item in operator["jobs"]: + job = status["jobs"].get(item["id"]) + if not job or job["phase"] != "completed": + scores.append({"job_id": item["id"], "passed": False, "reason": "cycle not completed"}) + continue + measurement, reset = job["measurement_receipt"], job["reset_receipt"] + stage = qualify_bench.native_score(root / "bench", measurement["generation"], measurement["state_sha256"], job["target_zone"]) + home = qualify_bench.native_score(root / "bench", reset["generation"], reset["state_sha256"], bench.HOME_ZONE) + rows = [row for row in database["rows"] if row["job_id"] == item["id"]] + passed = bool(item["status"] == "completed" and len(rows) == 1 and rows[0]["receipt"] == measurement and + item["measurement"] == agent_bench.public_response(measurement["measurement"]) and + stage["block_center_in_zone"] and home["block_center_in_zone"] and home["pusher_home"] and + stage["external_force_cleared"] and home["external_force_cleared"] and + reset["reset_contact_samples"] > 0 and reset["reset_native_steps"] > 0 and + reset["bench_id"] == measurement["bench_id"] == initial["bench_id"] == status["bench_id"] and + job["started_generation"] == previous_generation and job["started_native_time_s"] == previous_time) + scores.append({"job_id": item["id"], "label": item["label"], "passed": passed, + "measurement_state": stage, "reset_state": home, "measurement": measurement, + "reset": reset, "motion_count": job["moves"], "model_calls": item["call_count"]}) + previous_generation, previous_time = job["completed_generation"], job["completed_native_time_s"] + for directory in sorted(p for p in (root / "calls").iterdir() if p.is_dir()): + reservation = read(directory / "reservation.json") + record = {"job_id": reservation["job_id"], "call": reservation["call"], "call_dir": str(directory), + "image_sha256": reservation["challenge"]["observation"]["image_sha256"], + "input_has_no_private_pose": not private_fields(reservation)} + if (directory / "outcome.json").exists(): + record["process"] = read(directory / "outcome.json") + if (directory / "input.png").exists(): + record["exact_image"] = protocol.sha(protocol.read_bytes(directory / "input.png", maximum=1024 * 1024)) == record["image_sha256"] + prompt = protocol.read_bytes(directory / "prompt.txt").decode().split("\n", 1)[1] + record["input_has_no_private_pose"] &= not private_fields(json.loads(prompt)) + if (directory / "decision.json").exists(): + record["decision"] = read(directory / "decision.json")["action"] + if (directory / "dispatch.json").exists(): + dispatch = read(directory / "dispatch.json") + record.update(outcome=dispatch["outcome"], reason=dispatch.get("reason", "")) + calls.append(record) + disturbance_job = next((j["id"] for j in operator["jobs"] if j["disturbance_test"]), None) + stale = next((c for c in calls if c["job_id"] == disturbance_job and c.get("outcome") == "refused" and + c.get("decision", {}).get("action") == "measure"), None) + recovery = bool(stale and any(c["job_id"] == disturbance_job and c["call"] > stale["call"] and + c.get("outcome") == "executed" and c.get("decision", {}).get("action") == "move" for c in calls)) + model_boundaries = bool(calls and all(c.get("exact_image") and c["input_has_no_private_pose"] and + c.get("process", {}).get("process_exit_observed") for c in calls)) + fault = fault_score(root, calls) + passed = bool(unchanged and len(scores) == database["count"] == 2 and all(s["passed"] for s in scores) and + status["state"] == "ready" and status["active_job"] is None and recovery and model_boundaries and fault["passed"]) + result = {"version": 1, "attempt": 1, "passed": passed, "source_commit": frozen["commit"], + "source_unchanged": unchanged, "bench_id": status["bench_id"], "completed_cycles": sum(s["passed"] for s in scores), + "qualification_version": frozen.get("qualification_version", 1), + "stale_measure_refused": stale is not None, "fresh_model_move_after_refusal": recovery, "fault_chain": fault, + "model_boundaries_passed": model_boundaries, "scores": scores, "sqlite": database, "calls": calls, + "usage": operator["usage"], "current_status": status, + "scope": "separate post-trial native/SQLite and retained model-boundary scoring; browser evidence recorded separately"} + protocol.create_file(root / "qualification-score.json", protocol.encoded(result)) + return {k: result[k] for k in ("passed", "completed_cycles", "source_unchanged", "stale_measure_refused", "fresh_model_move_after_refusal")} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("command", choices=("freeze", "score")) + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--source-commit") + args = parser.parse_args() + print(json.dumps(freeze(args.root, args.source_commit) if args.command == "freeze" else score(args.root), sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/score_workbench_crashes.py b/tools/physical-workcell/score_workbench_crashes.py new file mode 100644 index 0000000..bef545b --- /dev/null +++ b/tools/physical-workcell/score_workbench_crashes.py @@ -0,0 +1,467 @@ +"""Independent stopped-evidence scorer for the four SIM13 hard-crash cases.""" +import argparse +import json +import math +from pathlib import Path + +import agent_bench +import bench +import body_client +import body_protocol as wire +import codex_policy +import order_protocol as protocol +import qualify_bench +from score_network_body import private_fields +from score_workbench_restarts import read + +BASE = Path(__file__).resolve().parent +REPO = BASE.parents[1] +CASES = {"host-inflight", "body-native-gap", "body-uncertain", "host-uncertain"} + + +def require(condition, reason): + if not condition: + raise protocol.Refusal(reason) + + +def scoped(root, folder, limit): + events = [read(path) for path in sorted((root / folder).glob("*.json"))] + key = "seq" if folder == "operator/events" else "sequence" + return [event for event in events if event[key] <= limit] + + +def indexed(root, manifest, kind, token): + content = protocol.read_bytes(root / kind / (token + ".json")) + require(manifest[kind].get(token) == protocol.sha(content), "lower indexed evidence digest differs") + return protocol.decode(content) + + +def envelope_bound(request, value): + protocol.fields(value, wire.ENVELOPE_FIELDS, "crash command receipt") + return bool(value["state"] in {"completed", "refused", "not_accepted", "unknown"} and + value["receipt_scope"] == "retained_command_result" and + type(value["recorded_epoch"]) is int and value["recorded_epoch"] == request["epoch"] and + value["request_sha256"] == protocol.sha(protocol.encoded(request)) and + all(value[key] == request[key] for key in ("protocol", "adapter_id", "body_id", "source_sha256", "command_id"))) + + +def command_identity(snapshot): + return {token: {key: value[key] for key in ("request_sha256", "send_count", "operation")} + for token, value in snapshot["client"]["commands"].items()} + + +def boundary_score(root, boundary, identity, transport, events): + case, role, token = boundary["case"], boundary["role"], boundary["command_id"] + require(case in CASES and role == ("body" if case.startswith("body") else "workbench"), "crash role or case changed") + request = transport[token]["request"] + require(request["operation"] == "dispatch" and boundary["request_sha256"] == protocol.sha(protocol.encoded(request)), + "crash token is not its exact accepted dispatch") + snapshots = [read(root / "snapshots" / boundary[name]) for name in ("pre_snapshot", "offline_snapshot", "restored_snapshot")] + before, offline, restored = snapshots + for snapshot in snapshots: + require(snapshot["host"]["app_id"] == before["host"]["app_id"] and + all(snapshot["host"]["body_identity"][key] == identity[key] for key in body_client.IDENTITY_KEYS - {"epoch"}), + "crash changed stable application/body pins") + stage = snapshot["host"]["stage"] + require(stage and stage["operation"] == "dispatch" and stage["arguments"] == request["arguments"] and + stage.get("command_id") in (None, token) and token not in stage["before_commands"] and + token in snapshot["client"]["pending_command_ids"], "crash lost its original pending host/client stage") + require(command_identity(snapshot) == command_identity(before) and snapshot["posts"] == before["posts"], + "restart introduced a client command or POST") + require(snapshot["client"]["lease"] == before["client"]["lease"], "unknown command rotated its historical lease") + process = boundary["process"] + require(process["exit_observed"] is True and process["returncode"] == -9 and process["group"] == process["pid"] and + before["processes"][role] == process["pid"] and offline["processes"][role] is None and + restored["processes"][role] not in (None, process["pid"]), "crash was not an observed owned SIGKILL and independent restart") + require(process.get("children_before") == [] and process.get("children_after") == [], "crash gate has unaccounted owned children") + require(before["public"] and before["public"]["model_request_pending"] is False, + "selected command gate had an active planner") + require(boundary["old_epoch"] == request["epoch"] and + boundary["new_epoch"] == boundary["old_epoch"] + (role == "body") and + restored["body_identity"]["epoch"] == boundary["new_epoch"], "restart boot epoch is not bound to crashed authority") + public = restored["public"] + require(public and public["runner_status"] == "blocked" and not public["model_request_pending"] and + public["recovery"]["command_id"] == token, "restart was not visibly paused with the exact uncertain command") + if case == "host-inflight": + marker = boundary["gate"] + require(marker == read(root / "relay/barrier.json") and marker["barrier"] == "body_receipt_before_link_reply" and + marker["command_id"] == token and marker["request_sha256"] == boundary["request_sha256"] and + marker["response_sha256"] == protocol.sha(protocol.encoded(transport[token]["result"])) and + marker["proposal_id"] == request["arguments"]["proposal_id"] and marker["body_id"] == request["body_id"] and + marker["epoch"] == request["epoch"], "held host reply is not its independently indexed body completion") + elif case != "body-native-gap": + require(boundary["gate"] is None and before["public"]["runner_status"] == "blocked" and + before["public"]["recovery"]["command_id"] == token and before["body_command"]["terminal"] == transport[token]["result"], + "already-uncertain crash did not have known independent body proof") + future = [event for event in events if event["seq"] > restored["host"]["sequence"]] + reconciled = next((event for event in future if event["kind"] == "reconciled" and event["outcome"] == token), None) + resumed = next((event for event in future if event["kind"] == "resume"), None) + if case == "body-native-gap": + require(reconciled is None and resumed is None, "missing native proof was promoted to recovered or resumed") + else: + require(reconciled and resumed and reconciled["seq"] < resumed["seq"], "exact receipt must precede explicit Resume") + return {"passed": True, "case": case, "role": role, "command_id": token, + "request_sha256": boundary["request_sha256"], "old_epoch": boundary["old_epoch"], + "new_epoch": boundary["new_epoch"], "restored_sequence": restored["host"]["sequence"], + "reconciled_sequence": reconciled["seq"] if reconciled else None, + "resume_sequence": resumed["seq"] if resumed else None, "snapshot_command_count": len(before["client"]["commands"])} + + +def relay_records(root): + records = [] + for directory in sorted((root / "relay" / "requests").iterdir()): + request = read(directory / "request.json") + payload = protocol.read_bytes(directory / "request.bin") + require(directory.name == f"{request['sequence']:06d}" and request["payload_sha256"] == protocol.sha(payload) and + request["upstream_sends"] == 1, "relay request bytes or actual-send evidence changed") + upstream, response = None, None + if (directory / "response.json").exists(): + upstream = read(directory / "response.json") + if (directory / "response.bin").exists(): + response = protocol.read_bytes(directory / "response.bin", maximum=body_client.MAX_IMAGE) + require(upstream["sha256"] == protocol.sha(response), "relay response digest differs") + records.append({"request": request, "payload": payload, "upstream": upstream, "response": response}) + require(records and [r["request"]["sequence"] for r in records] == list(range(1, len(records) + 1)), "relay sequence is incomplete") + return records + + +def transport_score(root, identity, client, link_events, relay, gap_token): + result = {} + received = [event for event in link_events if event["kind"] == "received" and event.get("method") == "POST" and event.get("command_id")] + for token, index in client["commands"].items(): + content = protocol.read_bytes(root / "operator/client/requests" / (token + ".json")) + request = wire.validate_command(protocol.decode(content)) + require(index["send_count"] == 1 and protocol.sha(content) == index["request_sha256"] and request["command_id"] == token and + all(request[key] == identity[key] for key in ("protocol", "adapter_id", "body_id", "source_sha256")), "client request identity changed") + matches = [e for e in received if e["command_id"] == token] + relay_matches = [r for r in relay if r["request"]["method"] == "POST" and r["request"]["path"] == "/commands" and protocol.decode(r["payload"])["command_id"] == token] + require(len(matches) == len(relay_matches) == 1, "command has zero or multiple actual wire sends") + transmitted = protocol.read_bytes(root / "operator/link/requests" / f"{matches[0]['sequence']:06d}.bin") + require(transmitted == content == relay_matches[0]["payload"] and matches[0]["payload_sha256"] == protocol.sha(content), + "client/link/relay command bytes disagree") + directory = root / "body/commands" / protocol.sha(token.encode()) + accepted = read(directory / "intent.json") + require(accepted["request"] == request and accepted["request_sha256"] == protocol.sha(content), "independent body acceptance differs") + terminal = None + if accepted["result_sha256"] is not None: + result_bytes = protocol.read_bytes(directory / "result.json") + terminal = protocol.decode(result_bytes) + require(protocol.sha(result_bytes) == accepted["result_sha256"] and envelope_bound(request, terminal), "body terminal receipt is not bound") + else: + require(token == gap_token and request["operation"] == "dispatch", "unexpected unresolved accepted body command") + response = None + if "response_sha256" in index: + response_bytes = protocol.read_bytes(root / "operator/client/responses" / (token + "-" + index["response_sha256"] + ".json")) + response = protocol.decode(response_bytes) + require(protocol.sha(response_bytes) == index["response_sha256"] and envelope_bound(request, response), "client retained response changed") + if token != gap_token: + require(terminal and terminal["state"] == "completed" and response == terminal and token not in client["pending_command_ids"], + "known command lacks exact terminal client/body receipt") + else: + require(token in client["pending_command_ids"] and (response is None or response["state"] == "unknown"), "native gap was incorrectly settled") + result[token] = {"request": request, "result": terminal, "received": matches[0], "relay": relay_matches[0]} + require(len(received) == len(result) and len(list((root / "body/commands").iterdir())) == len(result), "extra effect send/acceptance escaped client accounting") + return result + + +def calls_score(root, public, transport, relay, gap_token): + agent_root = root / "body/agent" + manifest = read(agent_root / "agent.json") + calls, proposal_ids, native_operations = [], set(), set() + native_manifest = read(root / "body/bench/bench.json") + for directory in sorted((root / "operator/calls").iterdir()): + reservation = read(directory / "reservation.json") + challenge = reservation["challenge"] + image = challenge["observation"]["image_sha256"] + core = indexed(agent_root, manifest, "challenges", challenge["challenge_id"]) + require({**agent_bench.public_response(core), "image_path": challenge["image_path"]} == challenge and + protocol.sha(protocol.encoded(challenge["observation"])) == challenge["observation_sha256"], "model reservation differs from indexed camera challenge") + image_path = Path(challenge["image_path"]) + require(image_path.parent == root / "operator/client/images" and + protocol.sha(protocol.read_bytes(image_path, maximum=body_client.MAX_IMAGE)) == image and + protocol.sha(protocol.read_bytes(directory / "input.png", maximum=body_client.MAX_IMAGE)) == image, + "model did not receive its exact downloaded image") + image_reads = [r for r in relay if r["request"]["method"] == "GET" and r["request"]["path"] == "/images/" + challenge["challenge_id"] and + r["upstream"] and r["upstream"].get("status") == 200 and r["response"] and protocol.sha(r["response"]) == image] + require(image_reads, "model image lacks actual TCP image reply") + prompt_bytes = protocol.read_bytes(directory / "prompt.txt") + prompt = json.loads(prompt_bytes.decode().split("\n", 1)[1]) + packet = challenge["observation"] + expected = agent_bench.public_response({"current": {"job_id": packet["job_id"], "phase": packet["phase"], + "generation": packet["generation"], "calibration": packet["calibration"], "task": challenge["task"]}, "recent_actions": reservation["history"][-12:]}) + require(prompt == expected and not private_fields(reservation) and not private_fields(prompt), "model received hidden pose or different task/history") + model, decision = read(directory / "outcome.json"), read(directory / "decision.json") + chosen = codex_policy.validate_action(read(directory / "result.json")) + request = read(directory / "request.json") + request_sha = protocol.sha(prompt_bytes + protocol.read_bytes(directory / "input.png", maximum=body_client.MAX_IMAGE) + + protocol.read_bytes(directory / "instructions.md") + protocol.read_bytes(directory / "schema.json")) + trace = protocol.read_bytes(directory / "events.jsonl", maximum=codex_policy.MAX_TRACE_BYTES) + trace_events = [json.loads(line) for line in trace.decode().splitlines() if line.strip()] + messages = [event["item"]["text"] for event in trace_events if event.get("type") == "item.completed" and + event.get("item", {}).get("type") == "agent_message"] + require(model["provider"] == "codex_cli" and model.get("outcome") == "decision" and model["process_exit_observed"] is True and + model["exit_code"] == 0 and decision["action"] == chosen and decision["metadata"] == model and + messages and protocol.decode(messages[-1].encode()) == chosen and any(event.get("type") == "turn.completed" for event in trace_events) and + model["cli_version"] == request["cli_version"] == codex_policy.EXPECTED_CLI and + protocol.read_bytes(directory / "instructions.md") == protocol.read_bytes(BASE / "codex-policy.md") and + protocol.read_bytes(directory / "schema.json") == protocol.read_bytes(BASE / "codex-action.schema.json") and + request.get("tools_enabled") is False and request["request_sha256"] == model["request_sha256"] == request_sha and + request["image_sha256"] == model["image_sha256"] == image and + codex_policy.usage_from_events(trace) == model["usage"], + "actual image-only Codex decision or process proof is incomplete") + proposal = read(directory / "proposal.json") + lower = indexed(agent_root, manifest, "proposals", proposal["proposal_id"]) + require(lower == proposal and proposal["challenge_id"] == challenge["challenge_id"] and + proposal["observation_sha256"] == challenge["observation_sha256"] and + all(proposal[key] == chosen[key] for key in ("action", "intent", "target_x", "target_y")), "AI decision/proposal/challenge binding differs") + require(proposal["proposal_id"] not in proposal_ids, "two model calls share a proposal") + proposal_ids.add(proposal["proposal_id"]) + dispatches = [(token, record) for token, record in transport.items() if record["request"]["operation"] == "dispatch" and + record["request"]["arguments"]["proposal_id"] == proposal["proposal_id"]] + require(len(dispatches) == 1, "model proposal has zero or multiple accepted dispatch commands") + token, dispatch = dispatches[0] + intent = indexed(agent_root, manifest, "intents", proposal["proposal_id"]) + require(intent["proposal_sha256"] == manifest["proposals"][proposal["proposal_id"]] and + intent["challenge_sha256"] == manifest["challenges"][challenge["challenge_id"]] and intent["operation_id"] == proposal["operation_id"], "lower dispatch intent differs") + if token == gap_token: + require(not (directory / "dispatch.json").exists() and proposal["proposal_id"] not in manifest["results"] and + dispatch["result"] is None, "missing native proof gained a terminal dispatch") + else: + result = indexed(agent_root, manifest, "results", proposal["proposal_id"]) + require(result["proposal_id"] == proposal["proposal_id"] and result["outcome"] in {"executed", "refused"} and + agent_bench.public_response(result) == read(directory / "dispatch.json") == dispatch["result"]["result"], "dispatch lacks exact lower/native/host receipt") + if result["outcome"] == "executed" and chosen["action"] != "begin_reset": + operation = proposal["operation_id"] + operation_bytes = protocol.read_bytes(root / "body/bench/operations" / (protocol.sha(operation.encode()) + ".json")) + native_record = protocol.decode(operation_bytes) + require(native_manifest["operations"].get(operation) == protocol.sha(operation_bytes) and + native_record["request"]["kind"] == chosen["action"] and native_record["request"]["observation"] == challenge["observation"] and + native_record["receipt"] == result["receipt"] and native_record["receipt"]["operation_id"] == operation, + "executed AI dispatch lacks its exact indexed native operation") + if chosen["action"] == "move": + require(native_record["request"]["target_x"] == chosen["target_x"] and native_record["request"]["target_y"] == chosen["target_y"] and + native_record["receipt"]["native_steps"] > 0 and native_record["receipt"]["max_axis_actuator_force_n"] <= 4.00000001, + "native move differs from its bounded model target") + native_operations.add(operation) + calls.append({"job_id": reservation["job_id"], "call": reservation["call"], "action": chosen["action"], + "command_id": token, "proposal_id": proposal["proposal_id"], "image_sha256": image, + "challenge_id": challenge["challenge_id"], "camera_generation": packet["generation"], + "native_gap": token == gap_token, "model_pid": model["pid"], "passed": True}) + require(calls and len(calls) == public["usage"]["calls"] == sum(j["call_count"] for j in public["jobs"]) and + {(c["job_id"], c["call"]) for c in calls} == {(j["job_id"], n) for j in public["jobs"] for n in range(1, j["call_count"] + 1)}, + "reserved model calls are missing, duplicated or outside final scope") + require(proposal_ids == {record["request"]["arguments"]["proposal_id"] for record in transport.values() if record["request"]["operation"] == "dispatch"}, + "accepted dispatch escaped actual image-model accounting") + require(native_operations == set(native_manifest["operations"]), "native effect escaped actual model-dispatch accounting") + return calls + + +def fresh_admissions(transport, relay, events): + proofs = [event for event in events if event["kind"] == "admission_proof"] + used, reports, previous_send = set(), [], float("-inf") + for token, record in sorted(transport.items(), key=lambda item: item[1]["received"]["sequence"]): + request = record["request"] + sent_time = record["relay"]["request"]["started_time"] + candidates = [event for event in proofs if event["seq"] not in used and event["outcome"] == request["operation"] and + event["time"] <= sent_time] + proof_event = candidates[-1] if candidates else None + require(proof_event, "effect command lacks a distinct fresh admission proof event") + proof = proof_event.get("evidence", {}) + states = [] + for entry in relay: + sent, upstream = entry["request"], entry["upstream"] + headers = {key.lower(): value for key, value in sent["headers"].items()} + expected_headers = {key.lower(): value for key, value in body_client.pinned_headers(request).items()} + if (sent["method"] == "GET" and sent["path"] == "/state" and "x-footwork-health-probe" not in headers and + all(headers.get(key) == value for key, value in expected_headers.items()) and + upstream and upstream.get("status") == 200 and entry["response"] and + sent["started_time"] > previous_send and upstream["time"] <= proof_event["time"]): + value = protocol.decode(entry["response"]) + if protocol.sha(protocol.encoded(value)) == proof.get("state_sha256"): + states.append(value) + require(states and any(state.get("state") == "ready" and + all(state["identity"][key] == request[key] == proof.get(key) for key in ("body_id", "adapter_id", "source_sha256", "epoch")) for state in states), + "cached auxiliary health or a different boot authorized command admission") + leases = [entry for entry in relay if entry["request"]["method"] == "POST" and entry["request"]["path"] == "/lease" and + entry["upstream"] and entry["upstream"]["status"] == 200 and entry["response"] and + protocol.decode(entry["response"]).get("lease_id") == request["lease_id"]] + require(len(leases) == 1 and proof_event["time"] <= leases[0]["request"]["started_time"] < record["relay"]["request"]["started_time"] and + protocol.decode(leases[0]["payload"])["expected_epoch"] == request["epoch"], "command lease precedes proof or belongs to another boot") + used.add(proof_event["seq"]) + reports.append({"command_id": token, "proof_event": proof_event["seq"], "passed": True}) + previous_send = sent_time + return reports + + +def native_gap_score(root, boundary, public, transport, calls, sqlite): + token, marker = boundary["command_id"], boundary["gate"] + require(marker and marker["barrier"] == "after_native" and marker == read(root / "body/barrier.json"), "native gap marker changed") + request = transport[token]["request"] + lower = read(root / "body/agent/agent.json") + proposal = indexed(root / "body/agent", lower, "proposals", request["arguments"]["proposal_id"]) + native = read(root / "body/bench/bench.json") + require(marker["pid"] == boundary["process"]["pid"] and marker["command_id"] == token and marker["request_sha256"] == boundary["request_sha256"] and + marker["proposal_id"] == proposal["proposal_id"] and marker["operation_id"] == proposal["operation_id"] and marker["bench_id"] == request["body_id"] and + marker["diagnostic_source_sha256"] == protocol.sha(protocol.read_bytes(BASE / "crash_body.py")) and + native["generation"] == marker["manifest_generation"] and marker["native_generation"] == native["generation"] + 1 and + native["busy"] == {"operation_id": proposal["operation_id"], "kind": "move", "generation": native["generation"]} and + proposal["operation_id"] not in native["operations"] and proposal["proposal_id"] not in lower["results"], "native gap lacks exact accepted, unresolved lower operation proof") + native_root = root / "body/bench" + before = qualify_bench.native_score(native_root, native["generation"], native["state_sha256"], bench.HOME_ZONE) + after = qualify_bench.native_score(native_root, marker["native_generation"], marker["native_state_sha256"], bench.HOME_ZONE) + moved = max(math.dist(before["block_xyz"], after["block_xyz"]), math.dist(before["pusher_xyz"], after["pusher_xyz"])) + require(marker["native_steps"] > 0 and after["native_time_s"] == marker["native_time_s"] > before["native_time_s"] and moved > 1e-6 and + after["external_force_cleared"] and calls[-1]["native_gap"] and calls[-1]["action"] == "move" and + sum(call["native_gap"] for call in calls) == 1 and native["generation"] == 0 and not native["operations"] and + len(public["jobs"]) == 1 and public["jobs"][0]["status"] == "unknown" and public["runner_status"] == "blocked" and + public["recovery"]["command_id"] == token and public["recovery"]["state"] in {"uncertain", "blocked"} and sqlite["count"] == 0 and + all(j["phase"] != "completed" for j in native["jobs"].values()), "missing receipt gained completion or lacks physical progress evidence") + return {"passed": True, "safely_blocked": True, "recovered": False, "marker": marker, + "before": before, "after": after, "native_displacement_m": moved, "reason": "Advanced native state without indexed terminal proof remains unknown"} + + +def completed_job_score(root, public, identity, sqlite): + native_root = root / "body/bench" + native = bench.Bench(native_root).status() + require(len(public["jobs"]) == sqlite["count"] == 1 and native["state"] == "ready" and native["active_job"] is None, "known case did not complete exactly one reusable cycle") + item = public["jobs"][0] + job = native["jobs"][item["job_id"]] + measurement, reset = job["measurement_receipt"], job["reset_receipt"] + stage = qualify_bench.native_score(native_root, measurement["generation"], measurement["state_sha256"], job["target_zone"]) + home = qualify_bench.native_score(native_root, reset["generation"], reset["state_sha256"], bench.HOME_ZONE) + initial = qualify_bench.native_score(native_root, 0, protocol.sha(protocol.read_bytes(bench.body.state_path(native_root, 0))), bench.HOME_ZONE) + operations = [protocol.decode(protocol.read_bytes(native_root / "operations" / (protocol.sha(token.encode()) + ".json"))) for token in read(native_root / "bench.json")["operations"]] + moves = sorted((record for record in operations if record["receipt"].get("kind") == "move"), key=lambda record: record["receipt"]["generation"]) + reset_moves = [record for record in moves if record["request"]["observation"]["phase"] == "resetting" and + record["receipt"]["generation"] > job["reset_started_generation"]] + require([record["receipt"]["generation"] for record in moves] == list(range(1, native["generation"] + 1)) and + sum(record["receipt"]["native_steps"] for record in reset_moves) == reset["reset_native_steps"] and + sum(record["receipt"]["pusher_block_contact_samples"] for record in reset_moves) == reset["reset_contact_samples"], + "native generation/contact reset proof is incomplete or not continuous") + require(item["status"] == job["phase"] == "completed" and sqlite["rows"][0]["job_id"] == item["job_id"] and sqlite["rows"][0]["receipt"] == measurement and + item["measurement"] == agent_bench.public_response(measurement["measurement"]) and item["reset"] == agent_bench.public_response(reset) and + stage["block_center_in_zone"] and home["block_center_in_zone"] and home["pusher_home"] and stage["external_force_cleared"] and home["external_force_cleared"] and + home["native_time_s"] > stage["native_time_s"] > initial["native_time_s"] and reset["reset_contact_samples"] > 0 and reset["reset_native_steps"] > 0 and + reset["bench_id"] == measurement["bench_id"] == identity["body_id"] == native["bench_id"] and + job["started_generation"] == 0 and job["started_native_time_s"] == initial["native_time_s"], "completed UI lacks independent measurement/native/reset continuity proof") + return {"passed": True, "job_id": item["job_id"], "measurement_state": stage, "reset_state": home, + "measurement_receipt": measurement, "reset_receipt": reset, "model_calls": item["call_count"]} + + +def measurement_score(native_root, native_gap=False): + database = native_root / "measurements.sqlite" + if native_gap and not database.exists(): + require(not database.is_symlink(), "absent gap database cannot be a dangling symlink") + return {"role": "independent_read_only_sqlite_scorer", "count": 0, "rows": [], + "database_created": False, "reason": "Native gap occurred before the first computer measurement"} + return qualify_bench.sqlite_score(native_root) + + +def ui_score(root, case, boundary, public, client): + steps = {step["name"]: step for step in read(root / "ui-ledger.json")["steps"]} + names = {"crash-offline", "crash-restored"} | ({"crash-blocked", "crash-still-blocked"} if case == "body-native-gap" else + {"crash-recovered", "crash-resumed", "crash-completed"}) + require(names <= steps.keys(), "actual browser crash/recovery stages are missing") + for name in names: + step = steps[name] + state = step["state"] + require(step["visible_text"] and step["browser_action"], "browser evidence is empty") + if name == "crash-offline" and case.startswith("host"): + require(state is None and step.get("offline") is True and + any(phrase in step["visible_text"].casefold() for phrase in ("disconnected", "failed to fetch")), + "dead host browser state was falsely presented as live") + before = read(root / "snapshots" / boundary["pre_snapshot"]) + selected = {key: before["public"][key] for key in ("app_id", "runner_status", "jobs", "connection", "recovery", + "model_request_pending", "active_job_id", "current_frame")} + offline_path = root / "snapshots" / boundary["offline_snapshot"] + require(step["cached_public"] == selected and step["pins"] == {"app_id": public["app_id"], "body_id": public["connection"]["body_id"]} and + step["snapshot_provenance"] == {"name": boundary["offline_snapshot"], "sha256": protocol.sha(protocol.read_bytes(offline_path))}, + "offline browser cache has another provenance") + else: + require(state and state["app_id"] == public["app_id"] and state["connection"]["body_id"] == public["connection"]["body_id"], + "browser state belongs to another application") + token = boundary["command_id"] + restored = steps["crash-restored"]["state"] + require(restored["runner_status"] == "blocked" and restored["recovery"]["command_id"] == token and + "uncertain" in steps["crash-restored"]["visible_text"].casefold(), "browser did not show the exact pending crash result") + count = steps["crash-restored"]["client_commands"] + if case == "body-native-gap": + for name in ("crash-blocked", "crash-still-blocked"): + state = steps[name]["state"] + require(state["runner_status"] == "blocked" and state["recovery"]["command_id"] == token and + steps[name]["client_commands"] == count and "uncertain" in steps[name]["visible_text"].casefold(), "missing proof was released by browser controls") + require(len(client["commands"]) == count, "native gap admitted a successor command") + else: + recovered, resumed, completed = (steps[name] for name in ("crash-recovered", "crash-resumed", "crash-completed")) + require(recovered["state"]["runner_status"] == "paused" and recovered["state"]["recovery"]["state"] == "recovered" and + recovered["state"]["recovery"]["command_id"] == token and recovered["client_commands"] == count and + "receipt recovered" in recovered["visible_text"].casefold() and resumed["state"]["runner_status"] == "running" and + "running" in resumed["visible_text"].casefold() and completed["state"]["jobs"] == public["jobs"] and + completed["client_commands"] == len(client["commands"]) and "completed" in completed["visible_text"].casefold(), "browser recovery did not remain paused until explicit Resume") + return {"passed": True, "required_names": sorted(names)} + + +def score(root): + root = protocol.directory(root) + frozen, ready = read(root / "source-freeze.json"), read(root / "ready.json") + case = ready["case"] + require(case in CASES and frozen["files"] and all(protocol.sha(protocol.read_bytes(REPO / path)) == digest for path, digest in frozen["files"].items()), "measured source changed or case is invalid") + process = read(root / "process-ledger.json") + require(process["owned_processes_exited"] is True and not process.get("error") and not process.get("cleanup_error"), "owned processes have not exited before native scoring") + public, identity = read(root / "final-public.json"), read(root / "identity.json") + crash = read(root / "crash-ledger.json") + require(crash["case"] == case and len(crash["boundaries"]) == 1 and crash["boundaries"][0]["case"] == case, "case requires one exact crash boundary") + boundary = crash["boundaries"][0] + for item in process["processes"]: + require(item["exit_observed"] is True and item["returncode"] == (-9 if item["pid"] == boundary["process"]["pid"] else 0), "owned actor exited unexpectedly or was not reaped") + cutoff = public["activity"][-1]["seq"] + events = scoped(root, "operator/events", cutoff) + require(events and events[-1]["seq"] == cutoff and public["app_id"] == ready["app_id"] and public["connection"]["body_id"] == identity["body_id"] == ready["body_id"], "final scoring scope/identity changed") + client = body_client.read_journal(root / "operator/client/client.json") + require(len(client["commands"]) <= 240 and public["usage"]["calls"] <= 32, "declared single-case budget exceeded") + relay = relay_records(root) + link_events = [read(path) for path in sorted((root / "operator/link/events").glob("*.json"))] + gap_token = boundary["command_id"] if case == "body-native-gap" else None + transport = transport_score(root, identity, client, link_events, relay, gap_token) + crash_result = boundary_score(root, boundary, identity, transport, events) + calls = calls_score(root, public, transport, relay, gap_token) + admissions = fresh_admissions(transport, relay, events) + sqlite = measurement_score(root / "body/bench", native_gap=bool(gap_token)) + native = native_gap_score(root, boundary, public, transport, calls, sqlite) if gap_token else completed_job_score(root, public, identity, sqlite) + ui = ui_score(root, case, boundary, public, client) + original = transport[boundary["command_id"]] + if not gap_token: + reconciliations = [read(path) for path in (root / "operator/calls").glob("*/reconciliation.json")] + bound_receipts = [value for value in reconciliations if value["command_id"] == boundary["command_id"]] + require(original["result"]["state"] == "completed" and len(bound_receipts) == 1 and bound_receipts[0] == original["result"], + "known crash has no exact historical receipt application") + restored = read(root / "snapshots" / boundary["restored_snapshot"]) + resumes = [event for event in events if event["kind"] == "resume" and event["seq"] == crash_result["resume_sequence"]] + before_resume = [record for record in transport.values() if record["relay"]["request"]["started_time"] < resumes[0]["time"]] + require(len(before_resume) == len(restored["client"]["commands"]), "successor command preceded receipt and explicit Resume") + later = sorted((record for record in transport.values() if record["relay"]["request"]["started_time"] >= resumes[0]["time"]), key=lambda record: record["received"]["sequence"]) + next_call = restored["host"]["usage"]["calls"] + require(later and later[0]["request"]["operation"] == "observe" and next_call < len(calls) and + calls[next_call]["challenge_id"] == later[0]["result"]["result"]["challenge_id"] and + calls[next_call]["camera_generation"] > calls[next_call - 1]["camera_generation"], + "Resume did not bind its first new decision to fresh post-crash TCP evidence") + cleanup = read(root / "relay/cleanup.json") + require(cleanup["server_thread_exited"] and cleanup["active_workers"] == 0, "owned relay workers survived scoring") + result = {"version": 1, "passed": True, "case": case, "source_commit": frozen["commit"], "source_unchanged": True, + "event_cutoff": cutoff, "body_id": identity["body_id"], "completed_cycles": 0 if gap_token else 1, + "safely_blocked": bool(gap_token), "crash": crash_result, "native": native, "calls": calls, + "admissions": admissions, "transport_command_count": len(transport), "single_wire_post_every_command": True, + "browser": ui, "sqlite": sqlite, "owned_processes_exited": True, + "scope": "selected process SIGKILL boundaries; actual image-only Codex browser; loopback simulation; no arbitrary power-loss guarantee"} + protocol.create_file(root / "hard-crash-score.json", protocol.encoded(result)) + return {key: result[key] for key in ("passed", "case", "completed_cycles", "safely_blocked", "source_unchanged", "single_wire_post_every_command")} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True) + print(json.dumps(score(parser.parse_args().root), sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/score_workbench_health.py b/tools/physical-workcell/score_workbench_health.py new file mode 100644 index 0000000..785a3da --- /dev/null +++ b/tools/physical-workcell/score_workbench_health.py @@ -0,0 +1,150 @@ +"""Independent health-delay qualification layered on unchanged SIM10 scoring.""" +import argparse +import json +from pathlib import Path + +import body_client +import order_protocol as protocol +import score_network_workbench as previous + +read = previous.read + + +def headers(record): + return {key.lower(): value for key, value in record["headers"].items()} + + +def relay_records(root): + records, valid = [], True + for directory in sorted((root / "health-relay" / "requests").iterdir()): + request = read(directory / "request.json") + result = read(directory / "result.json") + payload = protocol.read_bytes(directory / "request.bin") + upstream, content = None, None + if (directory / "upstream.json").exists(): + upstream = read(directory / "upstream.json") + content = protocol.read_bytes(directory / "response.bin", maximum=body_client.MAX_IMAGE) + valid &= (directory.name == f"{request['sequence']:06d}" and + request["payload_sha256"] == protocol.sha(payload) and request["upstream_sends"] == 1 and + upstream is not None and upstream["response_sha256"] == protocol.sha(content)) + if request["injected_delay_s"]: + valid &= request["tagged_health"] and request["method"] == "GET" and request["path"] in ("/identity", "/state") + elif request["method"] == "POST": + valid &= not request["tagged_health"] + records.append({"request": request, "result": result, "upstream": upstream, "content": content, + "payload": payload}) + valid &= bool(records) and [record["request"]["sequence"] for record in records] == list(range(1, len(records) + 1)) + return records, bool(valid) + + +def fresh_admissions(records, events, identity): + proofs = [event for event in events if event["kind"] == "admission_proof"] + commands = [record for record in records if record["request"]["method"] == "POST" and record["request"]["path"] == "/commands"] + leases = [record for record in records if record["request"]["method"] == "POST" and record["request"]["path"] == "/lease"] + states = [record for record in records if record["request"]["method"] == "GET" and record["request"]["path"] == "/state" and not record["request"]["tagged_health"]] + reports, used = [], set() + previous_command_finished = float("-inf") + for command in commands: + request, payload = command["request"], protocol.decode(command["payload"]) + candidates = [lease for lease in leases if lease["upstream"] and lease["upstream"]["http_status"] == 200 and + lease["upstream"]["finished_monotonic"] <= request["started_monotonic"] and + protocol.decode(lease["content"]).get("lease_id") == payload["lease_id"]] + lease = candidates[-1] if candidates else None + candidate_proofs = [event for event in proofs if event["time"] <= lease["request"]["started_wall"] and + event["outcome"] == payload["operation"]] if lease else [] + event = candidate_proofs[-1] if candidate_proofs else None + proof = event.get("evidence", {}) if event else {} + state_reads = [record for record in states if record["upstream"] and record["upstream"]["http_status"] == 200 and + record["upstream"]["wall"] <= event["time"] and record["request"]["started_monotonic"] > previous_command_finished and + protocol.sha(protocol.encoded(protocol.decode(record["content"]))) == proof.get("state_sha256")] if event else [] + state = max(state_reads, key=lambda record: record["request"]["started_monotonic"]) if state_reads else None + remote = protocol.decode(state["content"]) if state else {} + expected_headers = {key.lower(): value for key, value in body_client.pinned_headers(identity).items()} + ready = bool(lease and event and state and event["seq"] not in used and remote.get("state") == "ready" and + remote.get("identity") == identity and proof.get("epoch") == identity["epoch"] and + all(proof.get(key) == identity[key] for key in ("body_id", "adapter_id", "source_sha256")) and + all(headers(state["request"]).get(key) == value for key, value in expected_headers.items()) and + protocol.decode(lease["payload"])["expected_epoch"] == identity["epoch"] == payload["epoch"] and + state["request"]["started_monotonic"] <= state["upstream"]["finished_monotonic"] <= lease["request"]["started_monotonic"] < request["started_monotonic"]) + if event: + used.add(event["seq"]) + reports.append({"command_id": payload["command_id"], "operation": payload["operation"], "passed": ready, + "proof_event": event["seq"] if event else None, + "state_read_sequence": state["request"]["sequence"] if state else None, + "lease_sequence": lease["request"]["sequence"] if lease else None, + "command_sequence": request["sequence"]}) + if command["upstream"]: + previous_command_finished = command["upstream"]["finished_monotonic"] + return reports + + +def score(root): + root = protocol.directory(root) + base = previous.score(root) + records, relay_valid = relay_records(root) + identity = read(root / "identity.json") + events = [read(path) for path in sorted((root / "operator" / "events").glob("*.json"))] + admissions = fresh_admissions(records, events, identity) + long_reads = [] + natural_delays = [] + for record in records: + request, upstream, result = record["request"], record["upstream"], record["result"] + if request["tagged_health"] and upstream: + decoded = protocol.decode(record["content"]) + healthy = (decoded == identity if request["path"] == "/identity" else + decoded.get("identity") == identity and decoded.get("state") == "ready") + if healthy and upstream["http_status"] == 200 and upstream["upstream_elapsed_s"] >= .8: + natural_delays.append({"sequence": request["sequence"], "path": request["path"], + "upstream_elapsed_s": upstream["upstream_elapsed_s"]}) + if (request["injected_delay_s"] == 1.2 and upstream["http_status"] == 200 and healthy and + result.get("completed") and result.get("delivered") and not result.get("delay_interrupted") and + result["delay_elapsed_s"] >= 1.2): + long_reads.append({"sequence": request["sequence"], "path": request["path"], + "upstream_elapsed_s": upstream["upstream_elapsed_s"], + "injected_elapsed_s": result["delay_elapsed_s"]}) + ui = read(root / "ui-ledger.json") + steps = {step["name"]: step for step in ui["steps"]} + checking = steps.get("healthy_checking") + checking_ok = bool(checking and checking["state"]["runner_status"] == "running" and + checking["state"]["connection"]["state"] == "checking" and checking["state"]["active_job_id"] and + checking["state"]["recovery"]["state"] == "none" and + ("checking" in checking["visible_text"].casefold() or "waiting" in checking["visible_text"].casefold()) and + any(job["status"] == "running" for job in checking["state"]["jobs"])) + allowed_halt = steps.get("uncertain", {}).get("state", {}).get("recovery", {}).get("reason") + unexpected_halts = [event for event in events if event["kind"] == "halt" and event["outcome"] != allowed_halt] + model_outcomes = [read(path) for path in sorted((root / "operator" / "calls").glob("*/outcome.json"))] + cancelled = [index + 1 for index, outcome in enumerate(model_outcomes) if outcome.get("outcome") == "cancelled"] + cleanup = read(root / "health-relay" / "cleanup.json") + process = read(root / "process-ledger.json") + link_events = [read(path) for path in sorted((root / "operator" / "link" / "events").glob("*.json"))] + closed = next((event for event in reversed(link_events) if event["kind"] == "closed"), None) + # A clean host exit also requires its gateway close() to join the bounded readers. + readers_exited = bool(closed and cleanup["server_thread_exited"] and cleanup["active_workers"] == 0 and + cleanup["peak_workers"] <= cleanup["worker_bound"] == 8 and + process["owned_processes_exited"] and + all(item["exit_observed"] and item["returncode"] == 0 for item in process["processes"])) + readers_exited &= bool(closed and closed.get("health_readers_alive") == 0 and + 0 <= closed.get("health_readers_peak", 3) <= 2) + passed = bool(base["passed"] and relay_valid and len(long_reads) >= 3 and admissions and + all(report["passed"] for report in admissions) and checking_ok and not unexpected_halts and + not cancelled and readers_exited) + result = {"passed": passed, "base_qualification": base, "relay_evidence_valid": relay_valid, + "healthy_injected_long_read_count": len(long_reads), "healthy_injected_long_reads": long_reads[:64], + "genuine_upstream_long_read_count": len(natural_delays), "genuine_upstream_long_reads": natural_delays[:64], + "fresh_admission_proofs": admissions, "fresh_proof_every_rpc": bool(admissions and all(r["passed"] for r in admissions)), + "soft_checking_visible_while_running": checking_ok, "unexpected_halts": unexpected_halts, + "cancelled_model_calls": cancelled, "owned_readers_exited": readers_exited, + "relay_cleanup": cleanup, "scope": "same-Mac loopback simulation; 1.2s diagnostic auxiliary read delay"} + retain = protocol.create_file + retain(root / "health-qualification-score.json", protocol.encoded(result)) + return {key: result[key] for key in ("passed", "fresh_proof_every_rpc", "soft_checking_visible_while_running", "owned_readers_exited")} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True) + print(json.dumps(score(parser.parse_args().root), sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/score_workbench_recovery.py b/tools/physical-workcell/score_workbench_recovery.py new file mode 100644 index 0000000..7d8bd9e --- /dev/null +++ b/tools/physical-workcell/score_workbench_recovery.py @@ -0,0 +1,305 @@ +"""Independent SIM14 replay, interrupted-publication and orphan-planner scorer.""" +import argparse +import json +from pathlib import Path +from urllib.parse import parse_qs, urlsplit + +import agent_bench +import body_client +import codex_policy +import order_protocol as protocol +import score_workbench_crashes as sim13 +from qualify_workbench_recovery import BASE, CASES, REPO, read +from score_network_body import private_fields + +require, indexed = sim13.require, sim13.indexed + +def descendant_score(directory): + intent, startup, closure = [read(directory / name) for name in ("planner-intent.json", "planner-process.json", "planner-closure.json")] + proof, request = read(directory / "planner-descendants.json"), read(directory / "request.json") + require(set(proof) == {"version", "host_pid", "owner", "request_sha256", "guardian_pid", "complete", "observed", "remaining"} and + proof["version"] == 1 and proof["complete"] is True and proof["remaining"] == [] and + all(proof[key] == intent[key] == startup[key] == closure[key] for key in ("host_pid", "owner", "request_sha256")) and + proof["guardian_pid"] == startup["guardian_pid"] == closure["guardian_pid"], + "guardian descendant closure is not complete and bound to original planner") + identities = set() + require(isinstance(proof["observed"], list) and 0 < len(proof["observed"]) <= 64, "guardian descendant inventory missing or unbounded") + for row in proof["observed"]: + require(set(row) == {"pid", "parent", "group", "uid", "started"} and + all(type(row[k]) is int and row[k] > 0 for k in ("pid", "group")) and + type(row["uid"]) is int and row["uid"] >= 0 and type(row["parent"]) is int and row["parent"] >= 0 and + isinstance(row["started"], str) and 1 <= len(row["started"]) <= 64, + "guardian observed descendant identity is invalid") + key = (row["pid"], row["uid"], row["started"]) + require(key not in identities, "guardian observed descendant identity repeated") + identities.add(key) + require(any(row["pid"] == startup["planner_pid"] for row in proof["observed"]), "actual planner is missing from descendant proof") + flags = request["flags"] + require(any(flags[n:n + 2] == ["--disable", "shell_snapshot"] for n in range(len(flags) - 1)), + "actual planner did not disable detached shell snapshot startup") + return proof + + +def calls_score(root, public, transport, relay, gap_token, cancelled_directory=None): + agent_root = root / "body/agent" + manifest = read(agent_root / "agent.json") + calls, proposal_ids, native_operations = [], set(), set() + native_manifest = read(root / "body/bench/bench.json") + for directory in sorted((root / "operator/calls").iterdir()): + reservation = read(directory / "reservation.json") + challenge = reservation["challenge"] + image = challenge["observation"]["image_sha256"] + core = indexed(agent_root, manifest, "challenges", challenge["challenge_id"]) + require({**agent_bench.public_response(core), "image_path": challenge["image_path"]} == challenge and + protocol.sha(protocol.encoded(challenge["observation"])) == challenge["observation_sha256"], "model reservation differs from indexed camera challenge") + image_path = Path(challenge["image_path"]) + require(image_path.parent == root / "operator/client/images" and + protocol.sha(protocol.read_bytes(image_path, maximum=body_client.MAX_IMAGE)) == image and + protocol.sha(protocol.read_bytes(directory / "input.png", maximum=body_client.MAX_IMAGE)) == image, + "model did not receive its exact downloaded image") + image_reads = [r for r in relay if r["request"]["method"] == "GET" and r["request"]["path"] == "/images/" + challenge["challenge_id"] and + r["upstream"] and r["upstream"].get("status") == 200 and r["response"] and protocol.sha(r["response"]) == image] + require(image_reads, "model image lacks actual TCP image reply") + prompt_bytes = protocol.read_bytes(directory / "prompt.txt") + prompt = json.loads(prompt_bytes.decode().split("\n", 1)[1]) + packet = challenge["observation"] + expected = agent_bench.public_response({"current": {"job_id": packet["job_id"], "phase": packet["phase"], + "generation": packet["generation"], "calibration": packet["calibration"], "task": challenge["task"]}, "recent_actions": reservation["history"][-12:]}) + require(prompt == expected and not private_fields(reservation) and not private_fields(prompt), "model received hidden pose or different task/history") + descendant_score(directory) + if directory.name == cancelled_directory: + request, intent = read(directory / "request.json"), read(directory / "planner-intent.json") + live, closure = read(directory / "planner-process.json"), read(directory / "planner-closure.json") + request_sha = protocol.sha(prompt_bytes + protocol.read_bytes(directory / "input.png", maximum=body_client.MAX_IMAGE) + + protocol.read_bytes(directory / "instructions.md") + protocol.read_bytes(directory / "schema.json")) + trace = protocol.read_bytes(directory / "events.jsonl", maximum=codex_policy.MAX_TRACE_BYTES) + require(request["request_sha256"] == intent["request_sha256"] == live["request_sha256"] == closure["request_sha256"] == request_sha and + request["image_sha256"] == image and request["tools_enabled"] is False and + intent["owner"] == live["owner"] == closure["owner"] == reservation["planner_owner"] and + closure["process_exit_observed"] is True and closure["group_closed"] is True and + closure["guardian_pid"] == live["guardian_pid"] and closure["reason"] == "host liveness pipe closed" and + request["cli_version"] == codex_policy.EXPECTED_CLI and + protocol.read_bytes(directory / "instructions.md") == protocol.read_bytes(BASE / "codex-policy.md") and + protocol.read_bytes(directory / "schema.json") == protocol.read_bytes(BASE / "codex-action.schema.json") and + b'"thread.started"' in trace, + "cancelled actual model reservation lacks exact guardian closure and image request proof") + codex_policy.usage_from_events(trace) # Also rejects any tool-bearing trace item. + require(not any((directory / name).exists() for name in ("decision.json", "proposal.json", "dispatch.json")), + "cancelled old planner result reached proposal or dispatch") + calls.append({"job_id": reservation["job_id"], "call": reservation["call"], + "cancelled": True, "image_sha256": image, "challenge_id": challenge["challenge_id"], + "camera_generation": packet["generation"], "model_pid": live["planner_pid"], "passed": True}) + continue + model, decision = read(directory / "outcome.json"), read(directory / "decision.json") + chosen = codex_policy.validate_action(read(directory / "result.json")) + request = read(directory / "request.json") + request_sha = protocol.sha(prompt_bytes + protocol.read_bytes(directory / "input.png", maximum=body_client.MAX_IMAGE) + + protocol.read_bytes(directory / "instructions.md") + protocol.read_bytes(directory / "schema.json")) + trace = protocol.read_bytes(directory / "events.jsonl", maximum=codex_policy.MAX_TRACE_BYTES) + trace_events = [json.loads(line) for line in trace.decode().splitlines() if line.strip()] + messages = [event["item"]["text"] for event in trace_events if event.get("type") == "item.completed" and + event.get("item", {}).get("type") == "agent_message"] + require(model["provider"] == "codex_cli" and model.get("outcome") == "decision" and model["process_exit_observed"] is True and + model["exit_code"] == 0 and decision["action"] == chosen and decision["metadata"] == model and + messages and protocol.decode(messages[-1].encode()) == chosen and any(event.get("type") == "turn.completed" for event in trace_events) and + model["cli_version"] == request["cli_version"] == codex_policy.EXPECTED_CLI and + protocol.read_bytes(directory / "instructions.md") == protocol.read_bytes(BASE / "codex-policy.md") and + protocol.read_bytes(directory / "schema.json") == protocol.read_bytes(BASE / "codex-action.schema.json") and + request.get("tools_enabled") is False and request["request_sha256"] == model["request_sha256"] == request_sha and + request["image_sha256"] == model["image_sha256"] == image and + codex_policy.usage_from_events(trace) == model["usage"], + "actual image-only Codex decision or process proof is incomplete") + proposal = read(directory / "proposal.json") + lower = indexed(agent_root, manifest, "proposals", proposal["proposal_id"]) + require(lower == proposal and proposal["challenge_id"] == challenge["challenge_id"] and + proposal["observation_sha256"] == challenge["observation_sha256"] and + all(proposal[key] == chosen[key] for key in ("action", "intent", "target_x", "target_y")), "AI decision/proposal/challenge binding differs") + require(proposal["proposal_id"] not in proposal_ids, "two model calls share a proposal") + proposal_ids.add(proposal["proposal_id"]) + dispatches = [(token, record) for token, record in transport.items() if record["request"]["operation"] == "dispatch" and + record["request"]["arguments"]["proposal_id"] == proposal["proposal_id"]] + require(len(dispatches) == 1, "model proposal has zero or multiple accepted dispatch commands") + token, dispatch = dispatches[0] + intent = indexed(agent_root, manifest, "intents", proposal["proposal_id"]) + require(intent["proposal_sha256"] == manifest["proposals"][proposal["proposal_id"]] and + intent["challenge_sha256"] == manifest["challenges"][challenge["challenge_id"]] and intent["operation_id"] == proposal["operation_id"], "lower dispatch intent differs") + if token == gap_token: + require(not (directory / "dispatch.json").exists() and proposal["proposal_id"] not in manifest["results"] and + dispatch["result"] is None, "missing native proof gained a terminal dispatch") + else: + result = indexed(agent_root, manifest, "results", proposal["proposal_id"]) + require(result["proposal_id"] == proposal["proposal_id"] and result["outcome"] in {"executed", "refused"} and + agent_bench.public_response(result) == read(directory / "dispatch.json") == dispatch["result"]["result"], "dispatch lacks exact lower/native/host receipt") + if result["outcome"] == "executed" and chosen["action"] != "begin_reset": + operation = proposal["operation_id"] + operation_bytes = protocol.read_bytes(root / "body/bench/operations" / (protocol.sha(operation.encode()) + ".json")) + native_record = protocol.decode(operation_bytes) + require(native_manifest["operations"].get(operation) == protocol.sha(operation_bytes) and + native_record["request"]["kind"] == chosen["action"] and native_record["request"]["observation"] == challenge["observation"] and + native_record["receipt"] == result["receipt"] and native_record["receipt"]["operation_id"] == operation, + "executed AI dispatch lacks its exact indexed native operation") + if chosen["action"] == "move": + require(native_record["request"]["target_x"] == chosen["target_x"] and native_record["request"]["target_y"] == chosen["target_y"] and + native_record["receipt"]["native_steps"] > 0 and native_record["receipt"]["max_axis_actuator_force_n"] <= 4.00000001, + "native move differs from its bounded model target") + native_operations.add(operation) + calls.append({"job_id": reservation["job_id"], "call": reservation["call"], "action": chosen["action"], + "command_id": token, "proposal_id": proposal["proposal_id"], "image_sha256": image, + "challenge_id": challenge["challenge_id"], "camera_generation": packet["generation"], + "native_gap": token == gap_token, "model_pid": model["pid"], "passed": True}) + require(calls and len(calls) == public["usage"]["calls"] == sum(j["call_count"] for j in public["jobs"]) and + {(c["job_id"], c["call"]) for c in calls} == {(j["job_id"], n) for j in public["jobs"] for n in range(1, j["call_count"] + 1)}, + "reserved model calls are missing, duplicated or outside final scope") + require(proposal_ids == {record["request"]["arguments"]["proposal_id"] for record in transport.values() if record["request"]["operation"] == "dispatch"}, + "accepted dispatch escaped actual image-model accounting") + require(native_operations == set(native_manifest["operations"]), "native effect escaped actual model-dispatch accounting") + return calls + + +def boundary_score(root, boundary, transport, events): + before = read(root / "snapshots" / boundary["pre_snapshot"]) + restored = read(root / "snapshots" / boundary["restored_snapshot"]) + gate = boundary["gate"] + require(boundary["process"]["returncode"] == -9 and boundary["process"]["signal_target"] == "host_pid_only" and + gate["pid"] == boundary["process"]["pid"], "crash was not exact owned host-only SIGKILL") + require(before["client"]["commands"] == restored["client"]["commands"] and + before["posts"] == restored["posts"] and before["host"]["app_id"] == restored["host"]["app_id"] and + before["body_identity"] == restored["body_identity"] and not restored["public"]["model_request_pending"] and + restored["public"]["runner_status"] != "running", "restart changed commands/body or automatically resumed planning") + resumes = [e for e in events if e["kind"] == "resume" and e["seq"] > restored["host"]["sequence"]] + require(resumes, "explicit post-restart Resume is absent") + resumed = resumes[0] + if boundary["case"] == "host-publication": + token = boundary["command_id"] + event_path = root / "operator/events" / f"{gate['event_sequence']:06d}.json" + orphan = read(event_path) + require(protocol.sha(protocol.read_bytes(event_path)) == gate["event_sha256"] and + orphan["kind"] == "rpc_applied" and orphan["outcome"] == "dispatch: " + token and + gate["event_sequence"] == gate["manifest_sequence"] + 1 == before["host"]["sequence"] + 1 and + restored["host"]["sequence"] >= gate["event_sequence"], "orphan event was replaced, lost or not indexed on restart") + require(restored["public"]["recovery"]["command_id"] == token and transport[token]["result"]["state"] == "completed", + "restart lost exact original command uncertainty") + reconciled = [e for e in events if e["kind"] == "reconciled" and e["outcome"] == token and + restored["host"]["sequence"] < e["seq"] < resumed["seq"]] + exact = [read(p) for p in (root / "operator/calls").glob("*/reconciliation.json") if read(p)["command_id"] == token] + reads = [r for r in sim13.relay_records(root) if r["request"]["method"] == "GET" and + urlsplit(r["request"]["path"]).path == "/commands/" + token and + restored["time"] <= r["request"]["started_time"] < resumed["time"]] + expected_query = {key: [str(transport[token]["request"][key])] for key in ("client_id", "lease_id", "epoch")} + expected_query["request_sha256"] = [protocol.sha(protocol.encoded(transport[token]["request"]))] + require(reads and all(parse_qs(urlsplit(r["request"]["path"]).query) == expected_query for r in reads), + "historical receipt lookup differs from original owner, lease, epoch or digest") + require(len(exact) == 1 and exact[0] == transport[token]["result"] and reconciled and reads, + "publication recovery did not read and apply exact receipt before Resume") + else: + directory = root / "operator/calls" / gate["call_directory"] + closure = read(directory / "planner-closure.json") + require(closure["owner"] == gate["planner"]["owner"] and closure["guardian_pid"] == gate["planner"]["guardian_pid"] and + closure["process_exit_observed"] is True and closure["group_closed"] is True and + not restored["public"]["recovery"]["command_id"], "orphan planner was not retired before restart") + retirement = read(root / "planner-retirement.json") + require(retirement["old_processes_absent"] is True and retirement["every_captured_group_absent"] is True and + retirement["captured_group_current_rows"] == [] and + retirement["captured_descendants"] == gate["captured_descendants"] == boundary["process"]["children_before"] and + retirement["captured_groups"] == sorted({row["group"] for row in gate["captured_descendants"]}) and + retirement["same_group_current_rows"] == [] and retirement["old_identities"] == gate["process_identities"] and + retirement["old_owner"] == closure["owner"] == before["host"]["planner_owner"] and + retirement["new_owner"] == restored["host"]["planner_owner"] != retirement["old_owner"] and + retirement["time"] <= restored["time"] and retirement["old_call_files"] and + all(protocol.sha(protocol.read_bytes(directory / name, maximum=codex_policy.MAX_TRACE_BYTES)) == digest + for name, digest in retirement["old_call_files"].items()), + "old planner identities/group not independently absent, incarnation unchanged or retired call changed") + before_resume = [r for r in transport.values() if r["relay"]["request"]["started_time"] < resumed["time"]] + require(len(before_resume) == len(restored["client"]["commands"]), "a successor action preceded explicit Resume") + later = sorted((r for r in transport.values() if r["relay"]["request"]["started_time"] >= resumed["time"]), + key=lambda r: r["received"]["sequence"]) + require(later and later[0]["request"]["operation"] == "observe", "Resume did not first acquire fresh camera evidence") + return {"passed": True, "original_commands_preserved": True, "resume_sequence": resumed["seq"], + "fresh_challenge": later[0]["result"]["result"]["challenge_id"], "host_pid_only": True} + + +def ui_score(root, boundary): + steps = read(root / "ui-ledger.json")["steps"] + named = {s["name"]: s for s in steps} + required = ("crash-offline", "restored", "resumed", "completed") + require(all(name in named for name in required) and len(named) == len(steps), "browser recovery evidence steps are missing or duplicated") + offline, restored_step, resumed, completed = [named[name] for name in required] + require(offline["offline"] and offline["state"] is None and "disconnected" in offline["visible_text"].lower(), + "browser has no explicit visible offline evidence") + restored = read(root / "snapshots" / boundary["restored_snapshot"]) + require(not restored_step["offline"] and restored_step["time"] >= restored["time"] and + restored_step["state"]["runner_status"] != "running" and not restored_step["state"]["model_request_pending"] and + any(word in restored_step["visible_text"].lower() for word in ("paused", "uncertain")), "browser did not show restored pause or uncertainty") + require(not resumed["offline"] and resumed["state"]["runner_status"] == "running" and + "running" in resumed["visible_text"].lower() and restored_step["time"] < resumed["time"] < completed["time"], + "browser did not show ordered explicit resumed running state") + require(not completed["offline"] and any(j["status"] == "completed" for j in completed["state"]["jobs"]) and + "completed" in completed["visible_text"].lower(), "browser did not show completed measurement/reset") + if boundary["case"] == "host-publication": + require(restored_step["state"]["recovery"]["command_id"] == boundary["command_id"] and "recovered" in named, + "browser did not show original command uncertainty and receipt recovery") + recovered = named["recovered"] + require(not recovered["offline"] and recovered["state"]["runner_status"] == "paused" and + recovered["state"]["recovery"]["state"] == "recovered" and + recovered["state"]["recovery"]["command_id"] == boundary["command_id"] and not recovered["pending_command_ids"] and + "receipt recovered" in recovered["visible_text"].lower() and + recovered["client_commands"] == restored_step["client_commands"] and restored_step["time"] < recovered["time"] < resumed["time"], + "receipt recovery did not stay paused without new commands") + return {"passed": True, "steps": [s["name"] for s in steps], "offline_then_paused": True} + + +def score(root): + root = protocol.directory(root) + frozen, ready = read(root / "source-freeze.json"), read(root / "ready.json") + case = ready["case"] + require(case in CASES and frozen["files"] and all(protocol.sha(protocol.read_bytes(REPO / path)) == digest + for path, digest in frozen["files"].items()), "case invalid or frozen measured source changed") + process = read(root / "process-ledger.json") + boundary = read(root / "crash-ledger.json")["boundaries"][0] + require(process["owned_processes_exited"] and not process.get("error") and not process.get("cleanup_error"), "owned actors not cleaned up") + for actor in process["processes"]: + require(actor["exit_observed"] and actor["returncode"] == (-9 if actor["pid"] == boundary["process"]["pid"] else 0), + "an owned actor exited unexpectedly") + public, identity = read(root / "final-public.json"), read(root / "identity.json") + cutoff = public["activity"][-1]["seq"] + events = sim13.scoped(root, "operator/events", cutoff) + require([e["seq"] for e in events] == list(range(1, cutoff + 1)) and public["app_id"] == ready["app_id"] and + public["connection"]["body_id"] == identity["body_id"] == ready["body_id"], "journal is not contiguous or identity changed") + client = body_client.read_journal(root / "operator/client/client.json") + require(len(client["commands"]) <= 240 and public["usage"]["calls"] <= 32, "declared case budget exceeded") + relay = sim13.relay_records(root) + link_events = [read(p) for p in sorted((root / "operator/link/events").glob("*.json"))] + transport = sim13.transport_score(root, identity, client, link_events, relay, None) + crash = boundary_score(root, boundary, transport, events) + cancelled = boundary["gate"]["call_directory"] if case == "host-planning" else None + calls = calls_score(root, public, transport, relay, None, cancelled) + restored = read(root / "snapshots" / boundary["restored_snapshot"]) + fresh = calls[restored["host"]["usage"]["calls"]] + require(not fresh.get("cancelled") and fresh["challenge_id"] == crash["fresh_challenge"] and + fresh["challenge_id"] != calls[restored["host"]["usage"]["calls"] - 1]["challenge_id"] and + fresh["camera_generation"] >= calls[restored["host"]["usage"]["calls"] - 1]["camera_generation"], + "first resumed model did not use a fresh post-crash camera generation") + admissions = sim13.fresh_admissions(transport, relay, events) + sqlite = sim13.measurement_score(root / "body/bench") + native = sim13.completed_job_score(root, public, identity, sqlite) + ui = ui_score(root, boundary) + cleanup = read(root / "relay/cleanup.json") + require(cleanup["server_thread_exited"] and cleanup["active_workers"] == 0, "relay workers survived scoring") + result = {"version": 1, "passed": True, "case": case, "source_commit": frozen["commit"], "event_cutoff": cutoff, + "source_unchanged": True, "completed_cycles": 1, "single_wire_post_every_command": True, + "transport_command_count": len(transport), "calls": calls, "cancelled_calls": sum(bool(c.get("cancelled")) for c in calls), + "crash": crash, "browser": ui, "admissions": admissions, "native": native, "sqlite": sqlite, + "owned_processes_exited": True, "scope": "selected event publication and active-planning host SIGKILL; loopback simulation"} + protocol.create_file(root / "recovery-score.json", protocol.encoded(result)) + return {k: result[k] for k in ("passed", "case", "completed_cycles", "single_wire_post_every_command", "cancelled_calls")} + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", type=Path, required=True) + args = parser.parse_args() + print(json.dumps(score(args.root))) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/score_workbench_restarts.py b/tools/physical-workcell/score_workbench_restarts.py new file mode 100644 index 0000000..1233943 --- /dev/null +++ b/tools/physical-workcell/score_workbench_restarts.py @@ -0,0 +1,153 @@ +"""Independent post-stop restart proof plus the unchanged SIM10 native scorer.""" +import argparse +import json +from pathlib import Path + +import body_client +import order_protocol as protocol +import score_network_workbench as previous + + +def read(path): + content = protocol.read_bytes(path, maximum=1024 * 1024) + # Local aggregate ledgers may exceed the wire JSON limit. Reject duplicate keys. + def unique(pairs): + result = {} + for key, value in pairs: + if key in result: + raise protocol.Refusal("duplicate scorer ledger key") + result[key] = value + return result + return json.loads(content, object_pairs_hook=unique, + parse_constant=lambda _: (_ for _ in ()).throw(protocol.Refusal("nonfinite scorer ledger"))) + + +def command_identity(snapshot): + return {token: {key: record[key] for key in ("request_sha256", "send_count", "operation", "state")} + for token, record in snapshot["client"]["commands"].items()} + + +def score(root): + root = protocol.directory(root) + processes = read(root / "process-ledger.json") + if not processes.get("owned_processes_exited"): + raise protocol.Refusal("independent native scoring requires all owned trial processes stopped") + legacy = previous.score(root) + ledger = read(root / "restart-ledger.json") + identity, initial = read(root / "identity.json"), read(root / "initial-public.json") + host_events = [read(path) for path in sorted((root / "operator" / "events").glob("*.json"))] + link_events = [read(path) for path in sorted((root / "operator" / "link" / "events").glob("*.json"))] + ui = read(root / "ui-ledger.json") + visible = {} + for role in ("workbench", "body"): + for predicate in ("planning", "uncertain"): + for phase in ("offline", "restored"): + name = f"{role}-{predicate}-{phase}" + text = protocol.read_bytes(root / "ui" / (name + ".txt"), maximum=16000).decode("utf-8").casefold() + phrases = (["disconnected"] if role == "workbench" else ["body unavailable"]) if phase == "offline" else ( + ["paused"] if predicate == "planning" else ["uncertain", "check receipt"]) + visible[name] = all(phrase in text for phrase in phrases) + for name, phrase in (("body-uncertain-recovered", "receipt recovered"), ("body-uncertain-resumed", "running")): + visible[name] = phrase in protocol.read_bytes(root / "ui" / (name + ".txt"), maximum=16000).decode("utf-8").casefold() + client = body_client.read_journal(root / "operator" / "client" / "client.json") + requests = {token: read(root / "operator" / "client" / "requests" / (token + ".json")) for token in client["commands"]} + stops = {entry["request"]["sequence"]: entry for entry in ledger["boundaries"] + if entry["request"]["action"] == "stop-role"} + boundaries = [] + uncertain_tokens = [] + for start in ledger["boundaries"]: + if start["request"]["action"] != "start-role": + continue + stop = stops.get(start["stopped_sequence"]) + if stop is None: + raise protocol.Refusal("restart has no exact retained stop request") + role, predicate = stop["request"]["role"], stop["request"]["predicate"] + snapshots = [read(root / "snapshots" / entry[name]) for entry in (stop, start) for name in ("before", "after")] + before, stopped, restarting, after = snapshots + stable_ids = all(s["host"]["app_id"] == initial["app_id"] and + s["host"]["body_identity"]["body_id"] == identity["body_id"] and + s["host"]["body_identity"]["adapter_id"] == identity["adapter_id"] for s in snapshots) + stable_commands = all(command_identity(s) == command_identity(before) and s["posts"] == before["posts"] for s in snapshots) + public_before, public_after = before["public"], after["public"] + paused = bool(public_after and public_after["runner_status"] in {"paused", "blocked"} and + public_after["model_request_pending"] is False) + owned = bool(stop["process"]["exit_observed"] and stop["process"]["returncode"] == 0 and + stopped["processes"][role] is None and after["processes"][role] == start["pid"] and + stop["process"]["pid"] != start["pid"]) + epoch_before = before["body_identity"]["epoch"] + epoch_after = after["body_identity"]["epoch"] + epoch_ok = epoch_after == epoch_before + (1 if role == "body" else 0) + identities_ok = all(s["body_identity"] is None or all(s["body_identity"][key] == identity[key] + for key in body_client.IDENTITY_KEYS - {"epoch"}) for s in snapshots) + outage = True + if role == "body": + outage_public = restarting["public"] + outage = bool(stopped["body_identity"] is None and restarting["body_identity"] is None and + outage_public and outage_public["connection"]["state"] == "unavailable" and + outage_public["runner_status"] != "running" and not outage_public["model_request_pending"]) + pending_ok, model_reaped = True, True + token = public_before["recovery"]["command_id"] + if predicate == "uncertain": + uncertain_tokens.append(token) + pending_ok = bool(token and all(s["host"]["stage"] and s["host"]["stage"]["command_id"] == token and + token in s["client"]["pending_command_ids"] for s in snapshots) and + public_after["recovery"]["command_id"] == token and public_after["runner_status"] == "blocked" and + requests[token]["operation"] == "dispatch") + else: + pending_ok = public_before["model_request_pending"] is True and public_before["runner_status"] == "running" + job_id = public_before["active_job_id"] + job = next(job for job in public_before["jobs"] if job["job_id"] == job_id) + outcome = read(root / "operator" / "calls" / f"{job_id}-{job['call_count']:02d}" / "outcome.json") + model_reaped = outcome.get("outcome") == "cancelled" and outcome.get("process_exit_observed") is True + after_sequence = after["host"]["sequence"] + future_events = [event for event in host_events if event["seq"] > after_sequence] + resume = next((event for event in future_events if event["kind"] == "resume"), None) + reconcile = next((event for event in future_events if event["kind"] == "reconciled" and event.get("outcome") == token), None) + last_link = max((event["sequence"] for event in after["posts"]), default=0) + future_posts = [event for event in link_events if event["kind"] == "received" and event.get("method") == "POST" and + event.get("command_id") and event["sequence"] > last_link] + new_operations = [requests[event["command_id"]]["operation"] for event in future_posts] + first_decision = next((i for i, operation in enumerate(new_operations) if operation in {"propose", "dispatch"}), None) + fresh = bool(resume and first_decision is not None and "observe" in new_operations[:first_decision]) + receipt_ok = True + if predicate == "uncertain": + lookups = [event for event in link_events if event["kind"] == "received" and event.get("method") == "GET" and + event.get("path", "").startswith("/commands/" + token + "?") and event["sequence"] > last_link] + response_path = root / "operator" / "client" / "responses" / (token + "-" + client["commands"][token]["response_sha256"] + ".json") + receipt = read(response_path) + ui_recovered = [step for step in ui["steps"] if step["state"]["recovery"]["state"] == "recovered" and + step["state"]["recovery"]["command_id"] == token and + step["state"]["runner_status"] != "running" and + step["client_commands"] == len(after["client"]["commands"])] + receipt_ok = bool(ui_recovered and reconcile and resume and reconcile["seq"] < resume["seq"] and lookups and + receipt["state"] == "completed" and receipt["request_sha256"] == client["commands"][token]["request_sha256"] and + receipt["recorded_epoch"] == requests[token]["epoch"] and + len([event for event in link_events if event["kind"] == "received" and event.get("method") == "POST" and + event.get("command_id") == token]) == 1 and client["commands"][token]["send_count"] == 1) + ok = all((stop["passed"], start["passed"], stable_ids, stable_commands, paused, owned, epoch_ok, + identities_ok, outage, pending_ok, model_reaped, fresh, receipt_ok)) + boundaries.append({"role": role, "predicate": predicate, "passed": ok, "stop_sequence": stop["request"]["sequence"], + "start_sequence": start["request"]["sequence"], "command_id": token, + "stable_ids": stable_ids and identities_ok, "unchanged_sends": stable_commands, + "paused_after_restart": paused, "owned_process_reaped": owned, "epoch_bound": epoch_ok, + "outage_observed": outage, "pending_bound": pending_ok, "model_reaped": model_reaped, + "fresh_observation_after_explicit_resume": fresh, "historical_receipt_without_replay": receipt_ok}) + required = {("body", "planning"), ("body", "uncertain"), ("workbench", "planning"), ("workbench", "uncertain")} + passed = bool(legacy["passed"] and required == {(b["role"], b["predicate"]) for b in boundaries} and + len(boundaries) == 4 and all(b["passed"] for b in boundaries) and all(visible.values()) and + len(uncertain_tokens) == len(set(uncertain_tokens)) == 2 and not processes.get("cleanup_error")) + result = {"passed": passed, "legacy_qualification": legacy, "restart_boundaries": boundaries, + "scope": "four graceful owned mid-job restarts; actual Codex browser; loopback simulation", + "owned_processes_exited": processes["owned_processes_exited"], "visible_restart_states": visible} + protocol.create_file(root / "restart-score.json", protocol.encoded(result)) + return result + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--root", required=True, type=Path) + print(json.dumps(score(parser.parse_args().root), sort_keys=True)) + + +if __name__ == "__main__": + main() diff --git a/tools/physical-workcell/test_agent_bench.py b/tools/physical-workcell/test_agent_bench.py new file mode 100644 index 0000000..dfb63e2 --- /dev/null +++ b/tools/physical-workcell/test_agent_bench.py @@ -0,0 +1,218 @@ +"""SIM7 conformance for the external AI boundary against actual native benches.""" + +import contextlib +import io +import json +import unittest +import uuid +from pathlib import Path +from unittest import mock + +import agent_bench as agent + +ZONE = {"x_min": .04, "x_max": .10, "y_min": -.025, "y_max": .025} + + +class AgentBenchTests(unittest.TestCase): + def setUp(self): + self.root = agent.bench.body.directory(agent.BASE / "output" / ("a7test-" + uuid.uuid4().hex[:10]), create=True) + self.bench_dir = self.root / "bench" + agent.bench.initialize(self.bench_dir, diagnostic_enabled=True) + self.bench = agent.bench.Bench(self.bench_dir) + self.bench.start_job("job-one", ZONE) + self.session = self.root / "agent" + agent.initialize(self.session, self.bench_dir) + self.host = agent.AgentBench(self.session) + self.events = [] + + def tearDown(self): + result = self._outcome.result + failed = any(test is self for test, _ in (*result.failures, *result.errors)) + agent.bench.body.create_file(self.root / "conformance.json", agent.protocol.encoded( + {"test_id": self.id(), "passed": not failed, "events": self.events})) + + def proposal(self, action="move", challenge=None, **kwargs): + challenge = challenge or self.host.observe() + if action == "move" and not kwargs: + kwargs = {"target_x": -.25, "target_y": 0} + p = self.host.propose(challenge["challenge_id"], action, "AI development conformance intent", **kwargs) + self.events.append({"challenge": challenge, "proposal": p}) + return p + + def dispatch(self, p): + result = self.host.dispatch(p["proposal_id"]) + self.events.append({"dispatch": result}) + return result + + def test_challenge_has_only_images_task_and_public_observation(self): + c = self.host.observe() + self.assertEqual(c["observation_sha256"], agent.protocol.sha(agent.protocol.encoded(c["observation"]))) + self.assertEqual(c["task"]["zone"], ZONE) + self.assertEqual(Path(c["image_path"]).read_bytes()[:8], b"\x89PNG\r\n\x1a\n") + self.assertNotIn("block_xyz", json.dumps(c)) + self.assertNotIn("pusher_xyz", json.dumps(c)) + self.assertEqual(self.host.status()["host_role"], "external_multimodal_AI_host") + + def test_proposal_persists_intent_without_effect(self): + before = self.bench.status() + p = self.proposal() + self.assertEqual(before, self.bench.status()) + self.assertTrue((self.session / "proposals" / (p["proposal_id"] + ".json")).exists()) + self.assertEqual(list((self.session / "intents").iterdir()), []) + + def test_move_executes_once_duplicate_returns_exact_result(self): + p = self.proposal() + result = self.dispatch(p) + self.assertEqual(result["outcome"], "executed") + self.assertEqual(result["receipt"]["operation_id"], p["operation_id"]) + before = self.bench.status() + self.assertEqual(result, self.dispatch(p)) + self.assertEqual(before, self.bench.status()) + + def test_stale_move_refuses_and_returns_fresh_image(self): + c = self.host.observe() + p = self.proposal(challenge=c) + self.bench.disturb("external-force", -.4, 50) + generation = self.bench.status()["generation"] + result = self.dispatch(p) + self.assertEqual(result["outcome"], "refused") + self.assertEqual(self.bench.status()["generation"], generation) + self.assertGreater(result["next_observation"]["observation"]["generation"], c["observation"]["generation"]) + + def test_measurement_placement_refusal_does_not_create_sqlite(self): + result = self.dispatch(self.proposal("measure")) + self.assertEqual(result["outcome"], "refused") + self.assertFalse((self.bench_dir / "measurements.sqlite").exists()) + + def test_wrong_reset_phase_preserves_bench(self): + for action in ("begin_reset", "finish_reset"): + before = self.bench.status() + result = self.dispatch(self.proposal(action)) + self.assertEqual(result["outcome"], "refused") + self.assertEqual(before, self.bench.status()) + + def test_nonfinite_and_conflicting_arguments_never_create_proposal(self): + c = self.host.observe() + for action, x, y in (("move", float("nan"), 0), ("move", True, 0), ("move", 0, None), ("measure", 0, 0)): + with self.assertRaises((agent.Refusal, agent.protocol.Refusal)): + self.host.propose(c["challenge_id"], action, "intent", x, y) + self.assertEqual(list((self.session / "proposals").iterdir()), []) + + def test_foreign_challenge_and_unsafe_identity_refuse(self): + for token in ("challenge-" + uuid.uuid4().hex, "../bench/bench"): + with self.assertRaises((agent.Refusal, agent.protocol.Refusal)): + self.host.propose(token, "measure", "intent") + + def test_changed_proposal_refuses_before_dispatch(self): + p = self.proposal() + path = self.session / "proposals" / (p["proposal_id"] + ".json") + path.write_bytes(path.read_bytes().replace(b'"target_x": -0.25', b'"target_x": 0.18')) + before = self.bench.status() + with self.assertRaises((agent.Refusal, agent.protocol.Refusal)): + self.dispatch(p) + self.assertEqual(before, self.bench.status()) + + def test_changed_challenge_image_refuses(self): + c = self.host.observe() + p = self.proposal(challenge=c) + Path(c["image_path"]).write_bytes(b"changed image") + with self.assertRaises((agent.Refusal, agent.protocol.Refusal)): + self.dispatch(p) + self.assertEqual(list((self.session / "intents").iterdir()), []) + + def test_unresolved_intent_blocks_same_and_new_proposal(self): + p = self.proposal() + with agent.protocol.locked(self.session): + m, _ = self.host._load() + self.host._record(m, "intents", p["proposal_id"], {"interrupted_fixture": True}) + before = self.bench.status() + self.assertEqual(self.dispatch(p)["outcome"], "unknown") + self.assertEqual(self.dispatch(self.proposal())["outcome"], "unknown") + self.assertEqual(before, self.bench.status()) + + def test_measurement_worker_failure_retains_unknown_and_blocks_replay(self): + packet = self.bench.observe() + for index, x in enumerate((-.175, -.07, .015)): + self.bench.move("stage-" + str(index), packet, x, 0) + packet = self.bench.observe() + p = self.proposal("measure") + with mock.patch.object(agent.bench.subprocess, "run", return_value=mock.Mock(returncode=1, stdout=b"", stderr=b"fixture worker failure")): + result = self.dispatch(p) + self.assertEqual(result["outcome"], "unknown") + self.assertEqual(self.bench.status()["state"], "unknown") + self.assertIsNone(result["next_observation"]) + self.assertEqual(self.dispatch(p), result) + self.assertIn(p["proposal_id"], self.host.status()["unresolved_proposals"]) + + def test_source_change_and_foreign_bench_identity_block_dispatch(self): + p = self.proposal() + before = self.bench.status() + with mock.patch.object(agent, "sources", return_value={"changed": "source"}), self.assertRaises(agent.Refusal): + self.dispatch(p) + m = agent.protocol.decode(agent.protocol.read_bytes(self.session / "agent.json")) + m["bench_id"] = uuid.uuid4().hex + agent.protocol.persist(self.session / "agent.json", m) + with self.assertRaises(agent.Refusal): + self.dispatch(p) + self.assertEqual(before, self.bench.status()) + + def test_error_after_real_move_stays_unknown_even_if_bench_ready(self): + original = agent.bench.Bench.move + + def lost_reply(target, *args, **kwargs): + original(target, *args, **kwargs) + raise RuntimeError("fixture reply lost after real native motion") + + p = self.proposal() + with mock.patch.object(agent.bench.Bench, "move", lost_reply): + result = self.dispatch(p) + self.assertEqual(result["outcome"], "unknown") + self.assertEqual(self.bench.status()["state"], "ready") + before = self.bench.status() + self.assertEqual(self.dispatch(p), result) + self.assertEqual(self.dispatch(self.proposal())["outcome"], "unknown") + self.assertEqual(self.bench.status(), before) + self.assertIn(p["proposal_id"], self.host.status()["unresolved_proposals"]) + + def test_public_cli_measurement_reset_and_status_strip_detector_locations(self): + packet = self.bench.observe() + for index, x in enumerate((-.175, -.07, .015)): + self.bench.move("stage-" + str(index), packet, x, 0) + packet = self.bench.observe() + + def cli(*args): + output = io.StringIO() + with contextlib.redirect_stdout(output): + self.assertEqual(agent.main(["--session-dir", str(self.session), *args]), 0) + value = json.loads(output.getvalue()) + self.events.append({"public_cli": value}) + return value + + p = self.proposal("measure") + measured = cli("dispatch", "--proposal-id", p["proposal_id"]) + self.assertEqual(measured["outcome"], "executed") + metric = measured["receipt"]["measurement_receipt"]["measurement"] + self.assertIn("orange_pixel_count", metric) + self.assertIn("footprint_area_m2", metric) + private = agent.protocol.decode(agent.protocol.read_bytes(self.session / "results" / (p["proposal_id"] + ".json"))) + self.assertIn("block_center_xy", private["receipt"]["measurement_receipt"]["measurement"]) + reset = self.proposal("begin_reset", challenge=measured["next_observation"]) + responses = [measured, cli("dispatch", "--proposal-id", reset["proposal_id"]), cli("status")] + + def assert_no_locations(value): + if isinstance(value, dict): + self.assertFalse(agent.DERIVED_LOCATION_FIELDS.intersection(value)) + for item in value.values(): + assert_no_locations(item) + elif isinstance(value, list): + for item in value: + assert_no_locations(item) + + for response in responses: + assert_no_locations(response) + future = {"nested": [{"native_pose": [1, 2, 3], "block_xyz": [1, 2, 3], "target_x": .1}]} + self.assertEqual(agent.public_response(future), {"nested": [{"target_x": .1}]}) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_app_handoff.py b/tools/physical-workcell/test_app_handoff.py new file mode 100644 index 0000000..8dd1760 --- /dev/null +++ b/tools/physical-workcell/test_app_handoff.py @@ -0,0 +1,478 @@ +"""Actual local app/controller adapter conformance; independent private development inputs.""" + +import copy +import importlib.util +import io +import json +import os +import signal +import socket +import sqlite3 +import subprocess +import tempfile +import time +import unittest +import urllib.error +import urllib.parse +import urllib.request +import uuid +from pathlib import Path +from unittest import mock + +BASE = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("handoff_test", BASE / "app_handoff.py") +handoff = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(handoff) +protocol = handoff.protocol +PYTHON = BASE / ".venv/bin/python" + + +class ProposalShapeTests(unittest.TestCase): + def test_nonobject_proposals_refuse_before_lock_guard_or_controller_effects(self): + with tempfile.TemporaryDirectory(prefix="proposal-shape-", dir=protocol.OUTPUT) as temporary: + directory = Path(temporary) + record = {"operations": {}, "phase": "prepared", "lease": {"run_dir": "episode"}} + for index, value in enumerate((None, [], 42, "proposal", True)): + with self.subTest(value=value): + proposal = directory / f"proposal-{index}.json" + content = protocol.encoded(value) + protocol.create_file(proposal, content) + with (mock.patch.object(protocol, "locked") as locked, + mock.patch.object(handoff, "load", return_value=record), + mock.patch.object(handoff, "guard", return_value={"guard_current": True}) as guard, + mock.patch.object(handoff, "controller_health", return_value={}) as health, + mock.patch.object(handoff, "owned", return_value=True), + mock.patch.object(handoff, "controller") as controller): + with self.assertRaises(handoff.Refusal): + handoff.submit(directory, "shape", proposal, protocol.sha(content)) + locked.assert_not_called() + guard.assert_not_called() + health.assert_not_called() + controller.assert_not_called() + + def test_public_cli_returns_typed_nonobject_refusal_without_handoff_writes(self): + with tempfile.TemporaryDirectory(prefix="proposal-cli-", dir=protocol.OUTPUT) as temporary: + directory = Path(temporary) + proposal = directory / "proposal.json" + content = protocol.encoded([]) + protocol.create_file(proposal, content) + before = {path.name: path.read_bytes() for path in directory.iterdir()} + output = io.StringIO() + with mock.patch("sys.stdout", output): + code = handoff.main(["submit", "--handoff-dir", str(directory), "--operation-id", "shape", + "--proposal-file", str(proposal), "--proposal-sha256", protocol.sha(content)]) + self.assertEqual(code, 2) + self.assertEqual(json.loads(output.getvalue()), {"phase": "refused", "reason": "proposal must be a JSON object"}) + self.assertEqual({path.name: path.read_bytes() for path in directory.iterdir()}, before) + + +class HandoffTests(unittest.TestCase): + def setUp(self): + self.work = protocol.directory(protocol.OUTPUT / ("h5" + uuid.uuid4().hex[:6]), create=True) + self.app_dir = protocol.directory(self.work / "a", create=True) + self.evidence = [] + self.processes = [] + self.handles = [] + self.owned_controllers = [] + self.order = {"version": 1, "order_id": "handoff-private-order", + "project": {"namespace": "test", "project_id": "handoff-development"}, "part_id": "block", + "target_zone": {"x_min": .18, "x_max": .26, "y_min": -.04, "y_max": .04}, + "revision": 1, "state": "open"} + self.scene = {"block_xy": [-.17, 0], "illumination_scale": 1, + "occlusion_fraction": 0, "camera_offset_x_m": 0} + self.scene_path = self.work / "scene.json" + self.handoff_dir = None + self.counter = 0 + + def tearDown(self): + for controller_dir, pid, incarnation in self.owned_controllers: + reply = self.raw_controller(controller_dir, "health", allow_failure=True) + if reply.get("pid") == pid and reply.get("current_incarnation") == incarnation: + with self.subTest(cleanup_pid=pid): + os.kill(pid, signal.SIGTERM) + for process in self.processes: + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=6) + except subprocess.TimeoutExpired: + process.kill() + process.wait(timeout=4) + for handle in self.handles: + handle.close() + result = self._outcome.result + failed = any(test is self or getattr(test, "test_case", None) is self + for test, _ in (*result.failures, *result.errors)) + protocol.create_file(self.work / "conformance.json", protocol.encoded({ + "test_id": self.id(), "passed": not failed, "events": self.evidence, + "scope": "actual_local_process_adapter; mechanical HTTP use is not browser evidence"})) + + def command(self, script, command, *args, allow_failure=False, timeout=25): + result = subprocess.run([str(PYTHON), str(BASE / script), command, *map(str, args)], + capture_output=True, timeout=timeout, check=False) + try: + value = protocol.decode(result.stdout) + except protocol.Refusal as exc: + self.fail(f"public CLI returned no bounded JSON: {result.stdout!r}; {result.stderr!r}; {exc}") + self.evidence.append({"script": script, "command": command, "exit_code": result.returncode, "reply": value}) + self.no_oracle(value) + if not allow_failure: + self.assertEqual(result.returncode, 0, value) + return value + + def no_oracle(self, value): + serialized = json.dumps(value) + for forbidden in ("block_xyz", "pusher_xyz", "physical_condition_verified", "block_center_in_zone", "qpos", "xpos"): + self.assertNotIn(forbidden, serialized) + + def start_app(self, *, restart=False): + if not restart: + source = self.work / "order.json" + content = protocol.encoded(self.order) + protocol.create_file(source, content) + initialized = self.command("order_app.py", "init", "--app-dir", self.app_dir, + "--order-file", source, "--order-sha256", protocol.sha(content)) + self.app_id = initialized["app_id"] + port = 0 + else: + port = urllib.parse.urlsplit(self.url).port + logfile = open(self.work / f"app-{len(self.processes)}.log", "xb") # noqa: SIM115 - owned until tearDown + os.chmod(logfile.name, 0o600) + self.handles.append(logfile) + process = subprocess.Popen([str(PYTHON), str(BASE / "order_app.py"), "serve", "--app-dir", str(self.app_dir), + "--port", str(port)], stdout=logfile, stderr=subprocess.STDOUT) + self.processes.append(process) + deadline = time.monotonic() + 5 + while time.monotonic() < deadline: + if process.poll() is not None: + self.fail(f"app startup failed; private logs: {self.work}") + readiness = self.app_dir / "readiness.json" + if readiness.exists(): + value = protocol.decode(readiness.read_bytes()) + if value.get("phase") == "ready" and value.get("pid") == process.pid: + self.url = value["origin"] + self.assertEqual(value["app_id"], self.app_id) + return process + time.sleep(.02) + self.fail("app readiness timeout") + + def http(self, route, value=None, *, allow_failure=False): + request = urllib.request.Request(self.url + route, data=None if value is None else protocol.encoded(value), + headers={"Content-Type": "application/json", "Origin": self.url, + "X-Footwork-App-ID": self.app_id}) + opener = urllib.request.build_opener(urllib.request.ProxyHandler({})) + try: + response = opener.open(request, timeout=5) + except urllib.error.HTTPError as exc: + response = exc + with response: + code = response.code + reply = protocol.decode(response.read(protocol.MAX_BYTES + 1)) + self.evidence.append({"http": route, "method": "GET" if value is None else "POST", "code": code, "reply": reply}) + self.no_oracle(reply) + if not allow_failure: + self.assertEqual(code, 200, reply) + return code, reply + + def bind(self): + _, value = self.http(f"/api/orders/{self.order['order_id']}/bind", { + "expected_revision": 1, "binding_operation_id": "private-binding"}) + self.lease = value["lease"] + self.handoff_dir = Path(self.lease["handoff_dir"]) + + def prepare(self, *, pace=0, delay=0): + if not hasattr(self, "lease"): + self.start_app() + self.bind() + if not self.scene_path.exists(): + protocol.create_file(self.scene_path, protocol.encoded(self.scene)) + self.scene_sha = protocol.sha(self.scene_path.read_bytes()) + value = self.command("app_handoff.py", "prepare", "--app-url", self.url, "--expected-app-id", self.app_id, + "--lease-id", self.lease["lease_id"], "--scene-file", self.scene_path, + "--scene-sha256", self.scene_sha, "--pace-s", pace, "--dispatch-delay-s", delay) + self.assertEqual(value["phase"], "prepared", value) + controller = value["controller"] + ownership = (self.lease["controller_dir"], controller["pid"], controller["current_incarnation"]) + if ownership not in self.owned_controllers: + self.owned_controllers.append(ownership) + return value + + def adapter(self, command, *args, allow_failure=False): + return self.command("app_handoff.py", command, "--handoff-dir", self.handoff_dir, + *args, allow_failure=allow_failure) + + def raw_controller(self, control, command, *args, allow_failure=False): + return self.command("controller.py", command, "--controller-dir", control, *args, allow_failure=allow_failure) + + def packet(self): + value = self.adapter("observe") + self.assertEqual(value["phase"], "ready", value) + return value["observation"] + + def proposal(self, packet, token, *, complete=False, noncanonical=False): + value = {"version": 1, "run_dir": self.lease["run_dir"], "generation": packet["generation"], + "image_sha256": packet["image_sha256"], "token": token, + "block_box": packet["perception"]["block_box"], "decision": "complete" if complete else "move"} + if not complete: + value["target"] = {"x": min(.17, packet["perception"]["pusher_center_xy"][0] + .10), "y": 0} + content = json.dumps(value, indent=4).encode() if noncanonical else protocol.encoded(value) + path = self.work / f"proposal-{token}.json" + protocol.create_file(path, content) + return path, protocol.sha(content) + + def submit(self, operation_id, proposal, *, allow_failure=False): + return self.adapter("submit", "--operation-id", operation_id, "--proposal-file", proposal[0], + "--proposal-sha256", proposal[1], allow_failure=allow_failure) + + def until(self, operation_id, phases=("executed", "staged", "cancelled", "refused", "unknown")): + deadline = time.monotonic() + 12 + while time.monotonic() < deadline: + value = self.adapter("status", "--operation-id", operation_id, allow_failure=True) + if value.get("phase") in phases: + return value + time.sleep(.02) + self.fail("controller phase timeout") + + def states(self): + return {path.name: path.read_bytes() for path in Path(self.lease["run_dir"]).glob("state-*.json")} + + def test_prepare_submit_fresh_caller_repeat_and_exact_raw_proposal_digest(self): + prepared = self.prepare() + packet = self.packet() + proposal = self.proposal(packet, "first-move", noncanonical=True) + self.submit("first-move", proposal) + final = self.until("first-move") + self.assertEqual(final["phase"], "executed", final) + before = self.states() + repeated = self.prepare() + self.assertEqual(repeated["controller"]["pid"], prepared["controller"]["pid"]) + self.assertEqual(repeated["controller"]["current_incarnation"], prepared["controller"]["current_incarnation"]) + self.assertEqual(self.submit("first-move", proposal)["phase"], "executed") + self.assertEqual(self.states(), before) + changed = self.proposal(self.packet(), "conflict") + self.assertEqual(self.submit("first-move", changed, allow_failure=True)["phase"], "refused") + self.assertEqual(self.states(), before) + + def changed_during_motion(self, action): + self.prepare(pace=.003) + self.submit("contact", self.proposal(self.packet(), "contact")) + first = self.until("contact") + self.assertEqual(first["phase"], "executed") + self.assertGreater(first["receipt"]["contact_statistics"]["pusher_block_contact_samples"], 0) + packet = self.packet() + self.submit("active", self.proposal(packet, "active")) + self.until("active", phases=("running",)) + time.sleep(.18) + mutation = {"expected_revision": 1} + if action == "edit": + mutation["target_zone"] = {"x_min": .16, "x_max": .24, "y_min": -.04, "y_max": .04} + self.http(f"/api/orders/{self.order['order_id']}/{action}", mutation) + guarded = self.adapter("guard", allow_failure=True) + self.assertFalse(guarded["guard_current"]) + self.assertTrue(guarded["physical_effects_may_be_partial"]) + self.assertFalse(guarded["rollback_claim"]) + terminal = self.until("active") + self.assertEqual(terminal["phase"], "cancelled", terminal) + self.assertGreater(terminal["stop_evidence"]["native_steps_before_cancel"], 0) + self.assertTrue(terminal["stop_evidence"]["stop_verified"]) + before = self.states() + blocked_proposal = self.proposal(packet, "withheld") + self.assertIn(self.submit("withheld", blocked_proposal, allow_failure=True)["phase"], {"withheld", "unknown"}) + self.assertEqual(self.states(), before) + record = handoff.load(self.handoff_dir) + self.assertNotIn("withheld", record["operations"]) + _, order = self.http(f"/api/orders/{self.order['order_id']}") + self.assertEqual(order["order"]["revision"], 2) + self.assertEqual(order["order"]["state"], "cancelled" if action == "cancel" else "open") + self.assertEqual(self.adapter("reconcile", allow_failure=True)["phase"], "unknown") + + def test_order_edit_during_native_progress_guards_and_cancels(self): + self.changed_during_motion("edit") + + def test_order_cancel_during_native_progress_guards_and_cancels(self): + self.changed_during_motion("cancel") + + def test_exact_committed_app_effect_reconciles_after_lost_reply(self): + self.scene["block_xy"] = [.195, .006] + self.prepare() + completion_id = self.lease["physical_completion_id"] + wrong = self.proposal(self.packet(), "wrong-stage", complete=True) + self.assertEqual(self.submit("wrong-stage", wrong, allow_failure=True)["phase"], "refused") + self.submit(completion_id, self.proposal(self.packet(), completion_id, complete=True)) + self.assertEqual(self.until(completion_id)["phase"], "staged") + content = protocol.encoded({"lease_id": self.lease["lease_id"], + "completion_operation_id": self.lease["completion_operation_id"]}) + parsed = urllib.parse.urlsplit(self.url) + header = (f"POST /api/orders/{self.order['order_id']}/fulfill HTTP/1.1\r\nHost: {parsed.netloc}\r\n" + f"Origin: {self.url}\r\nX-Footwork-App-ID: {self.app_id}\r\nContent-Type: application/json\r\n" + f"Content-Length: {len(content)}\r\n\r\n").encode() + with socket.create_connection(("127.0.0.1", parsed.port), timeout=3) as client: + client.sendall(header + content) + self.evidence.append({"scenario": "actual_fulfillment_client_closed_before_reply", "blind_resubmissions": 0}) + deadline = time.monotonic() + 8 + while time.monotonic() < deadline: + reconciled = self.adapter("reconcile", allow_failure=True) + if reconciled["phase"] == "fulfilled": + break + time.sleep(.02) + self.assertEqual(reconciled["phase"], "fulfilled", reconciled) + self.assertEqual(reconciled["receipt"]["completion_operation_id"], self.lease["completion_operation_id"]) + self.assertEqual(reconciled["receipt"]["lease_id"], self.lease["lease_id"]) + before = self.states() + self.assertEqual(self.adapter("reconcile")["receipt"], reconciled["receipt"]) + self.assertEqual(self.states(), before) + self.assertFalse(self.adapter("guard", allow_failure=True)["guard_current"]) + + def test_app_restart_same_origin_reconnects_without_native_replay(self): + prepared = self.prepare() + self.submit("restart-history", self.proposal(self.packet(), "restart-history")) + self.assertEqual(self.until("restart-history")["phase"], "executed") + before = self.states() + self.processes[0].terminate() + self.processes[0].wait(timeout=5) + self.start_app(restart=True) + repeated = self.prepare() + self.assertEqual(repeated["controller"]["pid"], prepared["controller"]["pid"]) + self.assertEqual(self.adapter("status", "--operation-id", "restart-history")["historical_outcome"], "executed") + self.assertEqual(self.states(), before) + + def test_malformed_retained_receipt_actual_api_readback_never_claims_effect(self): + self.scene["block_xy"] = [.195, .006] + self.prepare() + physical_id = self.lease["physical_completion_id"] + self.submit(physical_id, self.proposal(self.packet(), physical_id, complete=True)) + self.assertEqual(self.until(physical_id)["phase"], "staged") + self.http(f"/api/orders/{self.order['order_id']}/fulfill", { + "lease_id": self.lease["lease_id"], "completion_operation_id": self.lease["completion_operation_id"]}) + self.assertEqual(self.adapter("reconcile")["phase"], "fulfilled") + database = self.app_dir / "orders.sqlite3" + with sqlite3.connect(database) as connection: + original_receipt = connection.execute("SELECT receipt_json FROM operations WHERE operation_id=?", ( + self.lease["completion_operation_id"],)).fetchone()[0] + original_order = connection.execute("SELECT revision,state,work_order_json FROM orders WHERE order_id=?", ( + self.order["order_id"],)).fetchone() + protocol.create_file(self.work / "original-application-receipt.json", original_receipt.encode()) + before = self.states() + variants = (("receipt_state", {"state": "open"}, {}), + ("boolean_leased_revision", {"leased_revision": True}, {}), + ("fulfilled_revision", {"fulfilled_revision": 4}, {}), + ("terminal_current_state", {}, {"state": "cancelled"}), + ("current_revision_before_receipt", {}, {"revision": 1}), + ("current_order_identity", {}, {"order_id": "another-order"})) + for name, receipt_changes, order_changes in variants: + with self.subTest(fault=name): + receipt = protocol.decode(original_receipt.encode()) + receipt.update(receipt_changes) + order_json = protocol.decode(original_order[2].encode()) + if "order_id" in order_changes: + order_json["order_id"] = order_changes["order_id"] + with sqlite3.connect(database) as connection: + connection.execute("UPDATE operations SET receipt_json=? WHERE operation_id=?", ( + protocol.encoded(receipt).decode(), self.lease["completion_operation_id"])) + connection.execute("UPDATE orders SET revision=?,state=?,work_order_json=? WHERE order_id=?", ( + order_changes.get("revision", original_order[0]), order_changes.get("state", original_order[1]), + protocol.encoded(order_json).decode(), self.order["order_id"])) + self.evidence.append({"scenario": "labeled_retained_store_readback_fault", "fault": name, + "source_store_identity_and_native_history_unchanged": True}) + result = self.adapter("reconcile", allow_failure=True) + self.assertEqual(result["phase"], "refused", result) + self.assertFalse(result.get("application_effect_confirmed", False)) + self.assertEqual(self.states(), before) + with sqlite3.connect(database) as connection: + connection.execute("UPDATE operations SET receipt_json=? WHERE operation_id=?", ( + original_receipt, self.lease["completion_operation_id"])) + connection.execute("UPDATE orders SET revision=?,state=?,work_order_json=? WHERE order_id=?", ( + *original_order, self.order["order_id"])) + self.assertEqual(self.adapter("reconcile")["phase"], "fulfilled") + + def test_external_controller_restart_allows_history_but_no_ownership_takeover(self): + self.prepare() + self.submit("external-history", self.proposal(self.packet(), "external-history")) + self.assertEqual(self.until("external-history")["phase"], "executed") + stopped = self.adapter("stop") + self.assertTrue(stopped["process_termination_observed"]) + logfile = open(self.work / "external-controller.log", "xb") # noqa: SIM115 - owned until tearDown + os.chmod(logfile.name, 0o600) + self.handles.append(logfile) + process = subprocess.Popen([str(PYTHON), str(BASE / "controller.py"), "serve", "--run-dir", self.lease["run_dir"], + "--controller-dir", self.lease["controller_dir"]], stdout=logfile, stderr=subprocess.STDOUT) + self.processes.append(process) + deadline = time.monotonic() + 5 + while time.monotonic() < deadline: + health = self.raw_controller(self.lease["controller_dir"], "health", allow_failure=True) + if health.get("pid") == process.pid: + break + time.sleep(.02) + before = self.states() + self.assertEqual(self.adapter("status", "--operation-id", "external-history")["historical_outcome"], "executed") + proposal = self.proposal(self.packet(), "unowned-submit") + self.assertEqual(self.submit("unowned-submit", proposal, allow_failure=True)["phase"], "refused") + self.assertEqual(self.adapter("stop", allow_failure=True)["phase"], "refused") + self.assertIsNone(process.poll()) + self.assertEqual(self.states(), before) + + def test_wrong_app_lease_scene_digest_and_retained_paths_refuse(self): + self.start_app() + self.bind() + protocol.create_file(self.scene_path, protocol.encoded(self.scene)) + digest = protocol.sha(self.scene_path.read_bytes()) + for app_id, lease_id, scene_sha in (("wrong-app", self.lease["lease_id"], digest), + (self.app_id, "0" * 32, digest), + (self.app_id, self.lease["lease_id"], "0" * 64)): + with self.subTest(app_id=app_id, lease_id=lease_id): + result = self.command("app_handoff.py", "prepare", "--app-url", self.url, "--expected-app-id", app_id, + "--lease-id", lease_id, "--scene-file", self.scene_path, "--scene-sha256", scene_sha, + allow_failure=True) + self.assertEqual(result["phase"], "refused") + self.assertFalse((self.handoff_dir / "handoff.json").exists()) + health = handoff.api(self.url, self.app_id, "/api/health") + changed = copy.deepcopy(self.lease) + changed["run_dir"] = str(self.work / "arbitrary-proof") + with self.assertRaises(protocol.Refusal): + handoff.lease_schema(changed, health, self.url, self.app_id, self.lease["lease_id"]) + + def test_scene_change_repeat_never_reinitializes_or_replaces_controller(self): + self.prepare() + before = self.states() + original = self.scene_path.read_bytes() + self.scene_path.write_bytes(original + b" ") + result = self.command("app_handoff.py", "prepare", "--app-url", self.url, "--expected-app-id", self.app_id, + "--lease-id", self.lease["lease_id"], "--scene-file", self.scene_path, + "--scene-sha256", protocol.sha(self.scene_path.read_bytes()), allow_failure=True) + self.assertEqual(result["phase"], "refused") + self.assertEqual(self.states(), before) + self.scene_path.write_bytes(original) + + def test_missing_app_readback_stays_unknown_and_guard_withholds(self): + self.prepare() + self.assertEqual(self.adapter("reconcile", allow_failure=True)["phase"], "unknown") + proposal = self.proposal(self.packet(), "app-down") + before = self.states() + self.processes[0].terminate() + self.processes[0].wait(timeout=5) + guarded = self.adapter("guard", allow_failure=True) + self.assertFalse(guarded["guard_current"]) + self.assertEqual(guarded["phase"], "unknown") + self.assertEqual(self.submit("app-down", proposal, allow_failure=True)["phase"], "unknown") + self.assertEqual(self.states(), before) + self.assertNotIn("app-down", handoff.load(self.handoff_dir)["operations"]) + + def test_loopback_origin_strict_and_scene_symlink_refusal(self): + for url in ("https://127.0.0.1:1234", "http://localhost:1234", "http://127.0.0.1:1234/", + "http://user:secret@127.0.0.1:1234", "http://127.0.0.1:1234?other=1"): + with self.subTest(url=url), self.assertRaises(protocol.Refusal): + handoff.origin(url) + self.start_app() + self.bind() + protocol.create_file(self.scene_path, protocol.encoded(self.scene)) + linked = self.work / "linked-scene.json" + linked.symlink_to(self.scene_path) + result = self.command("app_handoff.py", "prepare", "--app-url", self.url, "--expected-app-id", self.app_id, + "--lease-id", self.lease["lease_id"], "--scene-file", linked, + "--scene-sha256", protocol.sha(self.scene_path.read_bytes()), allow_failure=True) + self.assertEqual(result["phase"], "refused") + self.assertFalse((self.handoff_dir / "handoff.json").exists()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_bench.py b/tools/physical-workcell/test_bench.py new file mode 100644 index 0000000..26ac33d --- /dev/null +++ b/tools/physical-workcell/test_bench.py @@ -0,0 +1,290 @@ +"""SIM6 focused development conformance with actual native state and SQLite. + +These gates are separate from the frozen public-interface qualification. Each +test retains its own private inputs and outcome beneath physical-workcell/output. +""" + +import copy +import importlib.util +import json +import os +import sqlite3 +import unittest +import uuid +from pathlib import Path + +BASE = Path(__file__).resolve().parent +ZONE = {"x_min": .04, "x_max": .10, "y_min": -.025, "y_max": .025} + + +def load_backend(): + spec = importlib.util.spec_from_file_location("sim6_conformance_bench", BASE / "bench.py") + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +class BenchBehaviorTests(unittest.TestCase): + def setUp(self): + self.backend = load_backend() + self.work = BASE / "output" / ("b6test-" + uuid.uuid4().hex[:10]) + self.work.mkdir(mode=0o700) + self.root = self.work / "bench" + self.events = [] + + def tearDown(self): + result = self._outcome.result + failed = any(test is self for test, _ in (*result.failures, *result.errors)) + content = (json.dumps({"test_id": self.id(), "passed": not failed, + "scenario_kind": "actual_native_sqlite_conformance", + "events": self.events}, sort_keys=True, indent=2) + "\n").encode() + path = self.work / "conformance.json" + with path.open("xb") as handle: + os.chmod(path, 0o600) + handle.write(content) + + def event(self, label, value): + self.events.append({"kind": label, "value": value}) + return value + + def snapshot(self): + """Evidence around refusals; SQLite readback uses its own connection.""" + return {str(path.relative_to(self.root)): path.read_bytes() + for path in self.root.rglob("*") if path.is_file() and path.suffix != ".lock" + and path.parent.name != "images"} + + def sqlite_rows(self, query): + path = self.root / "measurements.sqlite" + with sqlite3.connect("file:" + str(path) + "?mode=ro", uri=True) as database: + database.row_factory = sqlite3.Row + result = [dict(row) for row in database.execute(query)] + return self.event("independent_sqlite_readback", result) + + def initialize(self, *, diagnostic=False): + initial = self.event("initialize", self.backend.initialize(self.root, diagnostic)) + self.bench = self.backend.Bench(self.root) + self.assertEqual(initial["generation"], 0) + self.assertIsNone(initial["active_job"]) + return initial + + def start(self): + self.event("start_job", self.bench.start_job("first-job", dict(ZONE))) + return self.event("observation", self.bench.observe()) + + def refusal_without_effect(self, callback): + before = self.snapshot() + with self.assertRaises((self.backend.Refusal, self.backend.protocol.Refusal)) as error: + callback() + self.event("refusal", str(error.exception)) + self.assertEqual(self.snapshot(), before, "refused request changed retained effects") + + def stage(self): + """Fixed contact trajectory for a declared native development fixture.""" + packet = self.start() + contact_samples = 0 + for index, x in enumerate((-.175, -.07, .015)): + receipt = self.event("stage_move", self.bench.move(f"stage-{index}", packet, x, 0)) + self.assertGreater(receipt["native_steps"], 0) + contact_samples += receipt["pusher_block_contact_samples"] + packet = self.event("observation", self.bench.observe()) + self.assertGreater(contact_samples, 0) + pixels = self.backend.vision.decode_png(Path(packet["image_path"]).read_bytes()) + center = self.backend.vision.image_world_center(self.backend.vision.locate(pixels)["block"]["box"]) + self.assertTrue(ZONE["x_min"] <= center[0] <= ZONE["x_max"], center) + self.assertTrue(ZONE["y_min"] <= center[1] <= ZONE["y_max"], center) + return packet + + def test_successive_job_gate_requires_measurement_and_verified_reset(self): + self.initialize() + packet = self.start() + self.refusal_without_effect(lambda: self.bench.start_job("second-job", ZONE)) + self.refusal_without_effect(lambda: self.bench.begin_reset("first-job")) + self.refusal_without_effect(lambda: self.bench.finish_reset("early-reset", packet)) + self.refusal_without_effect(lambda: self.bench.measure("early-measure", packet)) + self.assertFalse((self.root / "measurements.sqlite").exists()) + + def test_measurement_is_a_separate_process_with_exact_persisted_image_binding(self): + self.initialize() + packet = self.stage() + state_before = {path.name: path.read_bytes() for path in self.root.glob("state-*.json")} + measured = self.event("measure", self.bench.measure("optical-first", packet)) + result = measured["measurement_receipt"] + self.assertNotEqual(result["worker_pid"], os.getpid()) + self.assertIs(type(result["worker_pid"]), int) + self.assertGreater(result["worker_pid"], 0) + for key in ("bench_id", "job_id", "generation", "native_time_s", "image_sha256", "state_sha256"): + self.assertEqual(result[key], packet[key]) + self.assertEqual(result["operation_id"], "optical-first") + metric = result["measurement"] + self.assertEqual(metric["method"], "simulated_orange_RGB_footprint") + self.assertTrue(1600 <= metric["orange_pixel_count"] <= 2100, metric) + self.assertAlmostEqual(metric["footprint_area_m2"], .0049, delta=.0009) + self.assertTrue(.04 <= metric["block_center_xy"][0] <= .10, metric) + self.assertTrue(-.025 <= metric["block_center_xy"][1] <= .025, metric) + box = metric["block_box"] + for center, lower, upper in zip(metric["centroid_pixel_xy"], box[:2], box[2:]): + self.assertTrue(lower <= center < upper) + rows = self.sqlite_rows("SELECT * FROM measurements") + self.assertEqual(len(rows), 1) + row = rows[0] + self.assertEqual(json.loads(row.pop("receipt_json")), result) + self.assertEqual(row, {key: result[key] for key in row}) + worker = json.loads((self.root / "requests" / (self.backend.body.sha(b"optical-first") + ".worker.json")).read_bytes()) + self.assertEqual(worker["returncode"], 0) + self.assertEqual(json.loads(worker["stdout"]), result) + self.assertEqual(state_before, {path.name: path.read_bytes() for path in self.root.glob("state-*.json")}) + self.assertEqual(self.bench.status()["active_job"]["phase"], "measured") + + def test_measurement_replay_is_historical_and_does_not_run_another_worker(self): + self.initialize() + packet = self.stage() + measured = self.event("measure", self.bench.measure("measurement", packet)) + before = self.snapshot() + self.assertEqual(self.bench.measure("measurement", packet), measured) + self.assertEqual(self.snapshot(), before) + fresh = self.event("measured_observation", self.bench.observe()) + self.refusal_without_effect(lambda: self.bench.measure("measurement", fresh)) + self.refusal_without_effect(lambda: self.bench.measure("second-measurement", fresh)) + self.refusal_without_effect(lambda: self.bench.move("forbidden-measured-motion", fresh, -.1, 0)) + self.refusal_without_effect(lambda: self.bench.start_job("second-job", ZONE)) + self.event("begin_reset", self.bench.begin_reset("first-job")) + self.assertEqual(self.bench.measure("measurement", packet), measured) + resetting = self.event("resetting_observation", self.bench.observe()) + self.refusal_without_effect(lambda: self.bench.finish_reset("no-contact-reset", resetting)) + self.assertEqual(self.bench.status()["active_job"]["phase"], "resetting") + self.assertEqual(len(self.sqlite_rows("SELECT * FROM measurements")), 1) + + def test_exact_motion_replay_retains_one_native_effect_and_conflicts_refuse(self): + self.initialize() + packet = self.start() + moved = self.event("move", self.bench.move("one-motion", packet, -.175, 0)) + before = self.snapshot() + self.assertEqual(self.bench.move("one-motion", packet, -.175, 0), moved) + self.assertEqual(self.snapshot(), before) + self.refusal_without_effect(lambda: self.bench.move("one-motion", packet, -.17, 0)) + self.refusal_without_effect(lambda: self.bench.measure("one-motion", packet)) + self.assertEqual(self.bench.status()["generation"], 1) + + def test_external_force_invalidates_stale_motion_and_measurement_before_dispatch(self): + self.initialize(diagnostic=True) + packet = self.stage() + intervention = self.event("diagnostic_force", self.bench.disturb("unexpected-block-motion", 1.5, 80)) + self.assertEqual(intervention["kind"], "diagnostic_external_force") + self.assertGreater(intervention["generation"], packet["generation"]) + self.assertGreater(intervention["native_time_s"], packet["native_time_s"]) + fresh = self.event("fresh_after_intervention", self.bench.observe()) + self.assertNotEqual(packet["image_sha256"], fresh["image_sha256"]) + self.refusal_without_effect(lambda: self.bench.move("stale-motion", packet, .025, 0)) + self.refusal_without_effect(lambda: self.bench.measure("stale-measurement", packet)) + self.assertFalse((self.root / "measurements.sqlite").exists()) + before = self.snapshot() + self.assertEqual(self.bench.disturb("unexpected-block-motion", 1.5, 80), intervention) + self.assertEqual(self.snapshot(), before) + self.refusal_without_effect(lambda: self.bench.disturb("unexpected-block-motion", 1.4, 80)) + + def test_diagnostic_force_needs_explicit_enablement_and_finite_bounds(self): + self.initialize() + self.start() + for force, steps in ((1, 50), (float("nan"), 50), (2.1, 50), (1, True), (1, 151), (0, 50)): + with self.subTest(force=force, steps=steps): + self.refusal_without_effect(lambda force=force, steps=steps: self.bench.disturb("invalid-disturb", force, steps)) + + def test_foreign_or_modified_observation_cannot_authorize_a_motion(self): + self.initialize() + packet = self.start() + changes = (("bench_id", "0" * 32), ("job_id", "different-job"), ("generation", False), + ("state_sha256", "0" * 64), ("native_time_s", packet["native_time_s"] + .002), + ("image_sha256", "0" * 64), ("phase", "resetting"), ("source_kind", "real_camera")) + for key, value in changes: + with self.subTest(field=key): + foreign = copy.deepcopy(packet) + foreign[key] = value + self.refusal_without_effect(lambda foreign=foreign: self.bench.move("foreign-packet", foreign, -.175, 0)) + + def test_corrupted_native_state_never_causes_an_implicit_reset(self): + self.initialize() + self.start() + path = self.root / "state-000000.json" + with path.open("ab") as handle: + handle.write(b" ") + self.event("offline_fault", {"kind": "native_state_bytes_modified", "path": str(path)}) + before = path.read_bytes() + with self.assertRaises(self.backend.Refusal): + self.bench.observe() + with self.assertRaises(self.backend.Refusal): + self.backend.initialize(self.root) + self.assertEqual(path.read_bytes(), before) + self.assertFalse((self.root / "state-000001.json").exists()) + + def test_corrupted_source_binding_refuses_without_a_new_generation(self): + self.initialize() + packet = self.start() + manifest_path = self.root / "bench.json" + manifest = json.loads(manifest_path.read_bytes()) + manifest["source_fingerprints"]["bench_sha256"] = "0" * 64 + manifest_path.write_bytes(self.backend.body.encoded(manifest)) + self.event("offline_fault", {"kind": "retained_source_binding_modified"}) + self.refusal_without_effect(lambda: self.bench.move("changed-source", packet, -.175, 0)) + self.assertFalse((self.root / "state-000001.json").exists()) + + def test_corrupted_retained_model_refuses_without_recreating_the_body(self): + self.initialize() + packet = self.start() + with (self.root / "model.xml").open("ab") as handle: + handle.write(b" ") + self.event("offline_fault", {"kind": "retained_model_bytes_modified"}) + self.refusal_without_effect(lambda: self.bench.move("changed-model", packet, -.175, 0)) + self.assertFalse((self.root / "state-000001.json").exists()) + + def test_corrupted_image_refuses_measurement_and_does_not_create_a_database(self): + self.initialize() + packet = self.stage() + path = Path(packet["image_path"]) + with path.open("ab") as handle: + handle.write(b"changed-image") + self.event("offline_fault", {"kind": "retained_image_bytes_modified", "path": str(path)}) + corrupted = path.read_bytes() + self.refusal_without_effect(lambda: self.bench.measure("corrupted-image-measurement", packet)) + self.assertEqual(path.read_bytes(), corrupted) + self.assertFalse((self.root / "measurements.sqlite").exists()) + + def test_measurement_receipt_corruption_refuses_historical_replay(self): + self.initialize() + packet = self.stage() + self.event("measure", self.bench.measure("measurement", packet)) + with sqlite3.connect(self.root / "measurements.sqlite") as database: + receipt = json.loads(database.execute("SELECT receipt_json FROM measurements").fetchone()[0]) + receipt["measurement"]["orange_pixel_count"] += 1 + database.execute("UPDATE measurements SET receipt_json=?", (json.dumps(receipt),)) + self.event("offline_fault", {"kind": "retained_measurement_receipt_modified"}) + self.refusal_without_effect(lambda: self.bench.measure("measurement", packet)) + + def test_measurement_column_corruption_is_not_hidden_by_an_unchanged_receipt(self): + self.initialize() + packet = self.stage() + self.event("measure", self.bench.measure("measurement", packet)) + with sqlite3.connect(self.root / "measurements.sqlite") as database: + database.execute("UPDATE measurements SET job_id='different-job'") + self.event("offline_fault", {"kind": "retained_measurement_job_column_modified"}) + self.refusal_without_effect(lambda: self.bench.measure("measurement", packet)) + self.assertEqual(self.bench.status()["state"], "unknown") + + def test_retained_interrupted_intent_blocks_all_new_dispatch(self): + self.initialize() + packet = self.start() + path = self.root / "bench.json" + manifest = json.loads(path.read_bytes()) + manifest["busy"] = {"operation_id": "interrupted", "kind": "move", "generation": packet["generation"]} + path.write_bytes(self.backend.body.encoded(manifest)) + self.event("offline_fault", {"kind": "retained_interrupted_dispatch_intent"}) + status = self.event("unknown_status", self.bench.status()) + self.assertEqual(status["state"], "unknown") + self.assertEqual(status["unresolved_effect"]["operation_id"], "interrupted") + self.refusal_without_effect(lambda: self.bench.start_job("next-job", ZONE)) + self.refusal_without_effect(lambda: self.bench.observe()) + self.refusal_without_effect(lambda: self.bench.move("new-move", packet, -.175, 0)) + self.refusal_without_effect(lambda: self.bench.measure("new-measurement", packet)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_body_client.py b/tools/physical-workcell/test_body_client.py new file mode 100644 index 0000000..d002e52 --- /dev/null +++ b/tools/physical-workcell/test_body_client.py @@ -0,0 +1,277 @@ +"""Client/proxy development checks over real sockets and retained native bodies.""" +import socket +import time +import unittest +import uuid +from pathlib import Path + +import body_client +import body_network +import network_fault_proxy +import order_protocol as storage + +BASE = Path(__file__).resolve().parent +ZONE = {"x_min": .04, "x_max": .10, "y_min": -.025, "y_max": .025} + + +class BodyClientTests(unittest.TestCase): + def setUp(self): + self.root = BASE / "output" / ("sim9-client-" + uuid.uuid4().hex[:12]) + self.root.mkdir(mode=0o700) + body_network.initialize(self.root / "server", diagnostic_enabled=True) + self.server = body_network.BodyService(self.root / "server").start() + self.proxy = network_fault_proxy.FaultProxy(self.server.origin, self.root / "proxy").start() + self.client = body_client.BodyClient(self.root / "client", self.proxy.origin, timeout=.5) + self.clients = [self.client] + self.evidence = [] + + def tearDown(self): + for client in self.clients: + client.close() + self.proxy.close() + self.server.close() + result = self._outcome.result + failed = any(test is self for test, _ in (*result.failures, *result.errors)) + storage.create_file(self.root / "conformance.json", storage.encoded({"test_id": self.id(), "passed": not failed, + "scenario_kind": "actual_tcp_native_client_proxy", "evidence": self.evidence})) + + def event(self, value): + self.evidence.append(value) + return value + + def proposal(self): + self.client.acquire() + self.client.start_job("job", ZONE) + observation = self.client.observe() + return self.client.propose(observation["challenge_id"], "move", "bounded native approach", -.19, 0) + + def events(self): + return [storage.decode(storage.read_bytes(path)) for path in sorted((self.root / "proxy" / "events").glob("*.json"))] + + def test_images_are_downloaded_hash_bound_without_changing_nested_packet(self): + self.client.acquire() + self.client.start_job("job", ZONE) + challenge = self.event(self.client.observe()) + image = Path(challenge["image_path"]) + self.assertEqual(image.parent, self.root / "client" / "images") + self.assertNotEqual(challenge["image_path"], challenge["observation"]["image_path"]) + self.assertEqual(storage.sha(storage.read_bytes(image, maximum=body_client.MAX_IMAGE)), challenge["observation"]["image_sha256"]) + self.assertEqual(storage.sha(storage.encoded(challenge["observation"])), challenge["observation_sha256"]) + self.assertTrue(any(event.get("path", "").startswith("/images/") for event in self.events())) + + def test_drop_after_native_effect_reconciles_without_resending(self): + proposal = self.proposal() + before = self.server.state()["bench"]["generation"] + self.proxy.arm("drop_after") + with self.assertRaises(body_client.Uncertain) as caught: + self.client.dispatch(proposal["proposal_id"]) + token = caught.exception.command_id + self.event(self.client.status()) + with self.assertRaises(body_client.Uncertain): + self.client.observe() + envelope = self.event(self.client.reconcile(token)) + self.assertEqual(envelope["state"], "completed") + self.assertEqual(envelope["result"]["outcome"], "executed") + self.assertEqual(self.server.state()["bench"]["generation"], before + 1) + forwards = [event for event in self.events() if event["kind"] == "forwarded" and event.get("command_id") == token] + self.assertEqual(len(forwards), 1) + manifest = storage.decode(storage.read_bytes(self.root / "client" / "client.json")) + self.assertEqual(manifest["commands"][token]["send_count"], 1) + self.assertFalse(manifest["pending_command_ids"]) + + def test_drop_before_remains_unknown_across_client_restart_until_lease_fence(self): + self.client.acquire(duration_s=.25) + self.proxy.arm("drop_before", operation="observe") + with self.assertRaises(body_client.Uncertain) as caught: + self.client.observe() + token = caught.exception.command_id + self.client.close() + restarted = body_client.BodyClient(self.root / "client", self.proxy.origin, timeout=.5) + self.clients.append(restarted) + self.assertEqual(restarted.status()["pending_command_ids"], [token]) + with self.assertRaises(body_client.Uncertain): + restarted.acquire() + with self.assertRaises(body_client.Uncertain): + restarted.reconcile(token) + time.sleep(.3) + envelope = self.event(restarted.reconcile(token)) + self.assertEqual(envelope["state"], "not_accepted") + self.assertFalse(restarted.status()["pending_command_ids"]) + self.assertFalse([event for event in self.events() if event["kind"] == "forwarded" and event.get("command_id") == token]) + restarted.acquire() + self.assertIn("challenge_id", restarted.observe()) + + def test_duplicate_real_socket_dispatch_has_one_native_effect(self): + proposal = self.proposal() + before = self.server.state()["bench"]["generation"] + self.proxy.arm("duplicate") + result = self.event(self.client.dispatch(proposal["proposal_id"])) + self.assertEqual(result["outcome"], "executed") + self.assertEqual(self.server.state()["bench"]["generation"], before + 1) + duplicates = [event for event in self.events() if event["kind"] == "duplicate_replies"] + self.assertEqual(len(duplicates), 1) + self.assertTrue(duplicates[0]["same_reply"]) + self.assertEqual(len([event for event in self.events() if event["kind"] == "forwarded" and + event.get("command_id") == duplicates[0]["command_id"]]), 2) + + def test_delays_before_and_after_forwarding_use_real_transport(self): + self.client.acquire() + for mode in ("delay_before", "delay_after"): + self.proxy.arm(mode, operation="observe", delay_s=.03) + challenge = self.event(self.client.observe()) + self.assertIn("challenge_id", challenge) + self.assertEqual(len([event for event in self.events() if event["kind"] in {"delay_before", "delay_after"}]), 2) + + def test_rotating_lease_and_exclusive_client_holder(self): + first = self.client.acquire() + second = self.client.acquire() + self.assertNotEqual(first["lease_id"], second["lease_id"]) + outsider = body_client.BodyClient(self.root / "outsider", self.proxy.origin) + self.clients.append(outsider) + with self.assertRaises(body_client.Refusal): + outsider.acquire() + self.assertEqual(self.client.status()["server_state"]["lease"]["lease_id"], second["lease_id"]) + + def test_unbound_error_after_native_success_stays_pending(self): + proposal = self.proposal() + original = self.server.command + + def broken_reply(request): + response = original(request) + if request["operation"] == "dispatch": + return {"state": "not_accepted", "reason": "injected unbound terminal transport error"} + return response + + self.server.command = broken_reply + with self.assertRaises(body_client.Uncertain) as caught: + self.client.dispatch(proposal["proposal_id"]) + self.server.command = original + token = caught.exception.command_id + self.assertEqual(self.server.state()["bench"]["generation"], 1) + with self.assertRaises(body_client.Uncertain): + self.client.start_job("other", ZONE) + reconciled = self.event(self.client.reconcile(token)) + self.assertEqual(reconciled["state"], "completed") + self.assertEqual(self.server.state()["bench"]["generation"], 1) + + def test_unknown_request_corruption_cannot_clear_or_resend(self): + self.client.acquire() + self.proxy.arm("drop_before", operation="observe") + with self.assertRaises(body_client.Uncertain) as caught: + self.client.observe() + token = caught.exception.command_id + path = self.root / "client" / "requests" / (token + ".json") + request = storage.decode(storage.read_bytes(path)) + request["epoch"] += 1 + path.write_bytes(storage.encoded(request)) + with self.assertRaises(body_client.Refusal): + self.client.reconcile(token) + self.assertFalse([event for event in self.events() if event["kind"] == "forwarded" and event.get("command_id") == token]) + + def test_malformed_proxy_headers_never_forward_or_admit_a_native_command(self): + proposal = self.proposal() + manifest = storage.decode(storage.read_bytes(self.root / "client" / "client.json")) + pinned = manifest["identity"] + request = {"protocol": body_client.PROTOCOL, "adapter_id": pinned["adapter_id"], "body_id": pinned["body_id"], + "source_sha256": pinned["source_sha256"], "client_id": manifest["client_id"], + "lease_id": manifest["lease"]["lease_id"], "epoch": manifest["lease"]["epoch"], + "command_id": "command-malformed-" + uuid.uuid4().hex, "operation": "dispatch", + "arguments": {"proposal_id": proposal["proposal_id"]}} + payload = storage.encoded(request) + host, port = body_client.endpoint(self.proxy.origin) + good = [("Host", f"{host}:{port}"), ("Content-Type", "application/json"), + *body_client.pinned_headers(pinned).items(), ("Content-Length", str(len(payload)))] + invalid = [good + [(name, dict(good)[name])] for name in + ("Host", "Content-Type", "X-Body-Id", "X-Adapter-Id", "X-Source-Sha256")] + invalid += [[(name, "foreign.example" if name == "Host" else value) for name, value in good], + [(name, "text/plain" if name == "Content-Type" else value) for name, value in good]] + before = {str(path.relative_to(self.root / "server" / "commands")): path.read_bytes() + for path in (self.root / "server" / "commands").rglob("*") if path.is_file()} + generation = self.server.state()["bench"]["generation"] + self.event({"rejected_command": request}) + for headers in invalid: + self.event({"header_attempt": headers}) + wire = ("POST /commands HTTP/1.1\r\n" + "".join(f"{name}: {value}\r\n" for name, value in headers) + + "\r\n").encode() + payload + with socket.create_connection((host, port), timeout=2) as connection: + connection.sendall(wire) + try: + reply = connection.recv(1024) + except ConnectionResetError: + reply = b"" + self.assertEqual(reply, b"") + after = {str(path.relative_to(self.root / "server" / "commands")): path.read_bytes() + for path in (self.root / "server" / "commands").rglob("*") if path.is_file()} + self.assertEqual(after, before) + self.assertEqual(self.server.state()["bench"]["generation"], generation) + self.assertFalse([event for event in self.events() if event["kind"] == "forwarded" and + event.get("command_id") == request["command_id"]]) + failures = [event for event in self.events() if event["kind"] == "transport_failure"] + self.assertEqual(len(failures), len(invalid)) + self.event({"malformed_header_attempts": len(invalid), "accepted_intents_unchanged": True, + "native_generation_unchanged": generation}) + + def test_exact_256_record_client_journal_reloads_and_refuses_send_257(self): + self.client.acquire() + self.client.observe() + manifest = storage.decode(storage.read_bytes(self.root / "client" / "client.json")) + original_id = next(iter(manifest["commands"])) + original_record = manifest["commands"][original_id] + original_request = storage.decode(storage.read_bytes(self.root / "client" / "requests" / (original_id + ".json"))) + response_path = self.root / "client" / "responses" / (original_id + "-" + original_record["response_sha256"] + ".json") + original_response = storage.decode(storage.read_bytes(response_path)) + for index in range(255): + token = "command-capacity-" + f"{index:032x}" + request = {**original_request, "command_id": token} + content = storage.encoded(request) + digest = storage.sha(content) + storage.create_file(self.root / "client" / "requests" / (token + ".json"), content) + response = {**original_response, "command_id": token, "request_sha256": digest} + reply = storage.encoded(response) + reply_sha = storage.sha(reply) + storage.create_file(self.root / "client" / "responses" / (token + "-" + reply_sha + ".json"), reply) + manifest["commands"][token] = {"request_sha256": digest, "state": "completed", "send_count": 1, + "response_sha256": reply_sha} + raw = storage.encoded(manifest) + self.assertGreater(len(raw), storage.MAX_BYTES) + self.assertLess(len(raw), 128 * 1024) + storage.persist(self.root / "client" / "client.json", manifest) + self.event({"fixture_kind": "256 local bound records derived from one real RPC; not 256 native effects", + "serialized_bytes": len(raw), "records": len(manifest["commands"])}) + self.client.close() + reopened = body_client.BodyClient(self.root / "client", self.proxy.origin) + self.clients.append(reopened) + self.assertEqual(reopened.status()["pending_command_ids"], []) + requests_before = len([event for event in self.events() if event["kind"] == "received"]) + with self.assertRaises(body_client.Refusal): + reopened.observe() + self.assertEqual(len([event for event in self.events() if event["kind"] == "received"]), requests_before) + self.assertEqual((self.root / "client" / "client.json").read_bytes(), raw) + reopened.close() + invalid = {"duplicate": raw.replace(b'"version": 1', b'"version": 1, "version": 1'), + "nonfinite": raw.replace(b'"sequence": 3', b'"sequence": NaN'), + "oversized": raw + b" " * (128 * 1024)} + # Find the actual current sequence rather than relying on fixture event order. + invalid["nonfinite"] = raw.replace(f'"sequence": {manifest["sequence"]}'.encode(), b'"sequence": NaN') + for label, malformed in invalid.items(): + storage.create_file(self.root / ("capacity-invalid-" + label + ".json"), malformed) + (self.root / "client" / "client.json").write_bytes(malformed) + with self.assertRaises(body_client.Refusal): + body_client.BodyClient(self.root / "client", self.proxy.origin) + (self.root / "client" / "client.json").write_bytes(raw) + with self.assertRaises(storage.Refusal): + storage.decode(b" " * (storage.MAX_BYTES + 1)) + + def test_endpoint_pin_and_lifetime_client_lock(self): + with self.assertRaises(body_client.Refusal): + body_client.BodyClient(self.root / "client", self.proxy.origin) + for origin in (self.server.origin + "/", "http://localhost:80", "http://127.0.0.1:80?x=1"): + with self.assertRaises(body_client.Refusal): + body_client.BodyClient(self.root / "other", origin) + self.client.close() + with self.assertRaises(body_client.Refusal): + body_client.BodyClient(self.root / "client", self.server.origin) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_body_contract.py b/tools/physical-workcell/test_body_contract.py new file mode 100644 index 0000000..c686a41 --- /dev/null +++ b/tools/physical-workcell/test_body_contract.py @@ -0,0 +1,242 @@ +"""Shared body transport with an alternate vocabulary; synthetic TCP, no hardware.""" +import copy +import json +import socket +import subprocess +import sys +import threading +import unittest +import uuid +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from urllib.parse import parse_qs, urlsplit + +import bench_body_contract +import body_contract as wire +import body_protocol +import order_protocol as storage +from persistent_body_client import PersistentBodyClient, Refusal, Uncertain + +BASE = Path(__file__).resolve().parent +SOURCE_FILES = ("body_contract.py", "bench_body_contract.py", "persistent_body_client.py", "body_protocol.py", + "order_protocol.py", "test_body_contract.py") + + +class CameraOperations: + """A fixture schema, not a camera driver or physical qualification.""" + @staticmethod + def validate_arguments(operation, arguments): + if operation != "aim": + raise Refusal("unsupported fixture operation") + storage.fields(arguments, ("yaw_deg",), "aim") + if not -30 <= storage.finite(arguments["yaw_deg"]) <= 30: + raise Refusal("fixture yaw exceeds bounds") + + @staticmethod + def validate_result(request, result, pinned): + storage.fields(result, ("device_id", "yaw_deg", "settled"), "aim result") + if result["device_id"] != pinned["body_id"] or result["yaw_deg"] != request["arguments"]["yaw_deg"] or type(result["settled"]) is not bool: + raise Refusal("fixture aim result binding changed") + + @staticmethod + def is_unknown(result): + return isinstance(result, dict) and result.get("settled") is False + + +class CameraFixture: + def __init__(self): + self.identity = {"protocol": wire.PROTOCOL, "adapter_id": "camera-fixture-adapter", "body_id": "camera-fixture", + "source_sha256": "a" * 64, "epoch": 1, "simulation": True} + self.effects, self.reads, self.commands = 0, 0, [] + self.results = {} + self.drop, self.settled, self.corrupt = False, True, False + owner = self + + class Handler(BaseHTTPRequestHandler): + def log_message(self, *_): + pass + + def reply(self, value): + content = storage.encoded(value) + self.send_response(200) + self.send_header("Content-Length", str(len(content))) + self.send_header("Connection", "close") + self.end_headers() + self.wfile.write(content) + + def do_GET(self): + path = urlsplit(self.path) + if path.path == "/identity": + self.reply(owner.identity) + elif path.path.startswith("/commands/"): + owner.reads += 1 + query = {k: v[0] for k, v in parse_qs(path.query).items()} + query["epoch"] = int(query["epoch"]) + wire.validate_reconcile(query) + token = path.path.removeprefix("/commands/") + value = copy.deepcopy(owner.results[token]) + if owner.corrupt: + value["request_sha256"] = "b" * 64 + self.reply(value) + else: + self.reply({"identity": owner.identity, "state": "ready"}) + + def do_POST(self): + value = storage.decode(self.rfile.read(int(self.headers["Content-Length"]))) + if self.path == "/lease": + duration = wire.validate_lease_request(value) + self.reply({"client_id": value["client_id"], "lease_id": "1" * 32, "epoch": 1, "duration_s": duration}) + return + wire.validate_command(value) + CameraOperations.validate_arguments(value["operation"], value["arguments"]) + owner.commands.append(value) + owner.effects += 1 + result = {"device_id": owner.identity["body_id"], "yaw_deg": value["arguments"]["yaw_deg"], "settled": owner.settled} + envelope = {**{k: owner.identity[k] for k in wire.PIN_KEYS}, "command_id": value["command_id"], + "request_sha256": wire.command_digest(value), "state": "completed", "result": result, + "reason": None, "receipt_scope": wire.RECEIPT_SCOPE, "recorded_epoch": 1} + owner.results[value["command_id"]] = envelope + if owner.drop: + self.connection.shutdown(socket.SHUT_RDWR) + self.connection.close() + else: + self.reply(envelope) + + self.http = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + self.thread = threading.Thread(target=self.http.serve_forever, kwargs={"poll_interval": .05}, daemon=True) + self.thread.start() + self.origin = f"http://127.0.0.1:{self.http.server_port}" + + def close(self): + self.http.shutdown() + self.http.server_close() + self.thread.join(2) + + +class BodyContractTests(unittest.TestCase): + def setUp(self): + self.root = BASE / "output" / ("sim14-body-contract-" + uuid.uuid4().hex[:12]) + self.root.mkdir(mode=0o700) + self.sources = {name: storage.sha(storage.read_bytes(BASE / name)) for name in SOURCE_FILES} + self.service = CameraFixture() + self.clients = [] + + def tearDown(self): + for client in self.clients: + client.close() + self.service.close() + result = self._outcome.result + failed = any(test is self for test, _ in (*result.failures, *result.errors)) + unchanged = self.sources == {name: storage.sha(storage.read_bytes(BASE / name)) for name in SOURCE_FILES} + storage.create_file(self.root / "conformance.json", storage.encoded({"test_id": self.id(), "passed": not failed and unchanged, + "sources": self.sources, "sources_unchanged": unchanged, + "scenario_kind": "synthetic_alternate_vocabulary_actual_tcp", "effects": self.service.effects, + "receipt_reads": self.service.reads, "command_ids": [v["command_id"] for v in self.service.commands]})) + self.assertTrue(unchanged, "shared contract source changed during case") + + def client(self, root=None): + client = PersistentBodyClient(root or self.root / "client", self.service.origin, CameraOperations(), timeout=.5) + self.clients.append(client) + return client + + def test_alternate_command_recovers_after_reply_loss_and_client_restart_without_replay(self): + client = self.client() + client.acquire() + self.service.drop = True + with self.assertRaises(Uncertain) as caught: + client.command("aim", {"yaw_deg": 15}) + token = caught.exception.command_id + client.close() + client = self.client() + with self.assertRaises(Uncertain): + client.command("aim", {"yaw_deg": -15}) + receipt = client.reconcile(token) + self.assertEqual(receipt["result"]["yaw_deg"], 15) + self.assertEqual(self.service.effects, 1) + self.assertEqual(self.service.reads, 1) + manifest = client._load() + self.assertEqual(manifest["commands"][token]["send_count"], 1) + self.assertFalse(manifest["pending_command_ids"]) + + def test_unknown_lower_proof_blocks_completed_outer_receipt_until_exact_proof(self): + client = self.client() + client.acquire() + self.service.settled = False + with self.assertRaises(Uncertain) as caught: + client.command("aim", {"yaw_deg": 15}) + token = caught.exception.command_id + with self.assertRaises(Uncertain): + client.acquire() + with self.assertRaises(Uncertain): + client.reconcile(token) + self.service.results[token]["result"]["settled"] = True + self.assertEqual(client.reconcile(token)["state"], "completed") + self.assertEqual(self.service.effects, 1) + self.assertEqual(self.service.reads, 2) + + def test_unbound_historical_receipt_does_not_release_unknown_command(self): + client = self.client() + client.acquire() + self.service.drop = True + with self.assertRaises(Uncertain) as caught: + client.command("aim", {"yaw_deg": 15}) + self.service.corrupt = True + with self.assertRaises(Uncertain): + client.reconcile(caught.exception.command_id) + self.assertEqual(client._load()["pending_command_ids"], [caught.exception.command_id]) + self.assertEqual(self.service.effects, 1) + + def test_required_operation_schema_refuses_before_send_or_journal(self): + with self.assertRaises(Refusal): + PersistentBodyClient(self.root / "invalid", self.service.origin, object()) + self.assertFalse((self.root / "invalid").exists()) + client = self.client() + client.acquire() + for operation, args in (("move", {}), ("aim", {"yaw_deg": True}), ("aim", {"yaw_deg": 31}), ("aim", {"yaw_deg": 15, "extra": 1})): + with self.assertRaises(Refusal): + client.command(operation, args) + self.assertEqual(self.service.effects, 0) + self.assertEqual(client._load()["commands"], {}) + + def test_shared_transport_import_has_no_simulator_dependency(self): + code = "import json,sys; import persistent_body_client; print(json.dumps(sorted(sys.modules)))" + result = subprocess.run([sys.executable, "-c", code], cwd=BASE, capture_output=True, text=True, check=True) + modules = json.loads(result.stdout) + self.assertFalse(any(m in modules for m in ("bench", "agent_bench", "bench_body_contract", "mujoco", "numpy"))) + + def test_shared_envelope_refuses_wrong_identity_epoch_state_and_schema(self): + client = self.client() + client.acquire() + client.command("aim", {"yaw_deg": 15}) + request = self.service.commands[0] + good = self.service.results[request["command_id"]] + for key, value in (("adapter_id", "wrong"), ("request_sha256", "b" * 64), ("recorded_epoch", True), + ("recorded_epoch", 2), ("state", "success"), ("receipt_scope", "current_pose"), ("result", None)): + with self.assertRaises(Refusal, msg=key): + wire.validate_envelope({**good, key: value}, request, wire.command_digest(request)) + with self.assertRaises(Refusal): + wire.validate_envelope({**good, "extra": True}, request, wire.command_digest(request)) + self.assertFalse(wire.terminal({"state": "not_seen"}, False)) + self.assertFalse(wire.terminal({"state": "completed"}, True)) + with self.assertRaises(Refusal): + wire.terminal({"state": "completed"}, 0) + + def test_bench_schema_still_refuses_alternate_operations_and_unsafe_motion(self): + request = {**{k: self.service.identity[k] for k in wire.PIN_KEYS}, "client_id": "client", "lease_id": "lease", + "epoch": 1, "command_id": "command", "operation": "aim", "arguments": {"yaw_deg": 15}} + wire.validate_command(request) + with self.assertRaises(Refusal): + body_protocol.validate_command(request) + # General tabletop zones must not relax the bench's reachable job region. + for zone in ({"x_min": -.1, "x_max": .1, "y_min": -.03, "y_max": .03}, + {"x_min": .04, "x_max": .06, "y_min": -.025, "y_max": .025}): + with self.assertRaises(Refusal): + bench_body_contract.validate_arguments("start_job", {"job_id": "job", "target_zone": zone}) + for x, y in ((.181, 0), (0, .151), (True, 0), (float("nan"), 0)): + with self.assertRaises(Refusal): + bench_body_contract.validate_arguments("propose", {"challenge_id": "challenge", "action": "move", + "intent": "bounded motion", "target_x": x, "target_y": y}) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_body_network.py b/tools/physical-workcell/test_body_network.py new file mode 100644 index 0000000..0f790fa --- /dev/null +++ b/tools/physical-workcell/test_body_network.py @@ -0,0 +1,241 @@ +"""Actual TCP/native conformance; no model calls or uncertain effect retries.""" +import concurrent.futures +import json +import time +import unittest +import uuid +from http.client import HTTPConnection +from unittest import mock +from urllib.parse import urlencode, urlsplit + +import body_network as body +import body_protocol as wire +import order_protocol as storage + +ZONE = {"x_min": .035, "x_max": .105, "y_min": -.025, "y_max": .025} + + +class BodyNetworkTests(unittest.TestCase): + def setUp(self): + self.root = body.BASE / "output" / ("body-net-test-" + uuid.uuid4().hex[:10]) + body.initialize(self.root, diagnostic_enabled=True) + self.service = body.BodyService(self.root).start() + self.identity = self.get("/identity") + self.headers = {"X-Body-Id": self.identity["body_id"], "X-Adapter-Id": self.identity["adapter_id"], + "X-Source-Sha256": self.identity["source_sha256"]} + self.lease = self.post("/lease", {"client_id": "diagnostic-client", "expected_epoch": self.identity["epoch"], "duration_s": 30}) + self.events = [] + + def tearDown(self): + if self.service: + self.service.close() + failed = any(test is self for test, _ in (*self._outcome.result.failures, *self._outcome.result.errors)) + storage.create_file(self.root / "conformance.json", storage.encoded( + {"test_id": self.id(), "passed": not failed, "events": self.events})) + + def http(self, method, path, value=None, headers=None): + parsed = urlsplit(self.service.origin) + connection = HTTPConnection(parsed.hostname, parsed.port, timeout=10) + request_headers = getattr(self, "headers", {}) | (headers or {}) + content = None if value is None else storage.encoded(value) + if content is not None: + request_headers["Content-Type"] = "application/json" + connection.request(method, path, body=content, headers=request_headers) + response = connection.getresponse() + content = response.read() + connection.close() + result = content if path.startswith("/images/") else json.loads(content) + if hasattr(self, "events"): + self.events.append({"method": method, "path": path, "status": response.status, + "response": "PNG" if isinstance(result, bytes) else result}) + return response.status, result + + def get(self, path): + return self.http("GET", path)[1] + + def post(self, path, value): + return self.http("POST", path, value)[1] + + def request(self, operation, arguments, command_id=None): + return {**{k: self.identity[k] for k in ("protocol", "adapter_id", "body_id", "source_sha256")}, + **{k: self.lease[k] for k in ("client_id", "lease_id", "epoch")}, + "command_id": command_id or "command-" + uuid.uuid4().hex, + "operation": operation, "arguments": arguments} + + def command(self, operation, arguments): + request = self.request(operation, arguments) + reply = self.post("/commands", request) + self.assertEqual(reply["state"], "completed", reply) + return request, reply["result"] + + def start_and_propose(self): + self.command("start_job", {"job_id": "test-job", "target_zone": ZONE}) + _, challenge = self.command("observe", {}) + _, proposal = self.command("propose", {"challenge_id": challenge["challenge_id"], "action": "move", + "intent": "Diagnostic fixture", "target_x": -.25, "target_y": 0}) + return challenge, proposal + + def query(self, request): + return "/commands/" + request["command_id"] + "?" + urlencode({ + **{k: request[k] for k in ("client_id", "lease_id", "epoch")}, "request_sha256": wire.command_digest(request)}) + + def test_image_crosses_tcp_with_exact_challenge_sha(self): + self.command("start_job", {"job_id": "test-job", "target_zone": ZONE}) + _, challenge = self.command("observe", {}) + status, image = self.http("GET", "/images/" + challenge["challenge_id"]) + self.assertEqual(status, 200) + self.assertEqual(storage.sha(image), challenge["observation"]["image_sha256"]) + self.assertNotIn("block_center_xy", json.dumps(challenge)) + + def test_exact_concurrent_duplicate_executes_one_native_effect(self): + _, proposal = self.start_and_propose() + request = self.request("dispatch", {"proposal_id": proposal["proposal_id"]}) + with concurrent.futures.ThreadPoolExecutor(2) as pool: + replies = list(pool.map(lambda _: self.post("/commands", request), range(2))) + self.assertEqual(replies[0], replies[1]) + self.assertEqual(replies[0]["result"]["outcome"], "executed") + self.assertEqual(self.get("/state")["bench"]["generation"], 1) + self.assertEqual(self.get(self.query(request)), replies[0]) + + def test_conflicting_duplicate_refuses_without_second_effect(self): + _, proposal = self.start_and_propose() + request = self.request("dispatch", {"proposal_id": proposal["proposal_id"]}) + first = self.post("/commands", request) + conflicting = dict(request, operation="observe", arguments={}) + self.assertEqual(self.post("/commands", conflicting)["state"], "refused") + self.assertEqual(self.get(self.query(request)), first) + self.assertEqual(self.get("/state")["bench"]["generation"], 1) + + def test_expired_and_rotated_leases_fence_new_admission(self): + first = self.request("observe", {}) + self.lease = self.post("/lease", {"client_id": "diagnostic-client", "expected_epoch": self.identity["epoch"], "duration_s": .05}) + self.assertEqual(self.post("/commands", first)["state"], "not_accepted") + late = self.request("observe", {}) + time.sleep(.07) + self.assertEqual(self.post("/commands", late)["state"], "not_accepted") + self.assertEqual(self.get(self.query(late))["state"], "not_accepted") + self.assertEqual(self.service.records, {}) + + def test_lease_rechecked_after_slow_validation_before_intent(self): + self.lease = self.post("/lease", {"client_id": "diagnostic-client", "expected_epoch": self.identity["epoch"], "duration_s": .05}) + request = self.request("observe", {}) + original = self.service._known_body + def slow(): + time.sleep(.08) + return original() + with mock.patch.object(self.service, "_known_body", side_effect=slow): + reply = self.post("/commands", request) + self.assertEqual(reply["state"], "not_accepted") + self.assertEqual(self.service.records, {}) + + def test_identity_source_origin_and_exclusive_lease_refuse(self): + request = self.request("observe", {}) + self.assertEqual(self.http("POST", "/commands", request, {"Origin": self.service.origin})[0], 409) + for key, value in (("adapter_id", "foreign"), ("body_id", "foreign"), ("source_sha256", "0" * 64)): + self.assertEqual(self.post("/commands", dict(request, **{key: value}))["state"], "not_accepted") + reply = self.post("/lease", {"client_id": "other-client", "expected_epoch": self.identity["epoch"], "duration_s": 30}) + self.assertEqual(reply["state"], "not_accepted") + self.assertEqual(self.service.records, {}) + + def test_known_result_survives_restart_and_old_lease_is_fenced(self): + _, proposal = self.start_and_propose() + request = self.request("dispatch", {"proposal_id": proposal["proposal_id"]}) + first = self.post("/commands", request) + self.service.close() + self.service = body.BodyService(self.root).start() + new_identity = self.get("/identity") + self.assertEqual(new_identity["body_id"], self.identity["body_id"]) + self.assertEqual(new_identity["adapter_id"], self.identity["adapter_id"]) + self.assertGreater(new_identity["epoch"], self.identity["epoch"]) + self.assertEqual(self.get(self.query(request)), first) + self.assertEqual(self.post("/commands", request), first) + late = self.request("observe", {}) + self.assertEqual(self.post("/commands", late)["state"], "not_accepted") + self.assertEqual(self.get("/state")["bench"]["generation"], 1) + + def test_read_only_recovery_requires_indexed_hash_verified_lower_receipt(self): + _, proposal = self.start_and_propose() + request = self.request("dispatch", {"proposal_id": proposal["proposal_id"]}) + # A precise diagnostic interruption after lower receipt, before transport terminal. + with mock.patch.object(self.service, "_terminal", side_effect=OSError("fixture terminal storage failure")): + reply = self.post("/commands", request) + self.assertEqual(reply["state"], "unknown") + self.assertEqual(self.get("/state")["state"], "unknown") + self.service.close() + with mock.patch.object(body.agent.AgentBench, "dispatch", side_effect=AssertionError("recovery replayed dispatch")): + self.service = body.BodyService(self.root).start() + recovered = self.get(self.query(request)) + self.assertEqual(recovered["state"], "completed") + self.assertEqual(recovered["result"]["outcome"], "executed") + self.assertEqual(self.get("/state")["bench"]["generation"], 1) + + def test_terminal_pointer_persist_failure_keeps_cache_and_disk_unknown(self): + _, proposal = self.start_and_propose() + request = self.request("dispatch", {"proposal_id": proposal["proposal_id"]}) + directory = self.service._directory(request["command_id"]) + original = storage.persist + def fail_pointer(path, value): + if path == directory / "intent.json" and value["result_sha256"] is not None: + self.assertTrue((directory / "result.json").exists()) + raise OSError("fixture terminal pointer persistence failure") + return original(path, value) + with mock.patch.object(storage, "persist", side_effect=fail_pointer): + reply = self.post("/commands", request) + self.assertEqual(reply["state"], "unknown") + durable = storage.decode(storage.read_bytes(directory / "intent.json")) + cached_pointer = self.service.records[request["command_id"]]["result_sha256"] + self.events.append({"kind": "partial_terminal_persistence", "cached_result_sha256": cached_pointer, + "durable_result_sha256": durable["result_sha256"]}) + self.assertIsNone(durable["result_sha256"]) + self.assertEqual(self.get("/state")["state"], "unknown") + blocked = self.post("/commands", self.request("observe", {})) + self.assertEqual(blocked["state"], "not_accepted") + self.assertEqual(self.get("/state")["bench"]["generation"], 1) + self.service.close() + with mock.patch.object(body.agent.AgentBench, "dispatch", side_effect=AssertionError("recovery replayed dispatch")): + self.service = body.BodyService(self.root).start() + recovered = self.get(self.query(request)) + self.assertEqual(recovered["state"], "completed") + self.assertEqual(recovered["result"]["outcome"], "executed") + self.assertEqual(self.get("/state")["bench"]["generation"], 1) + self.assertIsNone(cached_pointer, "cached terminal pointer must change only after durable commit") + + def test_accepted_intent_without_lower_receipt_blocks_restart(self): + _, proposal = self.start_and_propose() + request = self.request("dispatch", {"proposal_id": proposal["proposal_id"]}) + directory = self.service._directory(request["command_id"]) + directory.mkdir(mode=0o700) + storage.persist(directory / "intent.json", {"request": request, "request_sha256": wire.command_digest(request), "result_sha256": None}) + self.service.close() + self.service = body.BodyService(self.root).start() + self.assertEqual(self.get(self.query(request))["state"], "unknown") + new = self.get("/identity") + self.lease = self.post("/lease", {"client_id": "diagnostic-client", "expected_epoch": new["epoch"], "duration_s": 30}) + self.identity = new + self.assertEqual(self.post("/commands", self.request("observe", {}))["state"], "not_accepted") + self.assertEqual(self.get("/state")["bench"]["generation"], 0) + + def test_missing_command_not_seen_then_not_accepted_after_lease_expiry(self): + self.lease = self.post("/lease", {"client_id": "diagnostic-client", "expected_epoch": self.identity["epoch"], "duration_s": .05}) + request = self.request("observe", {}) + self.assertEqual(self.get(self.query(request))["state"], "not_seen") + time.sleep(.06) + self.assertEqual(self.get(self.query(request))["state"], "not_accepted") + + def test_stale_observation_is_retained_known_dispatch_refusal(self): + _, proposal = self.start_and_propose() + self.command("disturb", {"operation_id": "diagnostic-force", "force_x_n": -.4, "steps": 50}) + _, result = self.command("dispatch", {"proposal_id": proposal["proposal_id"]}) + self.assertEqual(result["outcome"], "refused") + self.assertIn("stale", result["reason"]) + self.assertEqual(self.get("/state")["bench"]["generation"], 1) + + def test_invalid_arguments_never_create_intent(self): + for arguments in ({"job_id": "job", "target_zone": dict(ZONE, x_min=True)}, {"job_id": "../bad", "target_zone": ZONE}): + self.assertEqual(self.http("POST", "/commands", self.request("start_job", arguments))[0], 409) + self.assertEqual(self.http("POST", "/commands", dict(self.request("observe", {}), success=True))[0], 409) + self.assertEqual(self.service.records, {}) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_codex_policy.py b/tools/physical-workcell/test_codex_policy.py new file mode 100644 index 0000000..d7d9ad8 --- /dev/null +++ b/tools/physical-workcell/test_codex_policy.py @@ -0,0 +1,101 @@ +"""Policy conformance uses a labelled fake CLI; it is not live model evidence.""" + +import json +import signal +import subprocess +import sys +import tempfile +import threading +import unittest +from pathlib import Path +from unittest.mock import patch + +import codex_policy as policy +import operator_runtime + + +class PolicyTests(unittest.TestCase): + def test_delayed_guardian_cleanup_can_publish_before_forced_kill(self): + class Guardian: + pid = 12345 + closed = False + waits = 0 + + def poll(self): + return None + + def wait(self, timeout): + # Group retirement plus descendant proof can exceed ten seconds. + self.waits += 1 + if self.waits == 1 and timeout < 12: + raise subprocess.TimeoutExpired("labelled guardian", timeout) + if self.waits == 1: + self.closed = True + return 0 + + process = Guardian() + with patch.object(policy.os, "killpg") as signals: + policy.terminate(process) + self.assertTrue(process.closed) + self.assertEqual(signals.call_args_list, [unittest.mock.call(process.pid, signal.SIGTERM)]) + + def test_closed_action_schema_finite_targets_and_no_success_flag(self): + valid = {"action": "move", "target_x": .01, "target_y": 0, "intent": "Push into the task region."} + self.assertEqual(policy.validate_action(valid), valid) + for changed in ({**valid, "target_x": True}, {**valid, "target_x": float("nan")}, + {**valid, "target_x": .5}, {**valid, "success": True}, + {**valid, "action": "measure"}, {**valid, "intent": ""}): + with self.subTest(changed=changed), self.assertRaises((policy.PolicyUnavailable, ValueError)): + policy.validate_action(changed) + + def test_usage_optional_and_any_tool_event_refuses(self): + self.assertIsNone(policy.usage_from_events(b'{}\nnull\n')) + usage = {"input_tokens": 20, "output_tokens": 10, "cached_input_tokens": 2} + self.assertEqual(policy.usage_from_events(json.dumps({"type": "turn.completed", "usage": usage}).encode()), usage) + for message in ("Code Mode is unavailable because code-mode host is disabled.", + "Skill descriptions were shortened to fit the skills context budget."): + self.assertIsNone(policy.usage_from_events(json.dumps({"type": "item.completed", "item": {"type": "error", "message": message}}).encode())) + with self.assertRaises(policy.PolicyUnavailable): + policy.usage_from_events(b'{"item":{"type":"error","message":"unknown failure"}}') + for kind in ("command_execution", "file_change", "mcp_tool_call", "web_search", "tool_call", "plan", "image_view"): + with self.subTest(kind=kind), self.assertRaises(policy.PolicyUnavailable): + policy.usage_from_events(json.dumps({"type": "item.completed", "item": {"type": kind}}).encode()) + + def test_fake_cli_bound_image_schema_tool_isolation_and_reservation(self): + with tempfile.TemporaryDirectory(prefix="policy-test-", dir=policy.protocol.OUTPUT) as temp: + root = Path(temp) + operator_runtime.initialize(root / "operator") + target = policy.agent_bench.AgentBench(root / "operator/agent") + challenge = target.observe() + fake = root / "fake-cli" + fake.write_text("#!" + sys.executable + "\n" + '''import json,sys +from pathlib import Path +args=sys.argv[1:] +assert '--ignore-user-config' in args and '--ephemeral' in args +assert args[args.index('--sandbox')+1]=='read-only' +assert 'shell_tool' in args and 'tools.view_image=false' in args +assert args[args.index('shell_snapshot')-1]=='--disable' +assert 'web_search="disabled"' in args +png=Path(args[args.index('--image')+1]).read_bytes() +assert png.startswith(b'\\x89PNG') +prompt=sys.stdin.read() +assert 'block_center_xy' not in prompt and 'block_xyz' not in prompt +out=Path(args[args.index('--output-last-message')+1]) +out.write_text(json.dumps({'action':'stop','target_x':None,'target_y':None,'intent':'Labelled fake policy.'})) +print(json.dumps({'type':'turn.completed','usage':{'input_tokens':20,'output_tokens':10}})) +''') + fake.chmod(0o700) + call = root / "call" + call.mkdir(mode=0o700) + (call / "reservation.json").write_text("{}") + with patch.object(policy, "preflight", return_value={"available": True, "cli_version": policy.EXPECTED_CLI}), patch.object(policy.shutil, "which", return_value=str(fake)): + action, metadata = policy.decide(challenge, [{"block_center_xy": [1, 2], "block_xyz": [3, 4, 5]}], call, threading.Event()) + self.assertEqual(action["action"], "stop") + self.assertTrue(metadata["process_exit_observed"]) + self.assertEqual(metadata["usage"]["input_tokens"], 20) + with self.assertRaises(policy.PolicyUnavailable): + policy.decide(challenge, [], call, threading.Event()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_controller.py b/tools/physical-workcell/test_controller.py new file mode 100644 index 0000000..ee83782 --- /dev/null +++ b/tools/physical-workcell/test_controller.py @@ -0,0 +1,742 @@ +"""SIM4 actual-process development conformance; no qualification-case discovery. + +Each test keeps private inputs, public replies and owned server logs under output. +Offline retained-write faults are labeled separately from live process termination. +""" + +import importlib.util +import json +import os +import signal +import socket +import subprocess +import sys +import tempfile +import time +import unittest +import uuid +from pathlib import Path +from unittest import mock + +BASE = Path(__file__).resolve().parent +CONTROLLER = BASE / "controller.py" +TERMINAL = {"executed", "staged", "cancelled", "refused", "unknown"} + + +class SocketPathTests(unittest.TestCase): + def setUp(self): + spec = importlib.util.spec_from_file_location("controller_socket_dev", CONTROLLER) + self.controller = importlib.util.module_from_spec(spec) + spec.loader.exec_module(self.controller) + self.temporary = tempfile.TemporaryDirectory(prefix="", dir=self.controller.SOCKET_PARENT) + self.addCleanup(self.temporary.cleanup) + self.parent = Path(self.temporary.name).resolve() + self.patch = mock.patch.object(self.controller, "SOCKET_PARENT", self.parent, create=True) + self.patch.start() + self.addCleanup(self.patch.stop) + self.directory = self.parent / f"fw-c-{os.getuid()}" + self.control = BASE / "output" / ("long-checkout-" + "a" * 180) / "controller" + + def test_long_control_path_has_stable_private_bindable_socket(self): + path = self.controller.socket_path(self.control, create=True) + self.assertEqual(path, self.controller.socket_path(self.control)) + self.assertEqual(path.parent, self.directory) + self.assertLess(len(os.fsencode(path)), 104) + self.assertEqual(self.directory.stat().st_mode & 0o777, 0o700) + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as listener: + listener.bind(str(path)) + os.chmod(path, 0o600) + listener.listen(1) + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as caller: + caller.connect(str(path)) + connection, _ = listener.accept() + connection.close() + path.unlink() + + def test_control_identities_have_distinct_full_digest_names(self): + one = self.controller.socket_path(self.control, create=True) + two = self.controller.socket_path(self.control.parent / "other") + self.assertNotEqual(one, two) + self.assertEqual(one.name, self.controller.body.sha(str(self.control).encode()) + ".sock") + + def test_missing_runtime_directory_is_not_created_by_read(self): + with self.assertRaises(FileNotFoundError): + self.controller.socket_path(self.control) + self.assertFalse(self.directory.exists()) + + def test_runtime_directory_symlink_is_refused_without_touching_target(self): + target = self.parent / "target" + target.mkdir(mode=0o700) + self.directory.symlink_to(target, target_is_directory=True) + with self.assertRaises(self.controller.Refusal): + self.controller.socket_path(self.control, create=True) + self.assertTrue(self.directory.is_symlink()) + self.assertEqual(list(target.iterdir()), []) + + def test_runtime_directory_regular_file_is_refused_without_repair(self): + self.directory.write_bytes(b"unrelated") + with self.assertRaises(self.controller.Refusal): + self.controller.socket_path(self.control, create=True) + self.assertEqual(self.directory.read_bytes(), b"unrelated") + + def test_runtime_directory_unsafe_mode_is_refused_without_chmod(self): + self.directory.mkdir(mode=0o700) + self.directory.chmod(0o755) + with self.assertRaises(self.controller.Refusal): + self.controller.socket_path(self.control, create=True) + self.assertEqual(self.directory.stat().st_mode & 0o777, 0o755) + + def test_runtime_directory_foreign_owner_is_refused(self): + self.directory.mkdir(mode=0o700) + original = Path.lstat + + def foreign(path): + value = original(path) + if path == self.directory: + fields = list(value) + fields[4] = os.getuid() + 1 + return os.stat_result(fields) + return value + + with mock.patch.object(Path, "lstat", foreign), self.assertRaises(self.controller.Refusal): + self.controller.socket_path(self.control, create=True) + + def test_shared_parent_without_sticky_bit_is_refused(self): + self.parent.chmod(0o777) + with self.assertRaises(self.controller.Refusal): + self.controller.socket_path(self.control, create=True) + self.assertFalse(self.directory.exists()) + + def test_symlinked_socket_parent_is_refused(self): + link = self.parent / "alias" + link.symlink_to(self.parent, target_is_directory=True) + with mock.patch.object(self.controller, "SOCKET_PARENT", link), self.assertRaises(self.controller.Refusal): + self.controller.socket_path(self.control, create=True) + self.assertFalse(self.directory.exists()) + + def test_symlink_winning_directory_creation_race_is_refused(self): + target = self.parent / "race-target" + target.mkdir(mode=0o700) + original = Path.mkdir + + def replace(path, *args, **kwargs): + if path == self.directory: + path.symlink_to(target, target_is_directory=True) + raise FileExistsError("another actor created the directory") + return original(path, *args, **kwargs) + + with mock.patch.object(Path, "mkdir", replace), self.assertRaises(self.controller.Refusal): + self.controller.socket_path(self.control, create=True) + self.assertTrue(self.directory.is_symlink()) + self.assertEqual(list(target.iterdir()), []) + + +class ControllerProcessTests(unittest.TestCase): + def setUp(self): + spec = importlib.util.spec_from_file_location("controller_dev_runner", BASE / "episode_runner.py") + self.runner = importlib.util.module_from_spec(spec) + spec.loader.exec_module(self.runner) + self.body = self.runner.body + self.work = self.body.directory(self.body.OUTPUT / ("c4" + uuid.uuid4().hex[:6]), create=True) + self.root = self.body.directory(self.work / "r", create=True) + self.control = self.body.directory(self.work / "c", create=True) + self.processes = [] + self.handles = [] + self.evidence = [] + self.incarnation = None + self.case = {"version": 1, "case_id": "controller-private-development", "max_actions": 8, + "scene": {"block_xy": [-.155, .006], "illumination_scale": 1, + "occlusion_fraction": 0, "camera_offset_x_m": 0}, + "work_order": {"version": 1, "order_id": "controller-development-order", + "project": {"namespace": "test", "project_id": "controller-development"}, + "part_id": "block", "target_zone": { + "x_min": .18, "x_max": .26, "y_min": -.04, "y_max": .04}}} + self.case_path = self.work / "case.json" + self.initialized = False + + def tearDown(self): + for process in self.processes: + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=4) + except subprocess.TimeoutExpired: + process.kill() + process.wait(timeout=4) + for handle in self.handles: + handle.close() + result = self._outcome.result + failed = any(test is self for test, _ in (*result.failures, *result.errors)) + self.body.create_file(self.work / "conformance.json", self.body.encoded({ + "test_id": self.id(), "passed": not failed, "scenario_kind": "actual_process", + "events": self.evidence, "private_run_dir": str(self.work)})) + + def init(self): + content = self.body.encoded(self.case) + self.body.create_file(self.case_path, content) + self.runner.initialize(self.root, str(self.case_path), self.body.sha(content)) + self.initialized = True + + def serve(self, *, delay=.15, pace=0, control=None, fault_script=None): + if not self.initialized: + self.init() + control = control or self.control + logfile = open(self.work / f"server-{len(self.processes)}.log", "xb") # noqa: SIM115 - owned until tearDown + os.chmod(logfile.name, 0o600) + self.handles.append(logfile) + process = subprocess.Popen([sys.executable, str(fault_script or CONTROLLER), "serve", "--run-dir", str(self.root), + "--controller-dir", str(control), "--dispatch-delay-s", str(delay), + "--pace-s", str(pace)], stdout=logfile, stderr=subprocess.STDOUT) + self.processes.append(process) + deadline = time.monotonic() + 10 + while time.monotonic() < deadline: + if process.poll() is not None: + self.fail(f"controller startup exited {process.returncode}; private logs: {self.work}") + binding = control / "controller-binding.json" + if binding.exists() and self.socket_location(control).exists(): + reply = self.client("health", control=control, allow_failure=True) + if reply.get("phase") != "refused": + self.incarnation = reply["current_incarnation"] + return process, reply + time.sleep(.03) + self.fail(f"controller startup timed out; private logs: {self.work}") + + def socket_location(self, control=None): + control = control or self.control + binding = self.body.decoded(self.body.read_bytes(control / "controller-binding.json")) + path = Path(binding["socket_path"]) + self.assertEqual(path.parent, Path("/private/tmp" if sys.platform == "darwin" else "/tmp") / f"fw-c-{os.getuid()}") + self.assertLess(len(os.fsencode(path)), 104) + return path + + def client(self, command, *args, control=None, allow_failure=False): + argv = [sys.executable, str(CONTROLLER), command, "--controller-dir", str(control or self.control), *map(str, args)] + completed = subprocess.run(argv, capture_output=True, text=True, timeout=12, check=False) + try: + reply = json.loads(completed.stdout) + except json.JSONDecodeError as exc: + self.fail(f"non-JSON public reply: {completed.stdout!r}; stderr={completed.stderr!r}: {exc}") + self.evidence.append({"command": command, "args": list(map(str, args)), "exit_code": completed.returncode, "reply": reply}) + self.no_oracle(reply) + if not allow_failure: + self.assertEqual(completed.returncode, 0, reply) + return reply + + def no_oracle(self, reply): + serialized = json.dumps(reply) + for forbidden in ("block_xyz", "pusher_xyz", "physical_condition_verified", "block_center_in_zone", + "qpos", "xpos", "illumination_scale", "occlusion_fraction", "camera_offset_x_m"): + self.assertNotIn(forbidden, serialized) + + def observe(self): + reply = self.client("observe") + self.assertEqual(reply["phase"], "ready", reply) + return reply["observation"] + + def proposal(self, packet, token, *, complete=False, target_x=None): + proposal = {"version": 1, "run_dir": str(self.root), "generation": packet["generation"], + "image_sha256": packet["image_sha256"], "token": token, + "block_box": packet["perception"]["block_box"], "decision": "complete" if complete else "move"} + if not complete: + proposal["target"] = {"x": packet["perception"]["pusher_center_xy"][0] + .10 if target_x is None else target_x, "y": 0} + content = self.body.encoded(proposal) + path = self.work / f"proposal-{token}.json" + self.body.create_file(path, content) + return path, self.body.sha(content), proposal + + def submit(self, operation_id, proposal, *, allow_failure=False): + path, digest = proposal[:2] + return self.client("submit", "--operation-id", operation_id, "--proposal-file", path, + "--proposal-sha256", digest, "--expected-incarnation", self.incarnation, + allow_failure=allow_failure) + + def status(self, operation_id, *, allow_failure=False): + return self.client("status", "--operation-id", operation_id, allow_failure=allow_failure) + + def cancel(self, operation_id): + return self.client("cancel", "--operation-id", operation_id, "--expected-incarnation", self.incarnation, + allow_failure=True) + + def until(self, operation_id, phases=TERMINAL, *, timeout=12): + deadline = time.monotonic() + timeout + last = None + while time.monotonic() < deadline: + last = self.status(operation_id, allow_failure=True) + if last.get("phase") in phases: + return last + time.sleep(.025) + self.fail(f"phase timeout waiting for {phases}: {last}; private evidence: {self.work}") + + def stop(self, process, *, kill=False): + process.send_signal(signal.SIGKILL if kill else signal.SIGTERM) + process.wait(timeout=6) + + def snapshot_state(self): + return {path.name: path.read_bytes() for path in self.root.glob("state-*.json")} + + def goal_path(self, operation_id): + return self.control / f"goal-{self.body.sha(operation_id.encode())}.json" + + def test_submitter_exit_and_lost_reply_leave_one_durable_effect(self): + self.serve(delay=.4) + proposal = self.proposal(self.observe(), "lost-reply") + # A genuine socket caller sends then closes without consuming the server reply. + request = {"command": "submit", "operation_id": "disconnect", "proposal_file": str(proposal[0]), + "proposal_sha256": proposal[1], "expected_incarnation": self.incarnation} + with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as caller: + caller.settimeout(2) + caller.connect(str(self.socket_location())) + caller.sendall(json.dumps(request).encode() + b"\n") + self.evidence.append({"scenario": "actual_socket_closed_before_reply", "request": request}) + final = self.until("disconnect") + self.assertEqual(final["phase"], "executed", final) + state = self.snapshot_state() + repeated = self.submit("disconnect", proposal) + self.assertEqual(repeated["phase"], "executed") + self.assertEqual(self.snapshot_state(), state) + changed = self.proposal(self.observe(), "conflict") + refused = self.submit("disconnect", changed, allow_failure=True) + self.assertIn(refused["phase"], {"refused", "unknown"}) + self.assertEqual(self.snapshot_state(), state) + + def test_active_return_reports_busy_without_native_pose(self): + self.serve(delay=.2, pace=.002) + self.submit("active", self.proposal(self.observe(), "active")) + running = self.until("active", {"running"}) + self.assertEqual(running["phase"], "running") + busy = self.client("observe", allow_failure=True) + self.assertEqual(busy["phase"], "busy", busy) + self.assertIsNone(busy.get("observation")) + self.assertEqual(self.until("active")["phase"], "executed") + + def test_queued_cancellation_does_not_dispatch_or_increment_generation(self): + self.serve(delay=2) + packet = self.observe() + state = self.snapshot_state() + self.submit("queued", self.proposal(packet, "queued")) + self.cancel("queued") + terminal = self.until("queued") + self.assertEqual(terminal["phase"], "cancelled", terminal) + self.assertEqual(self.observe()["generation"], packet["generation"]) + self.assertEqual(self.snapshot_state(), state) + self.assertFalse(list(self.root.glob("op-*.json"))) + + def test_running_cancel_verifies_stop_after_real_native_progress(self): + self.serve(delay=.1, pace=.003) + packet = self.observe() + self.submit("cancel-running", self.proposal(packet, "cancel-running")) + self.until("cancel-running", {"running"}) + time.sleep(.18) # Pace keeps motion active; require the retained progress evidence below. + requested = self.cancel("cancel-running") + self.assertTrue(requested["cancel_requested"]) + terminal = self.until("cancel-running") + self.assertEqual(terminal["phase"], "cancelled", terminal) + self.assertEqual(terminal["cancellation_stage"], "during_motion") + self.assertTrue(terminal["stop_evidence"]["stop_verified"]) + self.assertTrue(terminal["stop_evidence"]["state_retained"]) + self.assertGreater(terminal["stop_evidence"]["native_steps_before_cancel"], 0) + current = self.observe() + self.assertGreater(current["generation"], packet["generation"]) + self.assertEqual(terminal["stop_evidence"]["retained_generation"], current["generation"]) + self.assertEqual(terminal["stop_evidence"]["retained_state_sha256"], current["state_sha256"]) + state = self.snapshot_state() + self.cancel("cancel-running") + self.assertEqual(self.snapshot_state(), state) + + def test_controller_death_after_cancel_request_never_claims_verified_stop(self): + process, _ = self.serve(delay=.1, pace=.003) + proposal = self.proposal(self.observe(), "cancel-interrupted") + self.submit("cancel-interrupted", proposal) + self.until("cancel-interrupted", {"running"}) + time.sleep(.18) + self.assertTrue(self.cancel("cancel-interrupted")["cancel_requested"]) + self.stop(process, kill=True) + state = self.snapshot_state() + self.serve(delay=.1, pace=.003) + terminal = self.status("cancel-interrupted", allow_failure=True) + self.assertEqual(terminal["phase"], "unknown", terminal) + self.assertNotEqual(terminal.get("historical_outcome"), "cancelled") + self.assertFalse(terminal.get("stop_evidence", {}).get("stop_verified", False)) + self.assertEqual(self.submit("cancel-interrupted", proposal, allow_failure=True)["phase"], "unknown") + self.assertEqual(self.snapshot_state(), state) + + def test_late_cancellation_preserves_terminal_effect(self): + self.serve() + self.submit("late", self.proposal(self.observe(), "late")) + final = self.until("late") + self.assertEqual(final["phase"], "executed") + state = self.snapshot_state() + retained_goal = self.goal_path("late").read_bytes() + late = self.cancel("late") + self.assertEqual(late["phase"], "executed", late) + self.assertEqual(self.snapshot_state(), state) + self.assertEqual(self.goal_path("late").read_bytes(), retained_goal) + + def test_lifetime_excludes_second_owner_and_legacy_cli(self): + self.serve() + other = self.body.directory(self.work / "d", create=True) + second = subprocess.run([sys.executable, str(CONTROLLER), "serve", "--run-dir", str(self.root), + "--controller-dir", str(other)], capture_output=True, text=True, timeout=8, check=False) + self.assertNotEqual(second.returncode, 0) + self.evidence.append({"scenario": "second_owner", "exit_code": second.returncode, "stdout": second.stdout}) + legacy = subprocess.run([sys.executable, str(BASE / "workcell.py"), "observe", "--run-dir", str(self.root)], + capture_output=True, text=True, timeout=8, check=False) + self.assertNotEqual(legacy.returncode, 0) + self.evidence.append({"scenario": "legacy_exclusion", "exit_code": legacy.returncode, "stdout": legacy.stdout}) + self.assertEqual(self.observe()["generation"], 0) + + def test_sigkill_restart_new_incarnation_unknown_and_no_replay(self): + process, health = self.serve(delay=.1, pace=.003) + proposal = self.proposal(self.observe(), "kill-running") + self.submit("kill-running", proposal) + self.until("kill-running", {"running"}) + self.stop(process, kill=True) + state = self.snapshot_state() + _, restart = self.serve(delay=.1, pace=.003) + self.assertNotEqual(restart["current_incarnation"], health["current_incarnation"]) + status = self.status("kill-running", allow_failure=True) + self.assertEqual(status["phase"], "unknown", status) + duplicate = self.submit("kill-running", proposal, allow_failure=True) + self.assertEqual(duplicate["phase"], "unknown", duplicate) + self.assertEqual(self.snapshot_state(), state) + blocked = self.client("observe", allow_failure=True) + self.assertIn(blocked["phase"], {"blocked", "unknown", "refused"}) + self.assertIsNone(blocked.get("observation")) + + def test_malformed_id_digest_and_stale_proposal_refuse_without_effect(self): + self.serve() + packet = self.observe() + first = self.proposal(packet, "valid") + state = self.snapshot_state() + for operation_id, proposal in (("../escape", first), ("digest-bad", (first[0], "0" * 64))): + with self.subTest(operation_id=operation_id): + refused = self.submit(operation_id, proposal, allow_failure=True) + self.assertEqual(refused["phase"], "refused", refused) + self.assertEqual(self.snapshot_state(), state) + self.submit("advance", first) + self.assertEqual(self.until("advance")["phase"], "executed") + state = self.snapshot_state() + stale_proposal = self.proposal(packet, "stale") + stale = self.submit("stale", stale_proposal, allow_failure=True) + if stale["phase"] in {"accepted", "running"}: + stale = self.until("stale") + self.assertIn(stale["phase"], {"refused", "unknown"}, stale) + self.assertEqual(self.snapshot_state(), state) + + def simulate_lost_controller_terminal_write(self, operation_id, *, phase="running"): + """Labeled offline retained-write fault; native engine/process was not mocked.""" + path = self.goal_path(operation_id) + original = path.read_bytes() + self.body.create_file(self.work / f"original-goal-{operation_id}.json", original) + goal = self.body.decoded(original) + goal["phase"] = phase + if phase == "unknown": + goal["reason"] = "lower effect is unresolved" + for field in ("receipt", "finished_at", "stop_evidence", "cancellation_stage", "reconciled"): + goal.pop(field, None) + self.body.utils.persist(path, goal) + self.evidence.append({"scenario": "offline_retained_write_fault", "fault": "controller_terminal_write_lost", + "operation_id": operation_id, "saved_fallback_phase": phase, "native_receipts_unchanged": True}) + + def test_exact_lower_receipt_reconciles_lost_controller_final_write(self): + process, initial = self.serve() + proposal = self.proposal(self.observe(), "reconcile") + self.submit("reconcile", proposal) + self.assertEqual(self.until("reconcile")["phase"], "executed") + self.stop(process, kill=True) + state = self.snapshot_state() + self.simulate_lost_controller_terminal_write("reconcile") + _, health = self.serve() + self.assertNotEqual(initial["current_incarnation"], health["current_incarnation"]) + recovered = self.status("reconcile") + self.assertEqual(recovered["phase"], "executed", recovered) + self.assertEqual(recovered["accepted_incarnation"], initial["current_incarnation"]) + self.assertEqual(self.submit("reconcile", proposal)["phase"], "executed") + self.assertEqual(self.snapshot_state(), state) + + def test_saved_unknown_fallback_reconciles_exact_terminal_lower_history(self): + process, initial = self.serve() + proposal = self.proposal(self.observe(), "fallback-unknown") + self.submit("fallback-unknown", proposal) + final = self.until("fallback-unknown") + self.assertEqual(final["phase"], "executed") + self.stop(process, kill=True) + state = self.snapshot_state() + lower = {path.name: path.read_bytes() for pattern in ("vision-op-*.json", "op-*.json") + for path in self.root.glob(pattern)} + self.simulate_lost_controller_terminal_write("fallback-unknown", phase="unknown") + self.serve() + recovered = self.status("fallback-unknown", allow_failure=True) + self.assertEqual(recovered["phase"], "executed", recovered) + self.assertNotIn("reason", recovered) + self.assertEqual(recovered["prior_reason"], "lower effect is unresolved") + self.assertEqual(recovered["accepted_incarnation"], initial["current_incarnation"]) + self.assertEqual(recovered["receipt"]["after_state_sha256"], final["receipt"]["after_state_sha256"]) + self.assertEqual(recovered["receipt"]["image_sha256"], final["receipt"]["image_sha256"]) + self.assertEqual(self.submit("fallback-unknown", proposal)["phase"], "executed") + self.assertEqual(self.snapshot_state(), state) + self.assertEqual({path.name: path.read_bytes() for pattern in ("vision-op-*.json", "op-*.json") + for path in self.root.glob(pattern)}, lower) + + def test_satisfied_current_scene_cannot_replace_missing_execution_history(self): + self.case["scene"]["block_xy"] = [.195, .006] + process, _ = self.serve() + proposal = self.proposal(self.observe(), "history-missing", complete=True) + self.submit("history-missing", proposal) + self.assertEqual(self.until("history-missing")["phase"], "staged") + self.stop(process, kill=True) + state = self.snapshot_state() + self.assertTrue(self.body.observation(self.root)["physical_condition_verified"]) + self.assertTrue((self.root / "order.json").exists()) + self.simulate_lost_controller_terminal_write("history-missing") + archive = self.body.directory(self.work / "a", create=True) + removed = [] + for pattern in ("vision-op-*.json", "op-*.json"): + for receipt in self.root.glob(pattern): + removed.append(receipt.name) + receipt.rename(archive / receipt.name) + self.evidence.append({"scenario": "offline_retained_write_fault", "fault": "lower_history_unavailable", + "archived_receipts": removed, "current_placement_and_order_unchanged": True}) + self.serve() + recovered = self.status("history-missing", allow_failure=True) + self.assertEqual(recovered["phase"], "unknown", recovered) + self.assertNotEqual(recovered.get("historical_outcome"), "staged") + self.assertEqual(self.submit("history-missing", proposal, allow_failure=True)["phase"], "unknown") + self.assertEqual(self.snapshot_state(), state) + + def test_accepted_undispatched_restart_never_replays_and_old_incarnation_refuses(self): + process, original = self.serve(delay=3) + proposal = self.proposal(self.observe(), "undispatched") + accepted = self.submit("undispatched", proposal) + self.assertEqual(accepted["phase"], "accepted", accepted) + self.stop(process, kill=True) + state = self.snapshot_state() + _, health = self.serve(delay=0) + self.assertNotEqual(original["current_incarnation"], health["current_incarnation"]) + self.assertEqual(self.status("undispatched", allow_failure=True)["phase"], "unknown") + self.assertFalse(list(self.root.glob("op-*.json"))) + stale_submit = self.client("submit", "--operation-id", "old-owner", "--proposal-file", proposal[0], + "--proposal-sha256", proposal[1], "--expected-incarnation", + original["current_incarnation"], allow_failure=True) + self.assertEqual(stale_submit["phase"], "refused", stale_submit) + stale_cancel = self.client("cancel", "--operation-id", "undispatched", "--expected-incarnation", + original["current_incarnation"], allow_failure=True) + self.assertEqual(stale_cancel["phase"], "refused", stale_cancel) + self.assertEqual(self.snapshot_state(), state) + + def test_changed_case_and_malformed_controller_binding_refuse_reuse(self): + process, _ = self.serve() + self.stop(process, kill=True) + original = self.case_path.read_bytes() + self.case_path.write_bytes(original + b" ") + restarted = subprocess.run([sys.executable, str(CONTROLLER), "serve", "--run-dir", str(self.root), + "--controller-dir", str(self.control)], capture_output=True, text=True, + timeout=8, check=False) + self.assertNotEqual(restarted.returncode, 0) + self.evidence.append({"scenario": "changed_case_restart", "exit_code": restarted.returncode, + "stdout": restarted.stdout}) + self.case_path.write_bytes(original) + binding = self.control / "controller-binding.json" + original_binding = binding.read_bytes() + self.body.create_file(self.work / "original-binding.json", original_binding) + binding.write_bytes(b'{"version":1}') + restarted = subprocess.run([sys.executable, str(CONTROLLER), "serve", "--run-dir", str(self.root), + "--controller-dir", str(self.control)], capture_output=True, text=True, + timeout=8, check=False) + self.assertNotEqual(restarted.returncode, 0) + self.evidence.append({"scenario": "offline_retained_write_fault", "fault": "malformed_controller_binding", + "exit_code": restarted.returncode, "stdout": restarted.stdout}) + changed_source = self.body.decoded(original_binding) + changed_source["controller_sha256"] = "0" * 64 + binding.write_bytes(self.body.encoded(changed_source)) + restarted = subprocess.run([sys.executable, str(CONTROLLER), "serve", "--run-dir", str(self.root), + "--controller-dir", str(self.control)], capture_output=True, text=True, + timeout=8, check=False) + self.assertNotEqual(restarted.returncode, 0) + self.evidence.append({"scenario": "offline_retained_write_fault", "fault": "changed_controller_source_binding", + "exit_code": restarted.returncode, "stdout": restarted.stdout}) + self.assertFalse(list(self.root.glob("op-*.json"))) + + def test_current_state_corruption_preserves_history_and_blocks_new_effects(self): + process, health = self.serve() + self.submit("historic", self.proposal(self.observe(), "historic")) + self.assertEqual(self.until("historic")["phase"], "executed") + packet = self.observe() + next_proposal = self.proposal(packet, "blocked-current") + path = self.body.state_path(self.root, packet["generation"]) + self.body.create_file(self.work / "original-current-state.json", path.read_bytes()) + path.write_bytes(b"{current state corrupted") + self.evidence.append({"scenario": "offline_retained_write_fault", "fault": "current_state_corrupt", + "historical_receipts_unchanged": True}) + state = self.snapshot_state() + historic = self.status("historic", allow_failure=True) + self.assertEqual(historic["historical_outcome"], "executed", historic) + self.assertIn(historic["current_health"], {"blocked", "unknown"}, historic) + self.assertIn(self.client("observe", allow_failure=True)["phase"], {"blocked", "unknown", "refused"}) + rejected = self.submit("blocked-current", next_proposal, allow_failure=True) + self.assertEqual(rejected["phase"], "refused", rejected) + self.assertFalse(self.goal_path("blocked-current").exists()) + self.assertEqual(self.snapshot_state(), state) + self.stop(process, kill=True) + _, restart = self.serve() + self.assertNotEqual(restart["current_incarnation"], health["current_incarnation"]) + historic = self.status("historic", allow_failure=True) + self.assertEqual(historic["historical_outcome"], "executed", historic) + self.assertIn(historic["current_health"], {"blocked", "unknown"}, historic) + rejected = self.submit("blocked-after-restart", next_proposal, allow_failure=True) + self.assertEqual(rejected["phase"], "refused", rejected) + self.assertEqual(self.snapshot_state(), state) + + def test_live_retained_binding_tamper_blocks_health_and_new_acceptance(self): + self.serve() + proposal = self.proposal(self.observe(), "live-binding") + state = self.snapshot_state() + binding = self.control / "controller-binding.json" + original = binding.read_bytes() + self.body.create_file(self.work / "original-live-binding.json", original) + changed = self.body.decoded(original) + changed["controller_sha256"] = "0" * 64 + binding.write_bytes(self.body.encoded(changed)) + self.evidence.append({"scenario": "live_retained_binding_fault", "fault": "changed_source_binding", + "runtime_source_and_native_state_unchanged": True}) + health = self.client("health", allow_failure=True) + self.assertEqual(health["phase"], "refused", health) + refused = self.submit("live-binding", proposal, allow_failure=True) + self.assertEqual(refused["phase"], "refused", refused) + self.assertFalse(self.goal_path("live-binding").exists()) + self.assertEqual(self.snapshot_state(), state) + + def test_shutdown_join_timeout_never_releases_ownership_to_live_writer(self): + self.case["scene"]["block_xy"] = [.195, .006] + # This subprocess fault changes timing only: actual dynamics and the public checker run. + # It establishes process/lock ownership; it is explicitly not native stop evidence. + script = self.work / "slow-checker-fault.py" + self.body.create_file(script, f'''import importlib.util +import sys +import time +from pathlib import Path + +spec = importlib.util.spec_from_file_location("controller_shutdown_fault", {str(CONTROLLER)!r}) +controller = importlib.util.module_from_spec(spec) +spec.loader.exec_module(controller) +work = Path({str(self.work)!r}) +original_check = controller.body.artifact_check +original_execute = controller.Controller.execute +original_join = controller.threading.Thread.join + +def slow_check(*args, **kwargs): + if not (work / "checker-entered").exists(): + controller.body.create_file(work / "checker-entered", b"read-only checker timing fault\\n") + time.sleep(2) + return original_check(*args, **kwargs) + +def tracked_execute(self, goal): + try: + return original_execute(self, goal) + finally: + controller.body.create_file(work / "worker-finished", b"writer thread finished\\n") + +def short_join(self, timeout=None): + if self.name == "native-controller-operation" and timeout == 15: + timeout = .15 + return original_join(self, timeout) + +controller.body.artifact_check = slow_check +controller.Controller.execute = tracked_execute +controller.threading.Thread.join = short_join +sys.exit(controller.main()) +'''.encode()) + process, _ = self.serve(delay=0, fault_script=script) + proposal = self.proposal(self.observe(), "slow-checker", complete=True) + self.submit("slow-checker", proposal) + deadline = time.monotonic() + 5 + while not (self.work / "checker-entered").exists() and time.monotonic() < deadline: + time.sleep(.02) + self.assertTrue((self.work / "checker-entered").exists(), self.work) + self.evidence.append({"scenario": "actual_process_with_labeled_timing_fault", + "fault": "read_only_checker_sleep_2s_worker_join_budget_0.15s", + "native_stop_proof": False}) + process.send_signal(signal.SIGTERM) + probe = '''import fcntl,json,os,sys +result = {} +for path in sys.argv[1:]: + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + try: + try: + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + result[path] = "available" + except BlockingIOError: + result[path] = "owned" + finally: + os.close(fd) +print(json.dumps(result)) +''' + deadline = time.monotonic() + 5 + while process.poll() is None and time.monotonic() < deadline: + locks = subprocess.run([sys.executable, "-c", probe, str(self.root / "lock"), str(self.control / "lock")], + capture_output=True, text=True, timeout=3, check=False) + self.assertEqual(locks.returncode, 0, locks.stderr) + ownership = json.loads(locks.stdout) + live_writer = process.poll() is None and not (self.work / "worker-finished").exists() + self.evidence.append({"scenario": "shutdown_ownership_probe", "live_unfinished_writer": live_writer, + "locks": ownership}) + if live_writer: + self.assertTrue(all(value == "owned" for value in ownership.values()), ownership) + time.sleep(.02) + process.wait(timeout=5) + completed_writer = (self.work / "worker-finished").exists() + self.serve(delay=0) + history = self.status("slow-checker", allow_failure=True) + self.assertEqual(history["phase"], "staged" if completed_writer else "unknown", history) + self.assertFalse(history.get("stop_evidence", {}).get("stop_verified", False)) + state = self.snapshot_state() + repeated = self.submit("slow-checker", proposal, allow_failure=True) + self.assertEqual(repeated["phase"], history["phase"]) + self.assertEqual(self.snapshot_state(), state) + + def test_image_feedback_contact_and_conditional_saved_json(self): + self.serve(delay=0) + contacts = 0 + for step in range(8): + packet = self.observe() + center = packet["perception"]["block_center_xy"] + zone = packet["task_intent"]["target_zone"] + inside = zone["x_min"] + .005 <= center[0] <= zone["x_max"] - .005 and zone["y_min"] + .005 <= center[1] <= zone["y_max"] - .005 + proposal = self.proposal(packet, f"loop-{step}", complete=inside, + target_x=min(.17, packet["perception"]["pusher_center_xy"][0] + .10)) + self.submit(f"loop-{step}", proposal) + final = self.until(f"loop-{step}") + if inside: + self.assertEqual(final["phase"], "staged", final) + expected = self.root / "expected-order.json" + checked = subprocess.run([str(BASE.parents[1] / "jevdual/.venv/bin/footwork"), "check-artifact", + str(self.root / "order.json"), "--expected-file", str(expected), + "--expected-sha256", self.body.sha(expected.read_bytes())], + capture_output=True, text=True, timeout=8, check=False) + self.assertEqual(checked.returncode, 0, checked.stdout) + self.assertTrue(json.loads(checked.stdout)["ok"]) + self.assertGreater(contacts, 0) + self.evidence.append({"scenario": "independent_public_footwork_check", "reply": json.loads(checked.stdout)}) + state = self.snapshot_state() + order = self.root / "order.json" + self.body.create_file(self.work / "original-saved-order.json", order.read_bytes()) + order.write_bytes(b"changed saved computer result") + self.evidence.append({"scenario": "offline_retained_write_fault", "fault": "saved_order_tampered", + "historical_staging_receipts_and_native_state_unchanged": True}) + historical = self.status(f"loop-{step}", allow_failure=True) + self.assertEqual(historical["historical_outcome"], "staged", historical) + self.assertEqual(historical["current_health"], "blocked", historical) + self.assertFalse(historical["current_software_artifact_check"]["ok"]) + new_proposal = self.proposal(packet, "after-artifact-tamper", complete=True) + refused = self.submit("after-artifact-tamper", new_proposal, allow_failure=True) + self.assertEqual(refused["phase"], "refused", refused) + self.assertFalse(self.goal_path("after-artifact-tamper").exists()) + self.assertEqual(self.snapshot_state(), state) + return + self.assertEqual(final["phase"], "executed", final) + contacts += final["receipt"]["contact_statistics"]["pusher_block_contact_samples"] + self.fail("independent bounded RGB feedback loop did not stage") + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_crash_body.py b/tools/physical-workcell/test_crash_body.py new file mode 100644 index 0000000..4c92dc8 --- /dev/null +++ b/tools/physical-workcell/test_crash_body.py @@ -0,0 +1,238 @@ +"""Real-process native/receipt SIGKILL checks; all actions use public TCP.""" +import concurrent.futures +import http.client +import signal +import socket +import subprocess +import sys +import time +import unittest +import uuid +from pathlib import Path + +import bench +import body_client +import body_network +import body_protocol as wire +import crash_body +import mujoco +import numpy as np +import order_protocol as storage + +BASE = Path(__file__).resolve().parent +ZONE = {"x_min": .04, "x_max": .10, "y_min": -.025, "y_max": .025} + + +class CrashBodyTests(unittest.TestCase): + def setUp(self): + self.root = BASE / "output" / ("sim13-crash-body-" + uuid.uuid4().hex[:12]) + self.root.mkdir(mode=0o700) + self.body_root = self.root / "body" + self.processes, self.handles, self.events = [], [], [] + self.client = None + with socket.socket() as reserve: + reserve.bind(("127.0.0.1", 0)) + self.port = reserve.getsockname()[1] + self.origin = f"http://127.0.0.1:{self.port}" + self.sources = {**body_network.sources(), "crash_body.py": storage.sha(storage.read_bytes(BASE / "crash_body.py"))} + + def tearDown(self): + if self.client: + self.client.close() + for process in self.processes: + if process.poll() is None: + process.kill() + process.wait(timeout=5) + for handle in self.handles: + handle.close() + failed = any(test is self for test, _ in (*self._outcome.result.failures, *self._outcome.result.errors)) + current = {**body_network.sources(), "crash_body.py": storage.sha(storage.read_bytes(BASE / "crash_body.py"))} + storage.create_file(self.root / "conformance.json", storage.encoded({ + "test_id": self.id(), "passed": not failed, "scenario_kind": "actual_process_SIGKILL_TCP_native", + "sources_unchanged": current == self.sources, "processes_reaped": all(p.poll() is not None for p in self.processes), + "processes": [{"pid": p.pid, "returncode": p.returncode} for p in self.processes], "events": self.events})) + self.assertEqual(current, self.sources) + + def record(self, kind, **value): + event = {"kind": kind, **value} + storage.create_file(self.root / f"event-{len(self.events):04d}.json", storage.encoded(event)) + self.events.append(event) + + def start(self, gate=None): + path = self.root / f"service-{len(self.processes)}.log" + handle = path.open("xb") + path.chmod(0o600) + self.handles.append(handle) + launcher = "crash_body.py" if gate else "body_network.py" + command = [sys.executable, str(BASE / launcher), "--root", str(self.body_root), "--port", str(self.port)] + if not self.processes: + command += ["--initialize", "--diagnostic"] + if gate: + command += ["--gate", gate] + self.process = subprocess.Popen(command, cwd=BASE, stdout=handle, stderr=subprocess.STDOUT) + self.processes.append(self.process) + deadline = time.monotonic() + 20 + while time.monotonic() < deadline and self.process.poll() is None: + try: + status, content = body_client.http_call(self.origin, "GET", "/identity", timeout=.3) + if status == 200: + identity = body_client.identity(storage.decode(content)) + self.record("ready", pid=self.process.pid, identity=identity, diagnostic_gate=gate, + launcher=launcher) + return identity + except (OSError, http.client.HTTPException): + pass + time.sleep(.02) + self.fail("owned body failed to start; inspect retained service log") + + def prepare(self): + self.client = body_client.BodyClient(self.root / "client", self.origin, timeout=2) + self.client.acquire() + self.client.start_job("job", ZONE) + challenge = self.client.observe() + proposal = self.client.propose(challenge["challenge_id"], "move", "Owned hard-crash native approach", -.19, 0) + return proposal + + def wait_marker(self, expected): + path = self.body_root / "barrier.json" + deadline = time.monotonic() + 10 + while time.monotonic() < deadline and self.process.poll() is None: + if path.exists(): + marker = storage.decode(storage.read_bytes(path)) + self.assertEqual(marker["pid"], self.process.pid) + self.assertEqual(marker["barrier"], expected) + self.record("stopped_barrier", marker=marker) + return marker + time.sleep(.005) + self.fail("owned process did not reach its bound crash gate") + + def kill_dispatch(self, gate, proposal): + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + future = pool.submit(self.client.dispatch, proposal["proposal_id"]) + marker = self.wait_marker(gate) + self.process.kill() + self.process.wait(timeout=5) + self.assertEqual(self.process.returncode, -signal.SIGKILL) + with self.assertRaises(body_client.Uncertain) as caught: + future.result(timeout=5) + self.assertEqual(caught.exception.command_id, marker["command_id"]) + request = storage.decode(storage.read_bytes(self.root / "client" / "requests" / (marker["command_id"] + ".json"))) + self.assertEqual(request["arguments"], {"proposal_id": proposal["proposal_id"]}) + self.assertEqual(request["operation"], "dispatch") + if "request_sha256" in marker: + self.assertEqual(marker["request_sha256"], wire.command_digest(request)) + self.record("SIGKILL", pid=self.process.pid, command_id=marker["command_id"], returncode=self.process.returncode) + return marker, request + + def native_state(self, generation): + content = bench.body.read_bytes(bench.body.state_path(self.body_root / "bench", generation)) + state = bench.body.decoded(content) + model = mujoco.MjModel.from_xml_path(str(self.body_root / "bench" / "model.xml")) + data = mujoco.MjData(model) + mujoco.mj_setState(model, data, np.array(state["values"]), mujoco.mjtState.mjSTATE_INTEGRATION) + mujoco.mj_forward(model, data) + return content, {"time_s": float(data.time), "pusher_xyz": data.xpos[model.body("pusher").id].tolist(), + "block_xyz": data.xpos[model.body("block").id].tolist()} + + def snapshot_effects(self): + # Independent stopped evidence; never passed to an agent or used to authorize an effect. + paths = [self.body_root / "bench" / "bench.json", self.body_root / "agent" / "agent.json"] + for directory in (self.body_root / "bench", self.body_root / "agent"): + paths += [p for p in directory.rglob("*") if p.is_file() and p.name not in {"app.lock", "bench.json", "agent.json"}] + return {str(p.relative_to(self.body_root)): storage.sha(bench.body.read_bytes(p)) for p in paths} + + def test_after_native_SIGKILL_retains_real_actuation_but_stays_unknown_without_replay(self): + initial = self.start("after_native") + proposal = self.prepare() + marker, request = self.kill_dispatch("after_native", proposal) + native = self.body_root / "bench" + manifest = storage.decode(storage.read_bytes(native / "bench.json")) + self.assertEqual(marker["bench_id"], initial["body_id"]) + self.assertEqual(marker["operation_id"], proposal["operation_id"]) + self.assertEqual(manifest["generation"], marker["manifest_generation"]) + self.assertEqual(manifest["busy"]["operation_id"], marker["operation_id"]) + self.assertNotIn(marker["operation_id"], manifest["operations"]) + self.assertEqual(marker["native_generation"], manifest["generation"] + 1) + before_bytes, before = self.native_state(manifest["generation"]) + next_bytes, advanced = self.native_state(marker["native_generation"]) + self.assertEqual(storage.sha(next_bytes), marker["native_state_sha256"]) + self.assertNotEqual(before_bytes, next_bytes) + self.assertEqual(advanced["time_s"], marker["native_time_s"]) + self.assertGreater(advanced["time_s"], before["time_s"]) + self.assertGreater(abs(advanced["pusher_xyz"][0] - before["pusher_xyz"][0]), .05) + agent_manifest = storage.decode(storage.read_bytes(self.body_root / "agent" / "agent.json")) + self.assertIn(proposal["proposal_id"], agent_manifest["intents"]) + self.assertNotIn(proposal["proposal_id"], agent_manifest["results"]) + effects = self.snapshot_effects() + restarted = self.start() + self.assertEqual({k: v for k, v in restarted.items() if k != "epoch"}, {k: v for k, v in initial.items() if k != "epoch"}) + self.assertEqual(restarted["epoch"], initial["epoch"] + 1) + state = self.client.status() + self.assertEqual(state["server_state"]["state"], "unknown") + self.assertEqual(state["server_state"]["bench"]["state"], "unknown") + self.assertIn(marker["command_id"], state["server_state"]["unresolved_commands"]) + for _ in range(2): + with self.assertRaises(body_client.Uncertain) as caught: + self.client.reconcile(marker["command_id"]) + self.assertEqual(caught.exception.command_id, marker["command_id"]) + for action in (self.client.acquire, self.client.observe, lambda: self.client.dispatch(proposal["proposal_id"])): + with self.assertRaises(body_client.Uncertain): + action() + # Challenge the server independently with a new lease/command; unknown cannot admit successors. + headers = body_client.pinned_headers(restarted) + value = {"client_id": request["client_id"], "expected_epoch": restarted["epoch"], "duration_s": 30} + status, content = body_client.http_call(self.origin, "POST", "/lease", headers, storage.encoded(value)) + self.assertEqual(status, 200) + lease = storage.decode(content) + successor = {**request, **{k: lease[k] for k in ("client_id", "lease_id", "epoch")}, + "command_id": "successor-" + uuid.uuid4().hex, "operation": "observe", "arguments": {}} + _, content = body_client.http_call(self.origin, "POST", "/commands", headers, storage.encoded(successor)) + self.assertEqual(storage.decode(content)["state"], "not_accepted") + self.assertFalse((self.body_root / "commands" / storage.sha(successor["command_id"].encode())).exists()) + self.assertEqual(self.snapshot_effects(), effects) + journal = self.client._load() + self.assertEqual(journal["commands"][marker["command_id"]]["send_count"], 1) + self.record("blocked_after_native", marker=marker, before=before, advanced=advanced, public_state=state, + exact_single_send=True, state_unchanged=True) + + def test_indexed_lower_receipt_SIGKILL_recovers_exact_command_without_motion_replay(self): + initial = self.start("after_dispatch") + proposal = self.prepare() + marker, request = self.kill_dispatch("after_dispatch", proposal) + agent_manifest = storage.decode(storage.read_bytes(self.body_root / "agent" / "agent.json")) + content = storage.read_bytes(self.body_root / "agent" / "results" / (proposal["proposal_id"] + ".json")) + self.assertEqual(storage.sha(content), agent_manifest["results"][proposal["proposal_id"]]) + lower = storage.decode(content) + self.assertEqual(lower["outcome"], "executed") + command_dir = self.body_root / "commands" / storage.sha(marker["command_id"].encode()) + self.assertIsNone(storage.decode(storage.read_bytes(command_dir / "intent.json"))["result_sha256"]) + effects = self.snapshot_effects() + restarted = self.start() + self.assertEqual(restarted["body_id"], initial["body_id"]) + self.assertEqual(restarted["epoch"], initial["epoch"] + 1) + receipt = self.client.reconcile(marker["command_id"]) + self.assertEqual(receipt["state"], "completed") + self.assertEqual(receipt["recorded_epoch"], request["epoch"]) + self.assertEqual(receipt["request_sha256"], wire.command_digest(request)) + self.assertEqual(receipt["result"], body_network.agent.public_response(lower)) + self.assertEqual(self.client.reconcile(marker["command_id"]), receipt) + self.assertEqual(self.snapshot_effects(), effects) + self.assertEqual(self.client.status()["server_state"]["state"], "ready") + self.assertEqual(self.client._load()["commands"][marker["command_id"]]["send_count"], 1) + self.record("exact_lower_receipt_recovered", receipt=receipt, exact_single_send=True, native_unchanged=True) + + def test_crash_gate_refuses_a_nondiagnostic_body_and_stale_marker(self): + body_network.initialize(self.body_root, diagnostic_enabled=False) + with self.assertRaises(storage.Refusal): + crash_body.CrashBody(self.body_root) + other = self.root / "diagnostic-body" + body_network.initialize(other, diagnostic_enabled=True) + storage.persist(other / "barrier.json", {"pid": 1, "barrier": "after_native"}) + with self.assertRaises(storage.Refusal): + crash_body.CrashBody(other) + with self.assertRaises(storage.Refusal): + crash_body.CrashBody(other, gate="invented") + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_episode_cases.py b/tools/physical-workcell/test_episode_cases.py new file mode 100644 index 0000000..68f3915 --- /dev/null +++ b/tools/physical-workcell/test_episode_cases.py @@ -0,0 +1,248 @@ +"""Independent development cases; never discovers or runs qualification fixtures.""" + +import copy +import importlib.util +import json +import tempfile +import time +import unittest +from pathlib import Path +from unittest.mock import patch + +BASE = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("case_vision", BASE / "image_guided.py") +vision = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(vision) +body = vision.body + + +class EpisodeCaseTests(unittest.TestCase): + def setUp(self): + body.OUTPUT.mkdir(mode=0o700, exist_ok=True) + self.tmp = tempfile.TemporaryDirectory(prefix="case-development-", dir=body.OUTPUT) + self.addCleanup(self.tmp.cleanup) + self.work = Path(self.tmp.name) + self.counter = 0 + + def case(self, **scene): + return {"version": 1, "case_id": "independent-development", "max_actions": 8, + "scene": {"block_xy": [-.16, .015], "illumination_scale": 1, + "occlusion_fraction": 0, "camera_offset_x_m": 0, **scene}, + "work_order": {"version": 1, "order_id": "independent-order", + "project": {"namespace": "test", "project_id": "development"}, + "part_id": "block", "target_zone": { + "x_min": .18, "x_max": .26, "y_min": -.04, "y_max": .04}}} + + def initialize(self, case): + self.counter += 1 + root = body.directory(self.work / f"run-{self.counter}", create=True) + source = self.work / f"case-{self.counter}.json" + content = body.encoded(case) + body.create_file(source, content) + native = body.initialize_episode(root, str(source), body.sha(content)) + return root, source, body.sha(content), native + + def propose(self, root, packet, *, decision="complete", target=None, token="development"): + proposal = {"version": 1, "run_dir": str(root), "generation": packet["generation"], + "image_sha256": packet["image_sha256"], "token": token, + "block_box": packet["perception"].get("block_box", [10, 10, 20, 20]), "decision": decision} + if target is not None: + proposal["target"] = target + content = body.encoded(proposal) + path = self.work / f"proposal-{self.counter}-{token}.json" + body.create_file(path, content) + return proposal, path, body.sha(content) + + def verdict(self, root, token="development"): + return body.decoded(body.read_bytes(root / f"vision-verdict-{body.sha(token.encode())}.json")) + + def test_case_validation_exact_fields_finite_bounds_and_duplicates(self): + cases = body.cases + valid = self.case() + variants = [] + extra = copy.deepcopy(valid) + extra["oracle"] = True + variants.append(extra) + invalid = copy.deepcopy(valid) + invalid["scene"]["block_xy"][0] = float("nan") + variants.append(invalid) + invalid = copy.deepcopy(valid) + invalid["max_actions"] = True + variants.append(invalid) + invalid = copy.deepcopy(valid) + invalid["scene"]["occlusion_fraction"] = 1.1 + variants.append(invalid) + for candidate in variants: + with self.subTest(candidate=candidate), self.assertRaises(cases.CaseError): + cases.validate(candidate) + with self.assertRaises(cases.CaseError): + cases.decode(b'{"version":1,"version":1}') + self.assertEqual(cases.validate(valid), valid) + + def test_initial_xy_applied_once_and_repeat_never_resets_motion(self): + case = self.case(block_xy=[-.19, -.025]) + root, source, digest, native = self.initialize(case) + self.assertAlmostEqual(native["block_xyz"][0], -.19, delta=.0001) + self.assertAlmostEqual(native["block_xyz"][1], -.025, delta=.0001) + body.act(root, "native-development", 0, -.21, 0) + retained_state = body.read_bytes(root / "manifest.json") + repeated = body.initialize_episode(root, str(source), digest) + self.assertEqual(repeated["generation"], 1) + self.assertEqual(body.read_bytes(root / "manifest.json"), retained_state) + self.assertEqual(body.case_for(root), case) + + def test_changed_source_retained_case_and_partial_initialization_refuse(self): + root, source, digest, _ = self.initialize(self.case()) + original = source.read_bytes() + source.write_bytes(original + b" ") + with self.assertRaises(body.Refusal): + body.load(root) + source.write_bytes(original) + (root / "episode-case.json").write_bytes(original + b" ") + with self.assertRaises(body.Refusal): + body.initialize_episode(root, str(source), digest) + partial = body.directory(self.work / "partial", create=True) + body.create_file(partial / "episode-case.json", original) + with self.assertRaises(body.Refusal): + body.initialize_episode(partial, str(source), digest) + self.assertFalse((partial / "manifest.json").exists()) + + def test_case_source_symlink_and_bad_digest_refuse_before_initialization(self): + content = body.encoded(self.case()) + source = self.work / "source.json" + body.create_file(source, content) + link = self.work / "link.json" + link.symlink_to(source) + root = body.directory(self.work / "symlink-run", create=True) + for path, digest in ((link, body.sha(content)), (source, "0" * 64)): + with self.subTest(path=path), self.assertRaises(body.Refusal): + body.initialize_episode(root, str(path), digest) + self.assertFalse((root / "manifest.json").exists()) + + def test_unavailable_and_occluded_images_remain_inspectable_without_effects(self): + for scene, expected in (({"illumination_scale": 0}, "unavailable"), + ({"occlusion_fraction": .7}, "ambiguous")): + with self.subTest(scene=scene): + root, _, _, _ = self.initialize(self.case(**scene)) + packet = vision.view(root) + self.assertEqual(packet["phase"], "ready") + self.assertEqual(packet["perception"]["state"], expected) + self.assertEqual(vision.decode_png(body.read_bytes(Path(packet["image_path"]))).shape, (360, 640, 3)) + self.assertEqual(vision.view(root), packet) + proposal, path, digest = self.propose(root, packet) + with patch.object(body, "complete") as complete, self.assertRaises(body.Refusal): + vision.apply(root, str(path), digest) + complete.assert_not_called() + verdict = self.verdict(root) + self.assertIsNone(verdict["visual_verdict"]) + self.assertTrue(verdict["before_oracle"]) + self.assertEqual(verdict["case_binding"], packet["binding"]["case_binding"]) + self.assertEqual(verdict["proposal"], proposal) + self.assertEqual(body.action_budget(root)["admitted_dispatches"], 0) + + def test_offset_image_has_invalid_calibration_and_no_world_center(self): + root, _, _, _ = self.initialize(self.case(camera_offset_x_m=.045)) + packet = vision.view(root) + self.assertEqual(packet["perception"]["state"], "recognized") + self.assertFalse(packet["calibration"]["valid"]) + self.assertNotIn("block_center_xy", packet["perception"]) + self.assertNotIn("pusher_center_xy", packet["perception"]) + _, path, digest = self.propose(root, packet, decision="move", target={"x": -.2, "y": 0}) + with patch.object(body, "act") as act, self.assertRaises(body.Refusal): + vision.apply(root, str(path), digest) + act.assert_not_called() + self.assertEqual(body.action_budget(root)["remaining"], 8) + output = json.dumps(packet) + for forbidden in ("block_xyz", "pusher_xyz", "block_xy", "illumination_scale", "physical_condition_verified"): + self.assertNotIn(forbidden, output) + + def test_false_visual_verdict_and_exact_request_retained_before_oracle(self): + root, _, _, _ = self.initialize(self.case()) + packet = vision.view(root) + proposal, path, digest = self.propose(root, packet) + with patch.object(body, "complete") as complete, self.assertRaises(body.Refusal): + vision.apply(root, str(path), digest) + complete.assert_not_called() + verdict = self.verdict(root) + self.assertFalse(verdict["visual_verdict"]) + self.assertEqual(verdict["state_sha256"], packet["state_sha256"]) + self.assertEqual(verdict["image_sha256"], packet["image_sha256"]) + request = root / f"vision-request-{body.sha(proposal['token'].encode())}.json" + self.assertEqual(body.read_bytes(request), body.read_bytes(path)) + self.assertFalse(vision.proposal_path(root, proposal["token"]).exists()) + + def test_positive_visual_verdict_survives_native_gate_failure_as_unknown(self): + root, _, _, _ = self.initialize(self.case(block_xy=[.195, .012])) + packet = vision.view(root) + _, path, digest = self.propose(root, packet) + + def refused(*_args): + self.assertTrue(self.verdict(root)["visual_verdict"]) + raise body.Refusal("independent native gate refusal") + + with patch.object(body, "complete", side_effect=refused): + result = vision.apply(root, str(path), digest) + self.assertEqual(result["phase"], "unknown") + self.assertTrue(self.verdict(root)["before_oracle"]) + with patch.object(body, "complete") as complete: + self.assertEqual(vision.apply(root, str(path), digest)["phase"], "unknown") + complete.assert_not_called() + self.assertFalse((root / "order.json").exists()) + + def test_budget_exhaustion_refuses_before_wrapper_or_native_intent(self): + case = self.case(block_xy=[-.16, 0]) + case["max_actions"] = 1 + root, _, _, _ = self.initialize(case) + packet = vision.view(root) + _, path, digest = self.propose(root, packet, decision="move", target={"x": -.2, "y": 0}, token="first") + self.assertEqual(vision.apply(root, str(path), digest)["phase"], "executed") + packet = vision.view(root) + self.assertEqual(packet["action_budget"], {"max_actions": 1, "admitted_dispatches": 1, "remaining": 0}) + proposal, path, digest = self.propose(root, packet, decision="move", target={"x": -.1, "y": 0}, token="exhausted") + before = body.read_bytes(root / "manifest.json") + with patch.object(body, "act") as act, self.assertRaises(body.Refusal): + vision.apply(root, str(path), digest) + act.assert_not_called() + self.assertFalse(vision.proposal_path(root, proposal["token"]).exists()) + self.assertEqual(body.read_bytes(root / "manifest.json"), before) + + def test_completion_is_an_admitted_action_without_manufactured_motion(self): + case = self.case(block_xy=[.195, .012]) + case["max_actions"] = 1 + root, _, _, _ = self.initialize(case) + packet = vision.view(root) + _, path, digest = self.propose(root, packet) + result = vision.apply(root, str(path), digest) + self.assertEqual(result["phase"], "staged") + self.assertTrue(result["current_software_artifact_check"]["ok"]) + self.assertEqual(body.action_budget(root)["remaining"], 0) + operations = [body.load_op(path) for path in root.glob("op-*.json")] + self.assertEqual([operation["request"]["kind"] for operation in operations], ["complete"]) + self.assertEqual(body.load(root)[0]["generation"], 0) + + def test_initialization_marker_exact_case_timing_and_no_stray_file_exception(self): + source = self.work / "marker-case.json" + content = body.encoded(self.case()) + body.create_file(source, content) + digest = body.sha(content) + root = body.directory(self.work / "marker-run", create=True) + marker = {"version": 1, "episode_id": "a" * 32, "run_dir": str(root), "case_path": str(source), + "case_sha256": digest, "participant": "fresh_host", "started_time_unix": time.time(), + "phase": "initializing", "fingerprint": {"runner_sha256": "b" * 64, "vision": vision.fingerprint()}} + marker_path = root / "episode.json" + body.create_file(marker_path, body.encoded(dict(marker, case_sha256="0" * 64))) + with self.assertRaises(body.Refusal): + body.initialize_episode(root, str(source), digest) + marker_path.write_bytes(body.encoded(dict(marker, started_time_unix=-1))) + with self.assertRaises(body.Refusal): + body.initialize_episode(root, str(source), digest) + marker_path.write_bytes(body.encoded(marker)) + body.create_file(root / "stray.json", b"{}") + with self.assertRaises(body.Refusal): + body.initialize_episode(root, str(source), digest) + (root / "stray.json").unlink() + self.assertEqual(body.initialize_episode(root, str(source), digest)["generation"], 0) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_episode_runner.py b/tools/physical-workcell/test_episode_runner.py new file mode 100644 index 0000000..e39fe02 --- /dev/null +++ b/tools/physical-workcell/test_episode_runner.py @@ -0,0 +1,229 @@ +"""SIM3 conformance with private development cases; deterministic tests are not AI trials.""" + +import importlib.util +import json +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +BASE = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("episode_test", BASE / "episode_runner.py") +runner = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(runner) +body = runner.body +vision = runner.vision + + +class EpisodeTests(unittest.TestCase): + def setUp(self): + body.OUTPUT.mkdir(mode=0o700, exist_ok=True) + self.tmp = tempfile.TemporaryDirectory(prefix="sim3-private-dev-", dir=body.OUTPUT) + self.addCleanup(self.tmp.cleanup) + self.work = Path(self.tmp.name) + self.root = body.directory(self.work / "run", create=True) + self.case = {"version": 1, "case_id": "private-dev-episode", "max_actions": 8, + "scene": {"block_xy": [-.17, .007], "illumination_scale": 1.0, + "occlusion_fraction": 0.0, "camera_offset_x_m": 0.0}, + "work_order": {"version": 1, "order_id": "private-dev-order", + "project": {"namespace": "codex_app", "project_id": "private-dev"}, + "part_id": "block", "target_zone": { + "x_min": .13, "x_max": .21, "y_min": -.03, "y_max": .05}}} + self.case_path = self.work / "dev-case.json" + self.case_sha = self.save_case(self.case_path) + + def save_case(self, path): + content = body.encoded(self.case) + body.create_file(path, content) + return body.sha(content) + + def init(self): + return runner.initialize(self.root, str(self.case_path), self.case_sha) + + def declare(self, outcome="completed", interventions=None): + content = b"Private development participant declaration.\n" + path = self.work / "explanation.txt" + if not path.exists(): + body.create_file(path, content) + return runner.finish(self.root, outcome, str(path), body.sha(content), interventions) + + def snapshot(self): + return {path.name: path.read_bytes() for path in self.root.iterdir() if path.is_file() and path.name != "lock"} + + def no_oracle(self, output): + serialized = json.dumps(output) + for forbidden in ("block_xyz", "pusher_xyz", "physical_condition_verified", "block_center_in_zone", + "physical_readback", "block_xy", "illumination_scale", "occlusion_fraction", "camera_offset_x_m"): + self.assertNotIn(forbidden, serialized) + + def test_case_binding_no_implicit_reset_and_public_init_filtering(self): + initial = self.init() + self.no_oracle(initial) + before = self.snapshot() + repeat = self.init() + self.assertEqual(initial, repeat) + self.assertEqual(before, self.snapshot()) + self.case["scene"]["block_xy"] = [-.19, .005] + other = self.work / "other-case.json" + digest = self.save_case(other) + with self.assertRaises(Refusal): + runner.initialize(self.root, str(other), digest) + self.assertEqual(before, self.snapshot()) + + def test_finish_declaration_elapsed_idempotency_conflict_and_no_effects(self): + self.init() + states = {p.name: p.read_bytes() for p in self.root.glob("state-*.json")} + declaration = self.declare("unresolved", interventions=2) + self.no_oracle(declaration) + before = self.snapshot() + self.assertEqual(declaration, self.declare("unresolved", interventions=2)) + self.assertEqual(before, self.snapshot()) + with self.assertRaises(Refusal): + self.declare("completed", interventions=2) + self.assertEqual(states, {p.name: p.read_bytes() for p in self.root.glob("state-*.json")}) + score = runner.evaluate(self.root) + self.assertGreaterEqual(score["elapsed_s"], 0) + self.assertEqual(score["interventions_declared"], 2) + self.assertEqual(score["unresolved"], 1) + + def test_false_completion_claim_is_visible_without_software(self): + self.init() + self.declare() + score = runner.evaluate(self.root) + self.assertTrue(score["incorrect_completion_claim"]) + self.assertFalse(score["qualified_completion"]) + self.assertEqual(score["completed"], 0) + self.assertIsNone(score["interventions_declared"]) + self.assertFalse((self.root / "order.json").exists()) + + def test_satisfactory_current_scene_does_not_manufacture_history(self): + self.case["work_order"]["target_zone"] = {"x_min": -.20, "x_max": -.14, "y_min": -.02, "y_max": .03} + self.case_path.unlink() + self.case_sha = self.save_case(self.case_path) + self.init() + self.declare() + score = runner.evaluate(self.root) + self.assertTrue(score["physical_condition"]) + self.assertEqual(score["motion_executed_count"], 0) + self.assertFalse(score["historical_software_staged"]) + self.assertTrue(score["incorrect_completion_claim"]) + + def test_already_placed_case_real_visual_and_software_completion_has_zero_motion(self): + self.case["work_order"]["target_zone"] = {"x_min": -.21, "x_max": -.13, "y_min": -.02, "y_max": .03} + self.case_path.unlink() + self.case_sha = self.save_case(self.case_path) + result = runner.baseline(self.root, str(self.case_path), self.case_sha) + self.assertEqual(result["declaration"]["outcome"], "completed", result) + score = runner.evaluate(self.root) + self.assertEqual(score["completed"], 1, score) + self.assertEqual(score["motion_executed_count"], 0) + self.assertEqual(score["action_count"], 1) + + def test_actual_rgb_baseline_completes_with_contact_and_independent_saved_file(self): + public = runner.baseline(self.root, str(self.case_path), self.case_sha) + self.no_oracle(public) + self.assertEqual(public["participant"], "deterministic_rgb") + score = runner.evaluate(self.root) + self.assertEqual(score["completed"], 1, score) + self.assertTrue(score["physical_condition"]) + self.assertTrue(score["current_software_ok"]) + self.assertGreater(score["motion_executed_count"], 0) + self.assertLessEqual(score["action_count"], 8) + before = self.snapshot() + self.assertEqual(public, runner.baseline(self.root, str(self.case_path), self.case_sha)) + self.assertEqual(before, self.snapshot()) + self.assertEqual(score, runner.evaluate(self.root)) + self.assertEqual(before, self.snapshot()) + + def test_failed_rgb_recognition_retains_image_and_declares_unresolved(self): + self.case["scene"]["illumination_scale"] = .002 + self.case_path.unlink() + self.case_sha = self.save_case(self.case_path) + public = runner.baseline(self.root, str(self.case_path), self.case_sha) + self.assertEqual(public["declaration"]["outcome"], "unresolved") + self.assertTrue(list(self.root.glob("vision-frame-*.png"))) + self.assertEqual(runner.evaluate(self.root)["action_count"], 0) + + def test_invalid_calibration_retains_image_and_blocks_baseline(self): + self.case["scene"]["camera_offset_x_m"] = .043 + self.case_path.unlink() + self.case_sha = self.save_case(self.case_path) + public = runner.baseline(self.root, str(self.case_path), self.case_sha) + self.assertEqual(public["declaration"]["outcome"], "unresolved") + self.assertTrue(list(self.root.glob("vision-frame-*.png"))) + score = runner.evaluate(self.root) + self.assertEqual(score["action_count"], 0) + self.assertEqual(score["unresolved"], 1) + + def test_small_budget_reserves_completion_slot_and_performs_no_unbudgeted_motion(self): + self.case["max_actions"] = 1 + self.case_path.unlink() + self.case_sha = self.save_case(self.case_path) + result = runner.baseline(self.root, str(self.case_path), self.case_sha) + self.assertEqual(result["declaration"]["outcome"], "unresolved", result) + self.assertEqual(runner.evaluate(self.root)["action_count"], 0) + self.assertTrue(list(self.root.glob("vision-frame-*.png"))) + + def test_failed_setup_is_durable_counted_and_never_retried(self): + with patch.object(body, "initialize_episode", side_effect=Refusal("dev setup failure")) as setup: + initial = self.init() + self.assertEqual(initial["phase"], "setup_failed") + self.assertEqual(initial, self.init()) + setup.assert_called_once() + self.declare("failed") + score = runner.evaluate(self.root) + self.assertTrue(score["setup_failed"]) + self.assertEqual(score["failed"], 1) + self.assertEqual(score["attempted"], 1) + + def test_unexpected_baseline_driver_failure_is_finished_and_counted(self): + with patch.object(vision, "view", side_effect=RuntimeError("private dev driver failure")): + result = runner.baseline(self.root, str(self.case_path), self.case_sha) + self.assertEqual(result["declaration"]["outcome"], "failed") + score = runner.evaluate(self.root) + self.assertTrue(score["driver_failed"]) + self.assertEqual(score["failed"], 1) + self.assertIsNotNone(score["elapsed_s"]) + + def test_bound_generation_verdict_counts_hidden_false_positive(self): + self.init() + packet = vision.view(self.root) + located = vision.locate(vision.decode_png(body.read_bytes(Path(packet["image_path"])))) + # Inject a false visual assertion into a private dev receipt to test the scorer; + # this is not a claim about the real detector's accuracy. + proposal = {"version": 1, "run_dir": str(self.root), "generation": packet["generation"], + "image_sha256": packet["image_sha256"], "token": "dev-false-visual", + "block_box": located["block"]["box"], "decision": "complete"} + content = body.encoded(proposal) + token_sha = body.sha(proposal["token"].encode()) + body.create_file(self.root / f"vision-request-{token_sha}.json", content) + verdict = {"proposal": proposal, "proposal_sha256": body.sha(content), + "binding": packet["binding"], "case_binding": packet["binding"].get("case_binding"), + "generation": packet["generation"], "state_sha256": packet["state_sha256"], + "image_sha256": packet["image_sha256"], "visual_verdict": True, "before_oracle": True, + "reason": "injected private dev false assertion"} + body.create_file(self.root / f"vision-verdict-{token_sha}.json", body.encoded(verdict)) + body.act(self.root, "dev-native-contact", 0, .11, 0) + self.assertTrue(body.observation(self.root)["physical_condition_verified"]) + self.declare() + before = self.snapshot() + score = runner.evaluate(self.root) + self.assertEqual(score["visual_positive_count"], 1, score) + self.assertEqual(score["visual_false_positive_count"], 1, score) + self.assertFalse(score["visual_verdicts"][0]["bound_native_condition"]) + self.assertTrue(score["physical_condition"]) + self.assertTrue(score["incorrect_completion_claim"]) + self.assertEqual(before, self.snapshot()) + + def test_doctor_has_no_episode_or_persisted_simulator_effect(self): + before = self.snapshot() + result = runner.doctor() + self.assertEqual(result["phase"], "ready", result) + self.no_oracle(result) + self.assertEqual(before, self.snapshot()) + + +Refusal = runner.Refusal + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_image_guided.py b/tools/physical-workcell/test_image_guided.py new file mode 100644 index 0000000..dcf72af --- /dev/null +++ b/tools/physical-workcell/test_image_guided.py @@ -0,0 +1,278 @@ +"""Real RGB/native-engine conformance; this deterministic harness is not the AI trial.""" + +import copy +import importlib.util +import json +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +import numpy as np + +BASE = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("image_wrapper", BASE / "image_guided.py") +vision = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(vision) + + +class ImageGuidedTests(unittest.TestCase): + def setUp(self): + vision.body.OUTPUT.mkdir(mode=0o700, exist_ok=True) + self.tmp = tempfile.TemporaryDirectory(prefix="image-conformance-", dir=vision.body.OUTPUT) + self.addCleanup(self.tmp.cleanup) + self.work = Path(self.tmp.name) + self.root = vision.body.directory(self.work / "run", create=True) + order = {"version": 1, "order_id": "image-fixture", "project": {"namespace": "codex_app", "project_id": "fixture"}, + "part_id": "block", "target_zone": {"x_min": .18, "x_max": .26, "y_min": -.04, "y_max": .04}} + path = self.work / "order.json" + content = vision.body.encoded(order) + vision.body.create_file(path, content) + vision.body.initialize(self.root, str(path), vision.body.sha(content)) + self.counter = 0 + + def view(self): + packet = vision.view(self.root) + image = vision.decode_png(vision.body.read_bytes(Path(packet["image_path"]))) + return packet, image, vision.locate(image) + + def proposal(self, packet, located, decision="move", target=None, token=None): + self.counter += 1 + proposal = {"version": 1, "run_dir": str(self.root), "generation": packet["generation"], + "image_sha256": packet["image_sha256"], "token": token or f"fixture-{self.counter}", + "block_box": located["block"]["box"], "decision": decision} + if decision == "move": + proposal["target"] = target or {"x": vision.image_world_center(located["pusher"]["box"])[0] + .10, "y": 0} + return proposal + + def submit(self, proposal): + content = vision.body.encoded(proposal) + path = self.work / f"proposal-{self.counter}-{proposal['token']}.json" + if not path.exists(): + vision.body.create_file(path, content) + else: + path.write_bytes(content) + return vision.apply(self.root, str(path), vision.body.sha(content)), path, vision.body.sha(content) + + def finish_loop(self): + contact_residual = False + for _ in range(8): + packet, _, located = self.view() + center = vision.image_world_center(located["block"]["box"]) + if .185 <= center[0] <= .255 and abs(center[1]) <= .035: + proposal = self.proposal(packet, located, "complete") + result, path, digest = self.submit(proposal) + self.assertEqual(result["phase"], "staged") + return result, path, digest, contact_residual + target = {"x": min(.16, vision.image_world_center(located["pusher"]["box"])[0] + .10), "y": 0} + result, _, _ = self.submit(self.proposal(packet, located, target=target)) + self.assertEqual(result["phase"], "executed") + next_packet, _, next_located = self.view() + next_center = vision.image_world_center(next_located["block"]["box"]) + if result["contact_statistics"]["pusher_block_contact_samples"] > 0 and next_center[0] < .185: + contact_residual = True + self.assertGreater(next_packet["generation"], packet["generation"]) + self.fail("bounded fixture loop did not finish") + + def assert_no_oracle(self, value): + output = json.dumps(value) + for forbidden in ("block_xyz", "pusher_xyz", "physical_condition_verified", "block_center_in_zone", "physical_readback", "qpos", "xpos"): + self.assertNotIn(forbidden, output) + + def test_native_rgb_calibration_full_silhouette_and_no_oracle_fields(self): + packet, image, located = self.view() + self.assertEqual(image.shape, (360, 640, 3)) + block = located["block"]["box"] + self.assertEqual((block[2] - block[0], block[3] - block[1]), (42, 42)) + self.assertAlmostEqual(vision.image_world_center(block)[0], -.12, delta=.002) + self.assertAlmostEqual(vision.image_world_center(located["pusher"]["box"])[0], -.30, delta=.002) + self.assert_no_oracle(packet) + receipt, _, _ = self.submit(self.proposal(packet, located)) + self.assert_no_oracle(receipt) + + def test_premature_visual_completion_refuses_before_native_effect(self): + packet, _, located = self.view() + proposal = self.proposal(packet, located, "complete") + with patch.object(vision.body, "complete") as complete, self.assertRaises(vision.Refusal): + self.submit(proposal) + complete.assert_not_called() + self.assertFalse(vision.proposal_path(self.root, proposal["token"]).exists()) + self.assertFalse((self.root / "order.json").exists()) + + def test_wrong_box_reverse_oversize_and_lateral_targets_refuse(self): + packet, _, located = self.view() + original = self.proposal(packet, located) + variants = [copy.deepcopy(original) for _ in range(4)] + variants[0]["block_box"] = [value + 20 for value in original["block_box"]] + variants[1]["target"]["x"] = -.31 + variants[2]["target"]["x"] = -.1 + variants[3]["target"]["y"] = .02 + for proposal in variants: + with self.subTest(proposal=proposal), patch.object(vision.body, "act") as act, self.assertRaises(vision.Refusal): + self.submit(proposal) + act.assert_not_called() + self.assertEqual(vision.body.load(self.root)[0]["generation"], 0) + + def test_bad_silhouettes_reject_blank_multiple_occluded_and_off_frame(self): + _, image, located = self.view() + blank = np.zeros_like(image) + occluded = image.copy() + left, top, right, bottom = located["block"]["box"] + occluded[top:bottom, left:(left + right) // 2] = 0 + multiple = image.copy() + multiple[40:82, 400:442] = image[top:bottom, left:right] + off_frame = image.copy() + off_frame[top:bottom, left:right] = 0 + off_frame[0:42, 0:42] = image[top:bottom, left:right] + for name, candidate in (("blank", blank), ("occluded", occluded), ("multiple", multiple), ("off-frame", off_frame)): + with self.subTest(name=name), self.assertRaises(vision.Refusal): + vision.locate(candidate) + + def test_tampered_image_stale_proposal_and_changed_token_never_step(self): + packet, _, located = self.view() + proposal = self.proposal(packet, located) + result, path, digest = self.submit(proposal) + self.assertEqual(result["phase"], "executed") + with patch.object(vision.body, "act") as act: + repeat = vision.apply(self.root, str(path), digest) + act.assert_not_called() + self.assertEqual(repeat["phase"], "executed") + changed = copy.deepcopy(proposal) + changed["target"]["x"] += .01 + with self.assertRaises(vision.Refusal): + self.submit(changed) + stale = copy.deepcopy(proposal) + stale["token"] = "stale" + with self.assertRaises(vision.Refusal): + self.submit(stale) + next_packet, _, next_located = self.view() + image_path = Path(next_packet["image_path"]) + image_path.write_bytes(image_path.read_bytes() + b"changed") + with self.assertRaises(vision.Refusal): + self.submit(self.proposal(next_packet, next_located)) + + def test_partial_view_is_not_rerendered_or_promoted(self): + with patch.object(vision, "render_rgb", side_effect=vision.Refusal("injected render failure")), self.assertRaises(vision.Refusal): + vision.view(self.root) + with patch.object(vision, "render_rgb") as render, self.assertRaises(vision.Refusal): + vision.view(self.root) + render.assert_not_called() + receipt = vision.body.decoded(vision.body.read_bytes(vision.image_paths(self.root, 0)[1])) + self.assertEqual(receipt["phase"], "pending") + + def test_lost_wrapper_receipt_blocks_replay_and_new_views(self): + packet, _, located = self.view() + proposal = self.proposal(packet, located) + original = vision.body.utils.persist + + def lose(path, record): + if path.name.startswith("vision-op-") and record.get("phase") == "executed": + raise OSError("injected wrapper receipt loss") + original(path, record) + + with patch.object(vision.body.utils, "persist", side_effect=lose): + result, path, digest = self.submit(proposal) + self.assertEqual(result["phase"], "unknown") + with patch.object(vision.body, "act") as act: + self.assertEqual(vision.apply(self.root, str(path), digest)["phase"], "unknown") + act.assert_not_called() + with self.assertRaises(vision.Refusal): + self.view() + + def test_rgb_feedback_correction_and_final_private_oracle_agreement(self): + result, _, _, had_residual = self.finish_loop() + self.assertTrue(had_residual) + self.assert_no_oracle(result) + self.assertTrue(result["current_software_artifact_check"]["ok"]) + # Native pose is used only for this final independent evaluator assertion, never targets. + native = vision.body.observation(self.root) + pixel_center = result["historical_visual_check"]["image_center_world_xy"] + self.assertTrue(native["physical_condition_verified"]) + self.assertAlmostEqual(pixel_center[0], native["block_xyz"][0], delta=.005) + + def test_current_result_tamper_or_missing_preserves_historical_staging_without_replay(self): + _, path, digest, _ = self.finish_loop() + output = self.root / "order.json" + output.write_bytes(b"changed") + repeated = vision.apply(self.root, str(path), digest) + self.assertEqual(repeated["phase"], "unknown") + self.assertEqual(repeated["historical_phase"], "staged") + self.assertTrue(repeated["historical_software_artifact_check"]["historical"]) + self.assertFalse(repeated["current_software_artifact_check"]["ok"]) + self.assert_no_oracle(repeated) + output.unlink() + repeated = vision.apply(self.root, str(path), digest) + self.assertEqual(repeated["phase"], "unknown") + self.assertFalse(output.exists()) + + def test_tampered_task_metadata_and_historical_receipt_refuse(self): + packet, _, located = self.view() + receipt_path = vision.image_paths(self.root, 0)[1] + before = receipt_path.read_bytes() + changed = dict(packet, task_intent={**packet["task_intent"], "part_id": "another-object"}) + vision.body.utils.persist(receipt_path, changed) + with self.assertRaises(vision.Refusal): + self.view() + receipt_path.write_bytes(before) + result, path, digest = self.submit(self.proposal(packet, located)) + record_path = vision.proposal_path(self.root, result["token"]) + receipt = vision.body.decoded(record_path.read_bytes()) + receipt["after_state_sha256"] = "0" * 64 + vision.body.utils.persist(record_path, receipt) + with self.assertRaises(vision.Refusal): + vision.apply(self.root, str(path), digest) + + def test_source_freeze_and_png_limits_are_enforced(self): + packet, _, _ = self.view() + with patch.object(vision, "fingerprint", return_value={"changed": True}), self.assertRaises(vision.Refusal): + self.view() + content = Path(packet["image_path"]).read_bytes() + for changed in (content[:30], content + b"x", content[:60] + b"broken" + content[60:]): + with self.assertRaises(vision.Refusal): + vision.decode_png(changed) + + def test_forged_oracle_extra_field_or_perception_scope_is_not_returned(self): + packet, _, located = self.view() + receipt_path = vision.image_paths(self.root, 0)[1] + original = receipt_path.read_bytes() + for changed in (dict(packet, block_xyz=[0.22, 0, 0.035]), + dict(packet, perception_scope="native oracle verified the block")): + with self.subTest(changed=changed): + vision.body.utils.persist(receipt_path, changed) + with self.assertRaises(vision.Refusal): + self.view() + with patch.object(vision.body, "act") as act, self.assertRaises(vision.Refusal): + self.submit(self.proposal(packet, located)) + act.assert_not_called() + process = subprocess.run([sys.executable, str(BASE / "image_guided.py"), "view", "--run-dir", str(self.root)], + capture_output=True, text=True, check=False, timeout=30) + self.assertEqual(process.returncode, 2) + self.assertEqual(json.loads(process.stdout)["phase"], "refused") + self.assert_no_oracle(json.loads(process.stdout)) + receipt_path.write_bytes(original) + + def test_public_fresh_process_view_apply_repeats_are_filtered(self): + packet, _, located = self.view() + proposal = self.proposal(packet, located) + path = self.work / "public-proposal.json" + content = vision.body.encoded(proposal) + vision.body.create_file(path, content) + args = [sys.executable, str(BASE / "image_guided.py"), "apply", "--run-dir", str(self.root), + "--proposal-file", str(path), "--proposal-sha256", vision.body.sha(content)] + for _ in range(2): + process = subprocess.run(args, capture_output=True, text=True, check=False, timeout=30) + self.assertEqual(process.returncode, 0, process.stdout + process.stderr) + result = json.loads(process.stdout) + self.assertEqual(result["phase"], "executed") + self.assert_no_oracle(result) + view = subprocess.run([sys.executable, str(BASE / "image_guided.py"), "view", "--run-dir", str(self.root)], + capture_output=True, text=True, check=False, timeout=30) + self.assertEqual(view.returncode, 0, view.stdout + view.stderr) + self.assertEqual(json.loads(view.stdout)["generation"], 1) + self.assert_no_oracle(json.loads(view.stdout)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_measurement.py b/tools/physical-workcell/test_measurement.py new file mode 100644 index 0000000..1725650 --- /dev/null +++ b/tools/physical-workcell/test_measurement.py @@ -0,0 +1,104 @@ +"""Synthetic measurement-worker storage boundaries; no renderer or live qualification.""" + +import os +import sqlite3 +import stat +import tempfile +import unittest +from pathlib import Path +from unittest import mock + +import measurement + + +class MeasurementStorageTests(unittest.TestCase): + def setUp(self): + protocol = measurement.protocol + protocol.OUTPUT.mkdir(mode=0o700, exist_ok=True) + self.temporary = tempfile.TemporaryDirectory(prefix="measurement-storage-", dir=protocol.OUTPUT) + self.addCleanup(self.temporary.cleanup) + self.root = Path(self.temporary.name) + self.requests = protocol.directory(self.root / "requests", create=True) + images = protocol.directory(self.root / "images", create=True) + image = images / "synthetic.png" + protocol.create_file(image, b"synthetic image boundary") + self.packet = {"bench_id": "bench", "job_id": "job", "generation": 1, "native_time_s": 0, + "image_sha256": protocol.sha(image.read_bytes()), "state_sha256": "a" * 64, + "image_path": str(image)} + self.packet_file = self.requests / "packet.json" + packet_bytes = protocol.encoded(self.packet) + protocol.create_file(self.packet_file, packet_bytes) + self.packet_sha = protocol.sha(packet_bytes) + manifest = {"bench_id": "bench", "generation": 1, "state_sha256": "a" * 64, + "active_job": {"job_id": "job"}, "busy": {"operation_id": "measure", "kind": "measure"}} + protocol.create_file(self.root / "bench.json", protocol.encoded(manifest)) + self.compute = mock.patch.object(measurement, "compute", return_value={ + "method": "simulated_orange_RGB_footprint", "orange_pixel_count": 4, + "centroid_pixel_xy": [2., 2.], "block_box": [1, 1, 3, 3], + "block_center_xy": [0., 0.], "footprint_area_m2": .0004}) + self.compute.start() + self.addCleanup(self.compute.stop) + self.database = self.root / "measurements.sqlite" + + def run_worker(self): + return measurement.run(self.root, "measure", self.packet_file, self.packet_sha) + + def test_crash_immediately_after_sqlite_open_leaves_private_file(self): + connect = sqlite3.connect + + def crash(path, *args, **kwargs): + connection = connect(path, *args, **kwargs) + connection.close() + raise RuntimeError("synthetic crash before insert") + + previous_umask = os.umask(0o022) + try: + with (mock.patch.object(measurement.sqlite3, "connect", side_effect=crash), + self.assertRaisesRegex(RuntimeError, "synthetic crash before insert")): + self.run_worker() + finally: + os.umask(previous_umask) + self.assertEqual(stat.S_IMODE(self.database.stat().st_mode), 0o600) + self.assertEqual(self.database.stat().st_uid, os.getuid()) + + def test_existing_public_database_is_refused_without_chmod_or_connection(self): + with sqlite3.connect(self.database) as connection: + connection.execute(measurement.SCHEMA) + self.database.chmod(0o644) + before = self.database.read_bytes() + with mock.patch.object(measurement.sqlite3, "connect", wraps=sqlite3.connect) as connect: + with self.assertRaises(measurement.body.Refusal): + self.run_worker() + connect.assert_not_called() + self.assertEqual(self.database.read_bytes(), before) + self.assertEqual(stat.S_IMODE(self.database.stat().st_mode), 0o644) + + def test_symlink_database_is_refused_without_touching_target(self): + target = self.root / "other" + measurement.protocol.create_file(target, b"unrelated") + self.database.symlink_to(target) + with mock.patch.object(measurement.sqlite3, "connect") as connect: + with self.assertRaises(measurement.body.Refusal): + self.run_worker() + connect.assert_not_called() + self.assertEqual(target.read_bytes(), b"unrelated") + + def test_existing_private_unknown_database_is_not_repaired_or_replaced(self): + measurement.protocol.create_file(self.database, b"not a SQLite database") + identity = self.database.stat().st_ino + with self.assertRaises(sqlite3.DatabaseError): + self.run_worker() + self.assertEqual(self.database.read_bytes(), b"not a SQLite database") + self.assertEqual(self.database.stat().st_ino, identity) + + def test_private_database_returns_exact_historical_row_without_duplicate_insert(self): + first = self.run_worker() + with mock.patch.object(measurement.os, "getpid", return_value=999999): + self.assertEqual(self.run_worker(), first) + self.assertEqual(measurement.read_result(self.root, "measure"), first) + with sqlite3.connect(self.database) as connection: + self.assertEqual(connection.execute("SELECT COUNT(*) FROM measurements").fetchone()[0], 1) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_network_body_faults.py b/tools/physical-workcell/test_network_body_faults.py new file mode 100644 index 0000000..b9f47c9 --- /dev/null +++ b/tools/physical-workcell/test_network_body_faults.py @@ -0,0 +1,731 @@ +"""SIM9 mechanical TCP faults; deterministic RGB conformance is not AI evidence. + +Each case retains inputs, socket replies, native proof, logs and its outcome under +output/n9test-*. Only this harness's child processes are stopped during cleanup. +""" + +import concurrent.futures +import copy +import hashlib +import http.client +import json +import math +import socket +import sqlite3 +import subprocess +import sys +import threading +import time +import unittest +import uuid +from pathlib import Path +from urllib.parse import urlencode, urlsplit + +import body_client +import image_guided as vision +import network_fault_proxy +import order_protocol as storage +import workcell as body + +BASE = Path(__file__).resolve().parent +ZONE = {"x_min": .04, "x_max": .11, "y_min": -.025, "y_max": .025} +CORE_FILES = ("body_protocol.py", "body_network.py", "body_client.py", "network_fault_proxy.py", + "agent_bench.py", "bench.py", "measurement.py", "workcell.py", "image_guided.py", + "episode_cases.py", "order_protocol.py", "scene.xml", "../camera-capture-experiment/capture.py") + + +def retain(path, value): + storage.create_file(path, storage.encoded(value)) + + +def port(): + with socket.socket() as connection: + connection.bind(("127.0.0.1", 0)) + return connection.getsockname()[1] + + +def tcp(origin, method, path, value=None, pinned=None, headers=None, timeout=15): + """Exercise the same public wire as clients, never call service methods.""" + parsed = urlsplit(origin) + request_headers = {} + if pinned: + request_headers.update({"X-Body-Id": pinned["body_id"], + "X-Adapter-Id": pinned["adapter_id"], + "X-Source-Sha256": pinned["source_sha256"]}) + content = None + if value is not None: + content = storage.encoded(value) + request_headers["Content-Type"] = "application/json" + request_headers.update(headers or {}) + connection = http.client.HTTPConnection(parsed.hostname, parsed.port, timeout=timeout) + try: + connection.request(method, path, content, request_headers) + response = connection.getresponse() + data = response.read(storage.MAX_BYTES + 1) + return {"http_status": response.status, "body": storage.decode(data)} + finally: + connection.close() + + +class NetworkRGBPlanner: + """Only public client calls and their hash-checked PNGs guide this policy.""" + + def __init__(self, client, record): + self.client, self.record = client, record + self.actions = 0 + + def observe(self): + self.client.acquire() + challenge = self.client.observe() + packet = challenge["observation"] + content = Path(challenge["image_path"]).read_bytes() + if hashlib.sha256(content).hexdigest() != packet["image_sha256"]: + raise AssertionError("downloaded public image digest differs") + if not packet["calibration"]["valid"]: + raise AssertionError("RGB policy requires valid published calibration") + located = vision.locate(vision.decode_png(content)) + block = vision.image_world_center(located["block"]["box"]) + pusher = vision.image_world_center(located["pusher"]["box"]) + self.record("deterministic_RGB_observation", challenge=challenge, + rgb_block_xy=block, rgb_pusher_xy=pusher) + return challenge, block, pusher + + def dispatch(self, challenge, action, intent, target=None): + self.actions += 1 + if self.actions > 64: + raise AssertionError("deterministic RGB action budget exhausted") + self.client.acquire() + proposal = self.client.propose(challenge["challenge_id"], action, intent, + *(target or (None, None))) + result = self.client.dispatch(proposal["proposal_id"]) + self.record("deterministic_RGB_dispatch", proposal=proposal, result=result) + if result["outcome"] != "executed": + raise AssertionError("RGB action did not execute: " + str(result)) + return result + + def move(self, challenge, pusher, target, intent): + delta = [target[i] - pusher[i] for i in (0, 1)] + distance = math.hypot(*delta) + if distance > .11: + target = [pusher[i] + delta[i] * .11 / distance for i in (0, 1)] + return self.dispatch(challenge, "move", intent, target) + + def position(self, center, intent): + for _ in range(48): + challenge, block, pusher = self.observe() + if abs(block[1]) > .018: + raise AssertionError("RGB block left supported axial corridor") + if abs(block[0] - center) <= .012: + return + direction = 1 if center > block[0] else -1 + side = block[0] - direction * .065 + if direction * (block[0] - pusher[0]) < .050: + target = [pusher[0], .135] if pusher[1] < .125 else [side, .135] + elif abs(pusher[1]) > .006: + target = [side, pusher[1]] if abs(pusher[0] - side) > .010 else [pusher[0], 0] + else: + gap = max(0, direction * (block[0] - pusher[0]) - .055) + target = [pusher[0] + direction * min(.09, abs(center - block[0]) + gap), 0] + if not -.32 <= target[0] <= .18: + raise AssertionError("RGB target outside published workspace") + self.move(challenge, pusher, target, intent) + raise AssertionError("RGB positioning did not converge") + + def park(self): + for _ in range(24): + challenge, block, pusher = self.observe() + if abs(pusher[0] + .3) <= .004 and abs(pusher[1]) <= .004: + return challenge + if pusher[0] > block[0] - .065 and pusher[1] < .125: + target = [pusher[0], .135] + elif pusher[0] > -.29: + target = [-.3, pusher[1]] + else: + target = [-.3, 0] + self.move(challenge, pusher, target, "deterministic RGB clear and park pusher") + raise AssertionError("RGB parking did not converge") + + def cycle(self, job_id, zone): + self.actions = 0 + self.client.acquire() + started = self.client.start_job(job_id, zone) + self.record("network_job_started", result=started) + self.position((zone["x_min"] + zone["x_max"]) / 2, "deterministic RGB prepare part") + measured = self.dispatch(self.park(), "measure", "measure image-guided placement") + challenge, _, _ = self.observe() + self.dispatch(challenge, "begin_reset", "start contact reset after measurement") + self.position(-.12, "deterministic RGB contact return to home") + reset = self.dispatch(self.park(), "finish_reset", "verify RGB part and pusher at home") + return {"job_id": job_id, "zone": zone, "measurement": measured, + "reset": reset, "actions": self.actions} + + +class NetworkBodyFaultTests(unittest.TestCase): + def setUp(self): + self.root = BASE / "output" / ("n9test-" + uuid.uuid4().hex[:10]) + self.root.mkdir(mode=0o700) + self.server_root = self.root / "body" + self.events, self.processes, self.logs, self.proxies, self.clients = [], [], [], [], [] + self.sources = {name: hashlib.sha256((BASE / name).read_bytes()).hexdigest() + for name in CORE_FILES} + self.process = None + self.client = None + self.record("case_started", test_id=self.id(), preregistration="c976508", + planner="mechanical_deterministic_RGB_only_not_AI") + + def tearDown(self): + for client in reversed(self.clients): + client.close() + for proxy in reversed(self.proxies): + try: + proxy.close() + except Exception as exc: # noqa: BLE001 - retain cleanup failures while reaping owned children. + self.record("proxy_cleanup_error", error=str(exc)) + for process in self.processes: + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=5) + except subprocess.TimeoutExpired: + process.kill() + process.wait(timeout=5) + for handle in self.logs: + handle.close() + result = self._outcome.result + errors = [trace for test, trace in (*result.failures, *result.errors) + if test is self or getattr(test, "test_case", None) is self] + unchanged = self.sources == {name: hashlib.sha256((BASE / name).read_bytes()).hexdigest() + for name in CORE_FILES} + retain(self.root / "conformance.json", {"test_id": self.id(), "passed": not errors and unchanged, + "source_unchanged": unchanged, "source_sha256": self.sources, + "scope": "real_loopback_TCP_native_body_mechanical_fault_matrix", + "processes_reaped": all(p.poll() is not None for p in self.processes), + "errors": errors, "events": self.events}) + self.assertTrue(unchanged, "network/core source changed during mechanical case") + + def record(self, kind, **value): + event = {"sequence": len(self.events), "kind": kind, **value} + retain(self.root / f"event-{len(self.events):04d}.json", event) + self.events.append(event) + return value + + def start(self, *, initialize=True, barrier=None): + number = len(self.processes) + chosen_port = getattr(self, "server_port", None) or port() + self.server_port = chosen_port + self.origin = f"http://127.0.0.1:{chosen_port}" + log = (self.root / f"service-{number}.log").open("xb") + self.logs.append(log) + command = [sys.executable, str(BASE / "body_network.py"), "--root", str(self.server_root), + "--port", str(chosen_port)] + if initialize: + command += ["--initialize", "--diagnostic"] + if barrier: + command += ["--barrier", barrier] + self.process = subprocess.Popen(command, stdout=log, stderr=log) + self.processes.append(self.process) + deadline = time.monotonic() + 20 + while True: + try: + reply = tcp(self.origin, "GET", "/identity", timeout=.5) + if reply["http_status"] == 200: + self.identity = reply["body"] + break + except (OSError, http.client.HTTPException, ValueError): + pass + if self.process.poll() is not None or time.monotonic() >= deadline: + raise AssertionError("owned body service did not become ready; inspect retained service log") + time.sleep(.05) + self.record("service_ready", identity=self.identity, pid=self.process.pid, + initialize=initialize, barrier=barrier) + return self.identity + + def stop(self, *, crash=False): + process = self.process + if process.poll() is None: + if crash: + process.kill() + else: + process.terminate() + process.wait(timeout=8) + self.record("owned_service_stopped", pid=process.pid, crashed=crash, + returncode=process.returncode) + + def connect(self, *, timeout=15, proxy=False): + origin = self.origin + if proxy: + self.proxy = network_fault_proxy.FaultProxy(origin, self.root / "proxy") + self.proxy.start() + self.proxies.append(self.proxy) + origin = self.proxy.origin + self.client = body_client.BodyClient(self.root / "client", origin, timeout=timeout) + self.clients.append(self.client) + self.lease = self.client.acquire() + self.record("client_acquired", lease=self.lease, via_proxy=proxy) + return self.client + + def state(self): + reply = tcp(self.origin, "GET", "/state", pinned=self.identity) + self.assertEqual(reply["http_status"], 200, reply) + return reply["body"] + + def generation(self): + return self.state()["bench"]["generation"] + + def proposal(self): + self.client.start_job("fault-job", ZONE) + challenge = self.client.observe() + proposal = self.client.propose(challenge["challenge_id"], "move", + "mechanical network conformance motion", -.25, 0) + self.record("public_proposal", challenge=challenge, proposal=proposal) + return proposal + + def command(self, operation, arguments, *, token=None): + return {**{k: self.identity[k] for k in ("protocol", "adapter_id", "body_id", "source_sha256")}, + **{k: self.lease[k] for k in ("client_id", "lease_id", "epoch")}, + "command_id": token or "matrix-" + uuid.uuid4().hex, + "operation": operation, "arguments": arguments} + + def send(self, request, *, pinned=None): + reply = tcp(self.origin, "POST", "/commands", request, pinned=pinned or self.identity) + self.record("raw_public_command", request=request, reply=reply) + return reply + + def retained_dispatch_request(self, command_id): + # Fault assertions may inspect outgoing bytes. Policy never reads these. + for path in (self.root / "client").rglob("*.json"): + try: + value = json.loads(path.read_bytes()) + except (ValueError, OSError): + continue + if isinstance(value, dict) and value.get("command_id") == command_id and value.get("operation") == "dispatch": + return value + if isinstance(value, dict) and isinstance(value.get("request"), dict): + request = value["request"] + if request.get("command_id") == command_id and request.get("operation") == "dispatch": + return request + raise AssertionError("immutable outgoing dispatch request not found") + + def uncertain_dispatch(self, proposal): + with self.assertRaises(body_client.Uncertain) as error: + self.client.dispatch(proposal["proposal_id"]) + token = error.exception.command_id + self.record("client_uncertain", command_id=token, status=self.client.status()) + return token + + def reconcile(self, token, *, expected=None): + if expected in {"unknown", "not_seen"}: + with self.assertRaises(body_client.Uncertain) as error: + self.client.reconcile(token) + self.assertEqual(error.exception.command_id, token) + request = self.retained_dispatch_request(token) + query = urlencode({**{key: request[key] for key in ("client_id", "lease_id", "epoch")}, + "request_sha256": storage.sha(storage.encoded(request))}) + response = tcp(self.origin, "GET", "/commands/" + token + "?" + query, pinned=self.identity) + self.assertEqual(response["http_status"], 200) + reply = response["body"] + self.assertEqual(reply["state"], expected) + self.assertIn(token, self.client.status()["pending_command_ids"]) + else: + reply = self.client.reconcile(token) + self.record("read_only_reconciliation", command_id=token, reply=reply) + return reply + + def assert_single_send(self, token): + journal = storage.decode((self.root / "client" / "client.json").read_bytes()) + request = self.retained_dispatch_request(token) + entry = journal["commands"][token] + self.assertEqual(entry["send_count"], 1) + self.assertEqual(entry["request_sha256"], storage.sha(storage.encoded(request))) + self.record("independent_single_send_journal_check", command_id=token, + request_sha256=entry["request_sha256"], send_count=entry["send_count"]) + + def journal_count(self): + return len(list((self.server_root / "commands").glob("*/intent.json"))) + + def barrier(self, expected): + path = self.server_root / "barrier.json" + deadline = time.monotonic() + 12 + while time.monotonic() < deadline: + if path.exists(): + value = storage.decode(path.read_bytes()) + self.assertEqual(value["barrier"], expected) + self.assertEqual(value["pid"], self.process.pid) + self.record("actual_process_barrier", evidence=value) + return value + if self.process.poll() is not None: + break + time.sleep(.02) + raise AssertionError("owned service never reached crash barrier") + + def frozen_effects(self): + """Independent persisted effects; no authority inferred from current scene.""" + roots = (self.server_root / "bench", self.server_root / "agent") + return {str(path.relative_to(self.server_root)): hashlib.sha256(path.read_bytes()).hexdigest() + for root in roots for path in root.rglob("*") if path.is_file() + and path.name != "app.lock"} + + def assert_no_private_pose(self, value): + if isinstance(value, dict): + self.assertFalse({"block_xyz", "pusher_xyz", "qpos", "qvel", "values", "native_pose"} & value.keys()) + for item in value.values(): + self.assert_no_private_pose(item) + elif isinstance(value, list): + for item in value: + self.assert_no_private_pose(item) + + def score_native(self, receipt, zone): + root = self.server_root / "bench" + content = body.read_bytes(body.state_path(root, receipt["generation"])) + self.assertEqual(body.sha(content), receipt["state_sha256"]) + state = body.decoded(content) + manifest = storage.decode((root / "bench.json").read_bytes()) + self.assertEqual(hashlib.sha256((root / "model.xml").read_bytes()).hexdigest(), manifest["model_sha256"]) + model = body.mujoco.MjModel.from_xml_path(str(root / "model.xml")) + data = body.mujoco.MjData(model) + body.mujoco.mj_setState(model, data, body.np.asarray(state["values"]), + body.mujoco.mjtState.mjSTATE_INTEGRATION) + body.mujoco.mj_forward(model, data) + block = data.xpos[model.body("block").id].tolist() + pusher = data.xpos[model.body("pusher").id].tolist() + proof = {"role": "independent_native_read_only_scorer", "block_xyz": block, + "pusher_xyz": pusher, "generation": receipt["generation"], + "state_sha256": receipt["state_sha256"], "native_time_s": float(data.time)} + self.record("independent_native_proof", proof=proof) + self.assertTrue(zone["x_min"] <= block[0] <= zone["x_max"]) + self.assertTrue(zone["y_min"] <= block[1] <= zone["y_max"]) + self.assertTrue(.02 <= block[2] <= .08) + self.assertFalse(body.np.any(data.xfrc_applied)) + return proof + + def test_01_successive_network_cycles_with_RGB_only_policy(self): + self.start() + self.connect() + planner = NetworkRGBPlanner(self.client, self.record) + first = planner.cycle("tcp-cycle-one", ZONE) + second_zone = {"x_min": .035, "x_max": .105, "y_min": -.025, "y_max": .025} + second = planner.cycle("tcp-cycle-two", second_zone) + public = self.state() + self.assert_no_private_pose(public) + self.stop() + root = self.server_root / "bench" + manifest = storage.decode((root / "bench.json").read_bytes()) + self.assertIsNone(manifest["active_job"]) + self.assertEqual(len(manifest["jobs"]), 2) + with sqlite3.connect((root / "measurements.sqlite").as_uri() + "?mode=ro", uri=True) as db: + rows = db.execute("SELECT job_id,bench_id,generation,state_sha256,receipt_json FROM measurements ORDER BY job_id").fetchall() + self.record("independent_SQLite_readback", rows=rows) + self.assertEqual(len(rows), 2) + previous_generation = 0 + previous_time = None + for cycle in (first, second): + job = manifest["jobs"][cycle["job_id"]] + measured = job["measurement_receipt"] + reset = job["reset_receipt"] + self.assertEqual(job["phase"], "completed") + self.assertEqual(job["started_generation"], previous_generation) + if previous_time is not None: + self.assertEqual(job["started_native_time_s"], previous_time) + stage = self.score_native(measured, cycle["zone"]) + home = self.score_native(reset, {"x_min": -.16, "x_max": -.08, "y_min": -.03, "y_max": .03}) + self.assertLess(stage["native_time_s"], home["native_time_s"]) + self.assertAlmostEqual(home["pusher_xyz"][0], -.3, delta=.005) + self.assertAlmostEqual(home["pusher_xyz"][1], 0, delta=.005) + self.assertGreater(reset["reset_contact_samples"], 0) + self.assertGreater(reset["reset_native_steps"], 0) + self.assertEqual(measured["bench_id"], self.identity["body_id"]) + row = next(row for row in rows if row[0] == cycle["job_id"]) + self.assertEqual(json.loads(row[4]), measured) + self.assertEqual(row[1:4], (measured["bench_id"], measured["generation"], measured["state_sha256"])) + for key, item in (("measurement", cycle["measurement"]), ("reset", cycle["reset"])): + self.assertEqual(item["outcome"], "executed", key) + previous_generation, previous_time = job["completed_generation"], job["completed_native_time_s"] + + def test_02_bounded_delays_preserve_exact_effect(self): + self.start() + self.connect(proxy=True) + proposal = self.proposal() + for mode in ("delay_before", "delay_after"): + if mode == "delay_after": + challenge = self.client.observe() + proposal = self.client.propose(challenge["challenge_id"], "move", "second delayed motion", -.2, 0) + before = self.generation() + self.proxy.arm(mode, operation="dispatch", delay_s=.15) + started = time.monotonic() + result = self.client.dispatch(proposal["proposal_id"]) + elapsed = time.monotonic() - started + self.record("bounded_delay_result", mode=mode, elapsed_s=elapsed, result=result) + self.assertGreaterEqual(elapsed, .13) + self.assertLess(elapsed, 10) + self.assertEqual(result["outcome"], "executed") + self.assertEqual(self.generation(), before + 1) + + def test_03_drop_before_acceptance_stays_pending_until_fenced(self): + self.start() + self.connect(proxy=True) + proposal = self.proposal() + self.client.acquire(duration_s=2) + before, count = self.generation(), self.journal_count() + self.proxy.arm("drop_before", operation="dispatch") + token = self.uncertain_dispatch(proposal) + self.assertEqual(self.reconcile(token, expected="not_seen")["state"], "not_seen") + with self.assertRaises((body_client.Refusal, body_client.Uncertain)): + self.client.observe() + self.assertEqual(self.journal_count(), count) + time.sleep(2.1) + self.assertEqual(self.reconcile(token)["state"], "not_accepted") + self.assertEqual(self.generation(), before) + self.assertEqual(self.journal_count(), count) + self.assert_single_send(token) + self.client.acquire() + self.client.observe() + + def test_04_drop_after_effect_reconciles_without_resending(self): + self.start() + self.connect(proxy=True) + proposal = self.proposal() + before, count = self.generation(), self.journal_count() + self.proxy.arm("drop_after", operation="dispatch") + token = self.uncertain_dispatch(proposal) + effects = self.frozen_effects() + reconciled = self.reconcile(token) + self.assertEqual(reconciled["state"], "completed") + self.assertEqual(reconciled["result"]["outcome"], "executed") + self.assertEqual(self.reconcile(token), reconciled) + self.assertEqual(self.generation(), before + 1) + self.assertEqual(self.journal_count(), count + 1) + self.assertEqual(self.frozen_effects(), effects) + self.assert_single_send(token) + + def test_05_exact_concurrent_duplicate_has_one_result_one_motion(self): + self.start() + self.connect() + proposal = self.proposal() + request = self.command("dispatch", {"proposal_id": proposal["proposal_id"]}) + before, count = self.generation(), self.journal_count() + gate = threading.Barrier(3) + + def concurrent_send(): + gate.wait(timeout=5) + started = time.monotonic() + reply = tcp(self.origin, "POST", "/commands", request, self.identity) + return {"started": started, "ended": time.monotonic(), "reply": reply} + + with concurrent.futures.ThreadPoolExecutor(max_workers=2) as pool: + futures = [pool.submit(concurrent_send) for _ in range(2)] + gate.wait(timeout=5) + calls = [future.result(timeout=20) for future in futures] + replies = [call["reply"] for call in calls] + self.assertLess(max(call["started"] for call in calls), min(call["ended"] for call in calls)) + self.record("actual_concurrent_duplicate", request=request, calls=calls) + self.assertEqual(replies[0], replies[1]) + self.assertEqual(replies[0]["body"]["state"], "completed") + self.assertEqual(replies[0]["body"]["result"]["outcome"], "executed") + self.assertEqual(self.generation(), before + 1) + self.assertEqual(self.journal_count(), count + 1) + frozen = self.frozen_effects() + self.assertEqual(self.send(request), replies[0]) + self.assertEqual(self.frozen_effects(), frozen) + + def test_06_conflicting_duplicate_refuses_without_second_effect(self): + self.start() + self.connect() + proposal = self.proposal() + request = self.command("dispatch", {"proposal_id": proposal["proposal_id"]}) + self.assertEqual(self.send(request)["body"]["state"], "completed") + frozen, count = self.frozen_effects(), self.journal_count() + conflict = copy.deepcopy(request) + conflict["arguments"]["proposal_id"] = "proposal-" + uuid.uuid4().hex + reply = self.send(conflict) + self.assertIn(reply["body"]["state"], ("refused", "not_accepted")) + self.assertEqual(self.frozen_effects(), frozen) + self.assertEqual(self.journal_count(), count) + + def test_07_stale_image_proposal_cannot_dispatch_effect(self): + self.start() + self.connect() + proposal = self.proposal() + self.client.disturb("external-force", -.4, 50) + before = self.generation() + result = self.client.dispatch(proposal["proposal_id"]) + self.record("stale_public_dispatch", result=result) + self.assertEqual(result["outcome"], "refused") + self.assertEqual(self.generation(), before) + self.assertGreater(result["next_observation"]["observation"]["generation"], 0) + + def test_08_delay_past_lease_expiry_never_admits_late_effect(self): + self.start() + self.connect(proxy=True) + proposal = self.proposal() + self.client.acquire(duration_s=.2) + before, count = self.generation(), self.journal_count() + self.client.timeout = .1 + self.proxy.arm("delay_before", operation="dispatch", delay_s=.5) + token = self.uncertain_dispatch(proposal) + time.sleep(.7) + self.assertEqual(self.reconcile(token)["state"], "not_accepted") + self.proxy.close() # Drain actual late-forward threads before checking physical state. + events = [storage.decode(path.read_bytes()) for path in sorted((self.root / "proxy" / "events").glob("*.json"))] + self.assertTrue(any(event["kind"] == "forwarded" and event.get("command_id") == token for event in events)) + self.record("late_proxy_threads_drained", proxy_events=events) + self.assertEqual(self.generation(), before) + self.assertEqual(self.journal_count(), count) + self.assert_single_send(token) + self.record("late_admission_fenced", command_id=token) + + def test_09_identity_source_epoch_and_exclusive_lease_fence(self): + self.start() + self.connect() + proposal = self.proposal() + request = self.command("dispatch", {"proposal_id": proposal["proposal_id"]}) + frozen, count = self.frozen_effects(), self.journal_count() + for key, value in (("body_id", uuid.uuid4().hex), ("adapter_id", uuid.uuid4().hex), + ("source_sha256", "0" * 64), ("epoch", self.identity["epoch"] + 1)): + foreign = copy.deepcopy(request) + foreign[key] = value + reply = self.send(foreign) + self.assertIn(reply["body"]["state"], ("not_accepted", "refused")) + for key in ("body_id", "adapter_id", "source_sha256"): + headers = dict(self.identity) + headers[key] = "0" * (64 if key == "source_sha256" else 32) + self.assertEqual(self.send(request, pinned=headers)["http_status"], 409) + other = body_client.BodyClient(self.root / "other-client", self.origin) + self.clients.append(other) + with self.assertRaises(body_client.Refusal): + other.acquire() + old_lease = dict(self.lease) + self.lease = self.client.acquire() + self.assertNotEqual(old_lease["lease_id"], self.lease["lease_id"]) + self.assertEqual(self.send(request)["body"]["state"], "not_accepted") + self.assertEqual(self.frozen_effects(), frozen) + self.assertEqual(self.journal_count(), count) + + def test_10_process_restart_preserves_terminal_history_and_body(self): + self.start() + self.connect(proxy=True) + proposal = self.proposal() + self.proxy.arm("drop_after", operation="dispatch") + token = self.uncertain_dispatch(proposal) + original_identity = dict(self.identity) + effects = self.frozen_effects() + self.stop(crash=True) + self.start(initialize=False) + self.assertEqual(self.identity["body_id"], original_identity["body_id"]) + self.assertEqual(self.identity["adapter_id"], original_identity["adapter_id"]) + self.assertGreater(self.identity["epoch"], original_identity["epoch"]) + reply = self.reconcile(token) + self.assertEqual(reply["state"], "completed") + self.assertEqual(reply["recorded_epoch"], original_identity["epoch"]) + self.assertEqual(reply["result"]["outcome"], "executed") + self.assertEqual(self.frozen_effects(), effects) + self.assert_single_send(token) + self.client.acquire() + self.client.observe() + + def crash_at_boundary(self, boundary): + self.start(barrier=boundary) + self.connect() + proposal = self.proposal() + self.client.timeout = 1 + # Child SIGSTOP occurs before timeout and before this owned SIGKILL. + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + future = pool.submit(self.client.dispatch, proposal["proposal_id"]) + barrier = self.barrier(boundary) + self.stop(crash=True) + with self.assertRaises(body_client.Uncertain) as error: + future.result(timeout=5) + self.assertEqual(error.exception.command_id, barrier["command_id"]) + token = error.exception.command_id + self.record("crash_client_uncertain", command_id=token, boundary=boundary) + effects = self.frozen_effects() + self.start(initialize=False) + return token, effects + + def test_11_restart_recovers_exact_lower_receipt_without_dispatch(self): + token, effects = self.crash_at_boundary("after_dispatch") + reply = self.reconcile(token) + self.assertEqual(reply["state"], "completed") + self.assertEqual(reply["result"]["outcome"], "executed") + self.assertEqual(self.generation(), 1) + self.assertEqual(self.frozen_effects(), effects) + self.assertEqual(self.reconcile(token), reply) + self.assertEqual(self.frozen_effects(), effects) + self.assert_single_send(token) + + def test_12_accepted_without_lower_proof_and_client_restart_stays_unknown(self): + self.start(barrier="before_dispatch") + self.connect() + proposal = self.proposal() + self.client.close() + # Both dispatch and resumed readback have distinct OS process lifetimes. + script = """ +import json,sys +from pathlib import Path +import body_client +client=body_client.BodyClient(Path(sys.argv[1]),sys.argv[2],timeout=3) +try: + try: + client.dispatch(sys.argv[3]) + except body_client.Uncertain as exc: + print(json.dumps({'command_id':exc.command_id,'pending':client.status()['pending_command_ids']}),flush=True) +finally: + client.close() +""" + handle = (self.root / "client-before-crash.log").open("xb") + self.logs.append(handle) + child = subprocess.Popen([sys.executable, "-c", script, str(self.root / "client"), + self.origin, proposal["proposal_id"]], cwd=BASE, stdout=handle, stderr=handle) + self.processes.append(child) + barrier = self.barrier("before_dispatch") + self.stop(crash=True) + child.wait(timeout=8) + self.assertEqual(child.returncode, 0) + token = barrier["command_id"] + effects = self.frozen_effects() + self.start(initialize=False) + resume = """ +import json,sys +from pathlib import Path +import body_client +client=body_client.BodyClient(Path(sys.argv[1]),sys.argv[2],timeout=3) +blocked={} +try: + for name,call in [('reconcile',lambda:client.reconcile(sys.argv[3])),('observe',client.observe),('acquire',client.acquire)]: + try: + call() + blocked[name]=False + except body_client.Uncertain as exc: + blocked[name]=exc.command_id==sys.argv[3] + print(json.dumps({'blocked':blocked,'status':client.status(),'pid':__import__('os').getpid()}),flush=True) +finally: + client.close() +""" + completed = subprocess.run([sys.executable, "-c", resume, str(self.root / "client"), self.origin, token], + cwd=BASE, capture_output=True, timeout=12, check=False) + storage.create_file(self.root / "client-resume.stdout", completed.stdout) + storage.create_file(self.root / "client-resume.stderr", completed.stderr) + self.assertEqual(completed.returncode, 0, completed.stderr.decode()) + resumed = json.loads(completed.stdout) + self.record("actual_client_process_restart", old_pid=child.pid, public_result=resumed, + dispatch_client_exit_observed=child.poll() is not None, + dispatch_client_returncode=child.returncode, resumed_client_returncode=completed.returncode) + self.assert_single_send(token) + self.assertNotEqual(child.pid, resumed["pid"]) + self.assertEqual(resumed["blocked"], {"reconcile": True, "observe": True, "acquire": True}) + self.assertIn(token, resumed["status"]["pending_command_ids"]) + self.assertEqual(self.generation(), 0) + self.assertEqual(self.state()["state"], "unknown") + self.assertEqual(self.frozen_effects(), effects) + # A fresh holder cannot bypass the server's independent unresolved-intent guard. + fresh = body_client.BodyClient(self.root / "fresh-client", self.origin) + self.clients.append(fresh) + self.lease = fresh.acquire() + count = self.journal_count() + self.assertIn(self.send(self.command("observe", {}))["body"]["state"], + ("refused", "not_accepted", "unknown")) + self.assertEqual(self.journal_count(), count) + self.assertEqual(self.frozen_effects(), effects) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_network_operator.py b/tools/physical-workcell/test_network_operator.py new file mode 100644 index 0000000..4caac18 --- /dev/null +++ b/tools/physical-workcell/test_network_operator.py @@ -0,0 +1,666 @@ +"""Deterministic real-socket workbench conformance; never model qualification.""" +import json +import threading +import time +import unittest +import uuid +from unittest import mock + +import body_client +import body_network +import network_operator_runtime as runtime +import order_protocol as protocol + + +def action(name="stop", x=None, y=None): + return {"action": name, "target_x": x, "target_y": y, "intent": "Declared deterministic network conformance"} + + +class NetworkOperatorTests(unittest.TestCase): + def setUp(self): + self.root = runtime.BASE / "output" / ("net-operator-test-" + uuid.uuid4().hex[:10]) + self.body_root = self.root / "body" + body_network.initialize(self.body_root, diagnostic_enabled=True) + self.service = body_network.BodyService(self.body_root).start() + self.host = self.root / "host" + self.patcher = mock.patch.object(runtime, "sources", return_value={"diagnostic_source": "fixed"}) + self.patcher.start() + runtime.initialize(self.host, self.service.origin) + self.op = None + + def tearDown(self): + if self.op: + self.op.close() + if self.service: + self.service.close() + failed = any(test is self for test, _ in (*self._outcome.result.failures, *self._outcome.result.errors)) + protocol.create_file(self.root / "conformance.json", protocol.encoded({"test_id": self.id(), "passed": not failed, + "policy": "explicitly_fake_diagnostic", "host_state": body_client.read_journal(self.host / "operator.json")})) + self.patcher.stop() + + def open(self, policy=None, worker=False): + self.op = runtime.Operator(self.host, policy or (lambda *_: (action(), {})), start_worker=worker) + return self.op + + def job(self, name="one", **changes): + return {"job_id": name, "label": "Network inspection", "target": "A", "disturbance_test": False, **changes} + + def wait(self, predicate, timeout=15): + until = time.monotonic() + timeout + while time.monotonic() < until and not predicate(): + time.sleep(.02) + self.assertTrue(predicate()) + + def restart_same_body(self): + identity = self.service.identity() + port = self.service.http.server_address[1] + self.service.close() + self.service = body_network.BodyService(self.body_root, port=port).start() + current = self.service.identity() + self.assertEqual(current["body_id"], identity["body_id"]) + self.assertEqual(current["adapter_id"], identity["adapter_id"]) + self.assertGreater(current["epoch"], identity["epoch"]) + return current + + def test_healthy_delayed_probe_preserves_model_then_requires_fresh_admission_proof(self): + entered, release = threading.Event(), threading.Event() + def policy(*_): + entered.set() + release.wait(8) + return action("move", -.25, 0), {} + op = self.open(policy) + op.health_stop.set() + op.health_worker.join(4) + op.enqueue(self.job()) + worker = threading.Thread(target=op._step) + worker.start() + self.assertTrue(entered.wait(10)) + epoch = op.control_epoch + boot = op.state()["connection"]["epoch"] + original = self.service.state + reads = [] + def delayed_first_state(): + reads.append(time.monotonic()) + if len(reads) == 1: + time.sleep(1.1) + return original() + proofs = [] + status = op.client.status + def fresh_status(): + result = status() + proofs.append(result["server_state"]) + return result + try: + with mock.patch.object(self.service, "state", side_effect=delayed_first_state): + self.assertFalse(op._refresh_health()) + self.assertEqual(op.state()["connection"]["state"], "checking") + self.assertEqual(op.state()["connection"]["epoch"], boot) + self.assertEqual(op.control_epoch, epoch) + self.assertFalse(op.cancel.is_set()) + self.assertFalse(op.body_ready) + self.assertEqual(op.state()["runner_status"], "running") + with mock.patch.object(op.client, "status", side_effect=fresh_status): + release.set() + worker.join(15) + self.assertFalse(worker.is_alive()) + self.assertGreaterEqual(len(proofs), 2) # Proposal and dispatch each need fresh proof. + self.assertTrue(all(p["state"] == "ready" for p in proofs)) + self.assertEqual(self.service.body.status()["generation"], 1) + self.assertEqual(op.state()["runner_status"], "running") + self.assertEqual(op.state()["connection"]["state"], "connected") + finally: + release.set() + worker.join(15) + + def test_checking_health_cannot_admit_when_fresh_tcp_proof_times_out(self): + op = self.open(lambda *_: self.fail("proof failure reached model")) + op.health_stop.set() + op.health_worker.join(4) + op.enqueue(self.job()) + original = self.service.state + def delayed_state(): + time.sleep(1.1) + return original() + with mock.patch.object(self.service, "state", side_effect=delayed_state): + self.assertFalse(op._refresh_health()) + self.assertEqual(op.state()["connection"]["state"], "checking") + op.client.timeout = .05 + self.assertFalse(op._step()) + self.assertEqual(op.state()["runner_status"], "paused") + self.assertTrue(op.cancel.is_set()) + self.assertIsNone(op.m["stage"]) + self.assertEqual(len(op._journal()["commands"]), 0) + self.assertIsNone(op._journal()["lease"]) + self.assertIsNone(self.service.body.status()["active_job"]) + + def test_checking_health_keeps_verified_boot_change_as_hard_veto(self): + op = self.open() + op.health_stop.set() + op.health_worker.join(4) + op.enqueue(self.job()) + current = self.restart_same_body() + original = self.service.state + def delayed_state(): + time.sleep(1.1) + return original() + with mock.patch.object(self.service, "state", side_effect=delayed_state): + self.assertFalse(op._refresh_health()) + self.assertEqual(op.state()["connection"]["epoch"], current["epoch"]) + self.assertEqual(op.state()["runner_status"], "paused") + self.assertTrue(op.cancel.is_set()) + self.assertFalse(op._step()) + self.assertEqual(len(op._journal()["commands"]), 0) + + def test_checking_health_fresh_unknown_body_blocks_before_lease_or_stage(self): + op = self.open(lambda *_: self.fail("unknown body reached model")) + op.health_stop.set() + op.health_worker.join(4) + op.enqueue(self.job()) + original = self.service.state + reads = [] + def state_then_unknown(): + reads.append(1) + if len(reads) == 1: + time.sleep(1.1) + return original() + result = original() + result["state"] = "unknown" + return result + with mock.patch.object(self.service, "state", side_effect=state_then_unknown): + self.assertFalse(op._refresh_health()) + self.assertEqual(op.state()["connection"]["state"], "checking") + self.assertFalse(op._step()) + self.assertEqual(op.state()["runner_status"], "blocked") + self.assertTrue(op.cancel.is_set()) + self.assertIsNone(op.m["stage"]) + self.assertEqual(len(op._journal()["commands"]), 0) + self.assertIsNone(op._journal()["lease"]) + + def test_pause_during_fresh_admission_proof_prevents_lease_and_command(self): + op = self.open(lambda *_: self.fail("paused admission reached model")) + op.health_stop.set() + op.health_worker.join(4) + op.enqueue(self.job()) + entered, release = threading.Event(), threading.Event() + original = self.service.state + def delayed_state(): + entered.set() + release.wait(5) + return original() + with mock.patch.object(self.service, "state", side_effect=delayed_state): + worker = threading.Thread(target=op._step) + worker.start() + try: + self.assertTrue(entered.wait(5)) + self.assertTrue(op.state()["transport_pending"]) + self.assertIsNone(op.m["stage"]) + op.pause() + finally: + release.set() + worker.join(10) + self.assertFalse(worker.is_alive()) + self.assertEqual(op.state()["runner_status"], "paused") + self.assertEqual(len(op._journal()["commands"]), 0) + self.assertIsNone(op._journal()["lease"]) + self.assertIsNone(op.m["stage"]) + + def test_checking_does_not_release_unknown_and_exact_receipt_stays_paused(self): + decisions = iter((action("move", -.25, 0), action())) + op = self.open(lambda *_: (next(decisions), {})) + op.health_stop.set() + op.health_worker.join(4) + op.enqueue(self.job()) + op.fault({"mode": "drop_after", "delay_s": 0}) + self.assertFalse(op._step()) + token = op.m["stage"]["command_id"] + journal = op._journal() + original = self.service.state + def delayed_state(): + time.sleep(1.1) + return original() + with mock.patch.object(self.service, "state", side_effect=delayed_state): + self.assertFalse(op._refresh_health()) + self.assertEqual(op.state()["connection"]["state"], "checking") + self.assertEqual(op.state()["runner_status"], "blocked") + self.assertEqual(op.m["stage"]["command_id"], token) + self.assertFalse(op._step()) + with self.assertRaises(protocol.Refusal): + op.resume() + op.reconcile() + self.assertEqual(op.state()["runner_status"], "paused") + self.assertEqual(len(op._journal()["commands"]), len(journal["commands"])) + self.assertEqual(op._journal()["lease"], journal["lease"]) + self.assertEqual(op._journal()["commands"][token]["send_count"], 1) + self.assertEqual(self.service.body.status()["generation"], 1) + op.resume() + self.assertEqual(op.state()["connection"]["state"], "connected") + self.assertFalse(op._step()) + self.assertEqual(self.service.body.status()["generation"], 1) + + def test_checking_followed_by_changed_source_identity_cancels_and_blocks(self): + op = self.open() + op.health_stop.set() + op.health_worker.join(4) + original = self.service.state + def delayed_state(): + time.sleep(1.1) + return original() + with mock.patch.object(self.service, "state", side_effect=delayed_state): + self.assertFalse(op._refresh_health()) + self.assertEqual(op.state()["connection"]["state"], "checking") + wrong_identity = self.service.identity() + wrong_identity["source_sha256"] = "0" * 64 + with mock.patch.object(self.service, "identity", return_value=wrong_identity): + self.assertFalse(op._refresh_health()) + self.assertEqual(op.state()["runner_status"], "blocked") + self.assertTrue(op.cancel.is_set()) + self.assertEqual(len(op._journal()["commands"]), 0) + + def test_health_first_fast_body_reboot_withholds_old_model_decision(self): + entered, release = threading.Event(), threading.Event() + decisions = iter((action("move", -.25, 0), action())) + def policy(*_): + entered.set() + release.wait(5) + return next(decisions), {} + op = self.open(policy, worker=True) + op.health_stop.set() + op.health_worker.join(4) + op.enqueue(self.job()) + self.assertTrue(entered.wait(10)) + before = len(op._journal()["commands"]) + current = self.restart_same_body() + try: + op._refresh_health() + finally: + release.set() + self.wait(lambda: not op.in_model and op.m["runner_status"] == "paused") + self.assertEqual(self.service.body.status()["generation"], 0) + self.assertEqual(len(op._journal()["commands"]), before) + self.assertEqual(op.state()["connection"]["epoch"], current["epoch"]) + self.assertTrue(any(e["kind"] == "decision_withheld" for e in op.m["activity"])) + self.assertEqual(op.state()["jobs"][0]["status"], "paused") + + def test_acquisition_first_reboot_cannot_authorize_old_decision_with_fresh_lease(self): + calls = [] + before = [] + old_leases = [] + old_challenges = [] + def policy(challenge, *_): + calls.append(challenge) + if len(calls) == 1: + old_challenges.append(challenge["challenge_id"]) + before.append(len(op._journal()["commands"])) + old_leases.append(op._journal()["lease"]) + self.restart_same_body() + return action("move", -.25, 0), {} + return action(), {} + op = self.open(policy) + op.health_stop.set() + op.health_worker.join(4) + op.enqueue(self.job()) + self.assertFalse(op._step()) + self.assertEqual(op.state()["runner_status"], "paused") + self.assertEqual(self.service.body.status()["generation"], 0) + self.assertEqual(len(op._journal()["commands"]), before[0]) + self.assertIsNone(op.m["stage"]) + self.assertEqual(op._journal()["lease"], old_leases[0]) + op.resume() + self.assertFalse(op._step()) + self.assertEqual(len(calls), 2) + self.assertNotEqual(calls[1]["challenge_id"], old_challenges[0]) + self.assertEqual(calls[1]["observation"]["generation"], 0) + self.assertEqual(self.service.body.status()["generation"], 0) + + def test_unknown_dispatch_survives_body_epoch_then_exact_recovery_and_resume(self): + decisions = iter((action("move", -.25, 0), action())) + op = self.open(lambda *_: (next(decisions), {})) + op.health_stop.set() + op.health_worker.join(4) + op.enqueue(self.job()) + op.fault({"mode": "drop_after", "delay_s": 0}) + self.assertFalse(op._step()) + token = op.m["stage"]["command_id"] + journal = op._journal() + lease = journal["lease"] + count = len(journal["commands"]) + current = self.restart_same_body() + op._refresh_health() + self.assertEqual(op.m["stage"]["command_id"], token) + self.assertEqual(op.state()["recovery"]["command_id"], token) + self.assertEqual(op.state()["runner_status"], "blocked") + self.assertEqual(op._journal()["lease"], lease) + self.assertEqual(op._journal()["pending_command_ids"], [token]) + with self.assertRaises(protocol.Refusal): + op.resume() + op.reconcile() + self.assertEqual(op.state()["runner_status"], "paused") + self.assertEqual(op.state()["jobs"][0]["status"], "paused") + self.assertEqual(op.state()["connection"]["epoch"], current["epoch"]) + self.assertEqual(op.m["applied_commands"].count(token), 1) + self.assertEqual(len(op._journal()["commands"]), count) + self.assertEqual(op._journal()["commands"][token]["send_count"], 1) + self.assertEqual(self.service.body.status()["generation"], 1) + with self.assertRaises(protocol.Refusal): + op.reconcile() + op.resume() + self.assertFalse(op._step()) + self.assertEqual(self.service.body.status()["generation"], 1) + self.assertEqual(op._journal()["identity"]["epoch"], current["epoch"]) + + def test_graceful_host_restart_labels_unfinished_planning_as_paused(self): + entered = threading.Event() + def policy(challenge, history, directory, cancel): + entered.set() + cancel.wait(5) + return action("move", -.25, 0), {} + op = self.open(policy, worker=True) + op.enqueue(self.job()) + self.assertTrue(entered.wait(10)) + count = len(op._journal()["commands"]) + op.close() + self.op = None + op = self.open(lambda *_: self.fail("restart automatically requested a model"), worker=True) + self.assertEqual(op.state()["runner_status"], "paused") + self.assertEqual(op.state()["jobs"][0]["status"], "paused") + self.assertEqual(len(op._journal()["commands"]), count) + self.assertEqual(self.service.body.status()["generation"], 0) + self.assertIsNone(op.m["stage"]) + + def test_acknowledged_second_boot_survives_outage_without_epoch_regression(self): + op = self.open() + op.health_stop.set() + op.health_worker.join(4) + current = self.restart_same_body() + op._refresh_health() + self.assertEqual(op.state()["runner_status"], "paused") + op.resume() + self.assertEqual(op.state()["connection"]["epoch"], current["epoch"]) + self.assertEqual(op.state()["runner_status"], "running") + self.service.close() + self.service = None + op._refresh_health() + self.assertEqual(op.state()["connection"]["state"], "unavailable") + self.assertEqual(op.state()["connection"]["epoch"], current["epoch"]) + self.assertEqual(op.state()["runner_status"], "paused") + self.assertEqual(len(op._journal()["commands"]), 0) + + def test_pause_wins_while_resume_waits_for_actual_socket_status(self): + op = self.open() + op.pause() + entered, release = threading.Event(), threading.Event() + original = self.service.state + errors = [] + def delayed_status(): + entered.set() + release.wait(5) + return original() + def resume(): + try: + op.resume() + except protocol.Refusal as exc: + errors.append(str(exc)) + with mock.patch.object(self.service, "state", side_effect=delayed_status): + thread = threading.Thread(target=resume) + thread.start() + self.assertTrue(entered.wait(5)) + op.pause() + release.set() + thread.join(5) + self.assertFalse(thread.is_alive()) + self.assertEqual(op.state()["runner_status"], "paused") + self.assertTrue(errors) + self.assertEqual(len(op._journal()["commands"]), 0) + self.assertEqual(self.service.body.status()["generation"], 0) + + def test_two_successive_jobs_and_disturbance_using_declared_replay(self): + audit = runtime.BASE.parent.parent / "jevdual/results/agent-visual-bench-20261011/AI-action-audit.json" + proposals = iter(json.loads(audit.read_text())["actions"]) + def policy(*_): + recorded = next(proposals)["proposal"] + return {k: recorded[k] for k in ("action", "target_x", "target_y", "intent")}, {} + op = self.open(policy) + op.enqueue(self.job()) + op.enqueue(self.job("two", target="B", disturbance_test=True)) + for _ in range(40): + if not op._step(): + break + self.assertEqual([j["status"] for j in op.state()["jobs"]], ["completed", "completed"]) + self.assertEqual(op.state()["usage"]["calls"], 29) + self.assertTrue(all(j["measurement"]["orange_pixel_count"] > 0 for j in op.state()["jobs"])) + self.assertTrue(all(j["reset"]["reset_contact_samples"] > 0 for j in op.state()["jobs"])) + self.assertEqual(len(self.service.body.status()["jobs"]), 2) + self.assertIsNone(op.m["stage"]) + self.assertLess(len(op._journal()["commands"]), runtime.MAX_COMMANDS) + + def test_lost_dispatch_disconnect_recovery_and_explicit_resume(self): + decisions = iter((action("move", -.25, 0), action())) + seen = [] + def policy(challenge, *_): + seen.append(challenge["observation"]["generation"]) + return next(decisions), {} + op = self.open(policy) + op.enqueue(self.job()) + op.fault({"mode": "drop_after", "delay_s": 0}) + self.assertFalse(op._step()) + token = op.state()["recovery"]["command_id"] + self.assertEqual(op.state()["recovery"]["state"], "uncertain") + self.assertEqual(self.service.body.status()["generation"], 1) + before = len(op._journal()["commands"]) + op.disconnect() + with self.assertRaises(protocol.Refusal): + op.reconcile() + op.reconnect() + self.assertEqual(len(op._journal()["commands"]), before) + op.reconcile() + self.assertEqual(op.state()["recovery"]["state"], "recovered") + self.assertEqual(op.state()["runner_status"], "paused") + self.assertEqual(op.m["applied_commands"].count(token), 1) + self.assertEqual(self.service.body.status()["generation"], 1) + with self.assertRaises(protocol.Refusal): + op.reconcile() + op.resume() + self.assertFalse(op._step()) + self.assertEqual(seen, [0, 1]) + self.assertEqual(op._journal()["commands"][token]["send_count"], 1) + + def test_unknown_start_observe_and_proposal_settle_without_replay_or_old_decision(self): + for index, operation in enumerate(("start_job", "observe", "propose")): + with self.subTest(operation=operation): + if index: + self.op.close() + self.op = None + self.service.close() + next_body = self.root / ("body-" + operation) + body_network.initialize(next_body, diagnostic_enabled=True) + self.service = body_network.BodyService(next_body).start() + self.host = self.root / ("host-" + operation) + runtime.initialize(self.host, self.service.origin) + op = self.open(lambda *_: (action("move", -.25, 0), {})) + op.enqueue(self.job(operation)) + op.link.arm("drop_after", operation=operation, delay_s=0) + self.assertFalse(op._step()) + self.assertEqual(op.state()["recovery"]["operation"], operation) + before = len(op._journal()["commands"]) + op.reconcile() + self.assertEqual(op.state()["runner_status"], "paused") + self.assertEqual(self.service.body.status()["generation"], 0) + self.assertEqual(len(op._journal()["commands"]), before) + self.assertIsNone(op.m["stage"]) + + def test_restart_recovers_exact_queue_stage_once_from_historical_receipt(self): + op = self.open(lambda *_: (action("move", -.25, 0), {})) + op.enqueue(self.job()) + op.fault({"mode": "drop_after", "delay_s": 0}) + self.assertFalse(op._step()) + token = op.m["stage"]["command_id"] + before = len(op._journal()["commands"]) + origin = op.link.origin + op.close() + self.op = None + op = self.open(lambda *_: self.fail("restart ran model"), worker=True) + self.assertEqual(op.link.origin, origin) + self.assertEqual(op.state()["runner_status"], "blocked") + op.reconcile() + self.assertEqual(op.state()["runner_status"], "paused") + self.assertEqual(len(op._journal()["commands"]), before) + self.assertEqual(op.m["applied_commands"].count(token), 1) + self.assertEqual(self.service.body.status()["generation"], 1) + + def test_pause_before_host_admission_suppresses_stage_and_socket_command(self): + op = self.open(lambda *_: self.fail("paused model")) + op.enqueue(self.job()) + op.pause() + self.assertFalse(op._step()) + self.assertIsNone(op.m["stage"]) + self.assertEqual(len(op._journal()["commands"]), 0) + self.assertIsNone(self.service.body.status()["active_job"]) + + def test_pause_after_host_admission_allows_exact_receipt_but_no_successor(self): + op = self.open(lambda *_: (action("move", -.25, 0), {})) + op.enqueue(self.job()) + entered, release = threading.Event(), threading.Event() + original = op.client.dispatch + def delayed_dispatch(proposal_id): + self.assertEqual(op.m["stage"]["operation"], "dispatch") + entered.set() + release.wait(5) + return original(proposal_id) + with mock.patch.object(op.client, "dispatch", side_effect=delayed_dispatch): + worker = threading.Thread(target=op._step) + worker.start() + self.assertTrue(entered.wait(10)) + op.pause() + release.set() + worker.join(10) + self.assertFalse(worker.is_alive()) + self.assertEqual(op.state()["runner_status"], "paused") + self.assertEqual(self.service.body.status()["generation"], 1) + self.assertIsNone(op.m["stage"]) + before = len(op._journal()["commands"]) + self.assertFalse(op._step()) + self.assertEqual(len(op._journal()["commands"]), before) + + def test_pause_during_model_withholds_unadmitted_decision(self): + entered, release = threading.Event(), threading.Event() + def policy(*_): + entered.set() + release.wait(5) + return action("move", -.25, 0), {} + op = self.open(policy, worker=True) + op.enqueue(self.job()) + self.assertTrue(entered.wait(10)) + before = len(op._journal()["commands"]) + op.pause() + release.set() + self.wait(lambda: not op.in_model) + self.wait(lambda: any(e["kind"] == "decision_withheld" for e in op.m["activity"])) + self.assertEqual(len(op._journal()["commands"]), before) + self.assertEqual(self.service.body.status()["generation"], 0) + + def test_state_and_frame_are_responsive_while_transport_pending(self): + op = self.open() + op.enqueue(self.job()) + op._step() + op.resume() + entered, release = threading.Event(), threading.Event() + original = op.client.observe + def delayed_observe(): + entered.set() + release.wait(5) + return original() + with mock.patch.object(op.client, "observe", side_effect=delayed_observe): + worker = threading.Thread(target=op._step) + worker.start() + self.assertTrue(entered.wait(10)) + started = time.monotonic() + state = op.state() + self.assertTrue(state["transport_pending"]) + self.assertTrue(op.frame(state["current_frame"]["image_url"]).startswith(b"\x89PNG")) + self.assertLess(time.monotonic() - started, .2) + release.set() + worker.join(10) + self.assertFalse(worker.is_alive()) + + def test_changed_image_and_source_refuse_without_network_effect(self): + op = self.open() + op.enqueue(self.job()) + op._step() + before = len(op._journal()["commands"]) + with (mock.patch.object(runtime, "sources", return_value={"changed": True}), self.assertRaises(protocol.Refusal)): + op.state() + image = runtime.Path(op.m["frame"]["path"]) + image.write_bytes(b"changed cached image") + with self.assertRaises(protocol.Refusal): + op.frame() + self.assertEqual(len(op._journal()["commands"]), before) + + def test_invalid_body_identity_health_is_blocked_without_thread_crash(self): + op = self.open() + with mock.patch.object(self.service, "identity", side_effect=protocol.Refusal("diagnostic invalid identity")): + self.assertFalse(op._refresh_health()) + self.assertEqual(op.state()["connection"]["state"], "unavailable") + self.assertEqual(op.state()["runner_status"], "blocked") + self.assertTrue(op.health_worker.is_alive()) + self.assertEqual(len(op._journal()["commands"]), 0) + + def test_full_local_manifest_capacity_keeps_wire_limits(self): + op = self.open() + for i in range(6): + op.enqueue(self.job("job-" + str(i), label="L" * 120)) + for job in op.m["jobs"]: + job["history"] = [{"action": dict(action("move", -.25, 0), intent="I" * 500), "outcome": "executed", "reason": ""} for _ in range(6)] + ids = ["command-" + uuid.uuid4().hex for _ in range(240)] + op.m["applied_commands"] = ids + op.m["stage"] = {"before_commands": ids, "operation": "observe", "arguments": {}, "command_id": None, "context": {"job_id": "job-0"}, "control_epoch": 0} + op.m["activity"] = [{"seq": i, "time": 1, "kind": "diagnostic", "intent": "I" * 1000, "outcome": "O" * 1000} for i in range(24)] + op._save() + size = len(protocol.encoded(op.m)) + self.assertGreater(size, protocol.MAX_BYTES) + self.assertLessEqual(size, body_client.MAX_CLIENT_JOURNAL) + self.assertEqual(body_client.read_journal(self.host / "operator.json"), op.m) + self.assertLessEqual(len(protocol.encoded(op.state())), protocol.MAX_BYTES) + + def test_stale_health_timeout_after_known_socket_dispatch_does_not_pause(self): + op = self.open() + op.enqueue(self.job()) + op._step() + op.resume() + op.policy = lambda *_: (action("move", -.25, 0), {}) + op.health_stop.set() + op.health_worker.join(4) + entered, release = threading.Event(), threading.Event() + original = self.service.state + reads = [] + def delayed_health_state(): + reads.append(1) + if len(reads) == 1: + entered.set() + release.wait(5) + return original() + with mock.patch.object(self.service, "state", side_effect=delayed_health_state): + health = threading.Thread(target=op._refresh_health) + health.start() + self.assertTrue(entered.wait(5)) + self.assertTrue(op._step()) + health.join(3) + release.set() + self.assertFalse(health.is_alive()) + self.assertEqual(self.service.body.status()["generation"], 1) + self.assertEqual(op.state()["connection"]["state"], "connected") + self.assertEqual(op.state()["runner_status"], "running") + self.assertIsNone(op.m["stage"]) + + def test_idle_body_outage_becomes_unavailable_without_effect_polling(self): + op = self.open() + before = len(op._journal()["commands"]) + self.service.close() + self.service = None + time.sleep(2.5) + self.assertEqual(op.state()["connection"]["state"], "unavailable") + self.assertEqual(op.state()["runner_status"], "paused") + self.assertEqual(len(op._journal()["commands"]), before) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_network_operator_http.py b/tools/physical-workcell/test_network_operator_http.py new file mode 100644 index 0000000..1da5876 --- /dev/null +++ b/tools/physical-workcell/test_network_operator_http.py @@ -0,0 +1,137 @@ +"""Real HTTP boundary conformance; the operator is an explicitly labelled test double.""" +import json +import threading +import unittest +from http.client import HTTPConnection + +import network_bench_operator as product + + +class DiagnosticOperator: + """Records handler admission without starting a body or model.""" + + def __init__(self): + self.m = {"app_id": "http-conformance-app"} + self.calls = [] + + def _identity(self): + pass + + def state(self): + return {"app_id": self.m["app_id"], "simulation": True} + + def frame(self, path): + self.calls.append(("frame", path)) + return b"\x89PNG\r\n\x1a\n" + + def enqueue(self, request): + self.calls.append(("enqueue", request)) + return {"app_id": self.m["app_id"]} + + def fault(self, request): + self.calls.append(("fault", request)) + return {"app_id": self.m["app_id"]} + + def __getattr__(self, name): + if name not in ("pause", "resume", "disconnect", "reconnect", "reconcile"): + raise AttributeError(name) + def control(): + self.calls.append((name, None)) + return {"app_id": self.m["app_id"]} + return control + + +class NetworkHTTPTests(unittest.TestCase): + def setUp(self): + self.operator = DiagnosticOperator() + self.server = product.Server(("127.0.0.1", 0), self.operator) + self.thread = threading.Thread(target=self.server.serve_forever, kwargs={"poll_interval": .01}, daemon=True) + self.thread.start() + + def tearDown(self): + self.server.shutdown() + self.server.server_close() + self.thread.join(3) + self.assertFalse(self.thread.is_alive()) + + def request(self, method, path, payload=None, overrides=None, extra=()): + headers = {"Host": self.server.host} + body = json.dumps(payload).encode() if payload is not None else b"" + if method == "POST": + headers.update({"Origin": self.server.origin, + "X-Footwork-App-ID": self.operator.m["app_id"], + "Content-Type": "application/json", "Content-Length": str(len(body))}) + for name, value in (overrides or {}).items(): + if value is None: + headers.pop(name, None) + else: + headers[name] = value + connection = HTTPConnection("127.0.0.1", self.server.server_address[1], timeout=3) + try: + connection.putrequest(method, path, skip_host=True, skip_accept_encoding=True) + for name, value in headers.items(): + connection.putheader(name, value) + for name, value in extra: + connection.putheader(name, value) + connection.endheaders(body) + response = connection.getresponse() + return response.status, response.read(), response.getheader("Content-Type") + finally: + connection.close() + + def test_new_controls_admitted_only_with_exact_identity(self): + for route in ("disconnect", "reconnect", "reconcile", "pause", "resume"): + self.assertEqual(self.request("POST", "/api/" + route, {})[0], 200) + self.assertEqual([call[0] for call in self.operator.calls], + ["disconnect", "reconnect", "reconcile", "pause", "resume"]) + + def test_missing_foreign_or_duplicate_headers_never_mutate(self): + for name, foreign in (("Host", "localhost:9999"), ("Origin", "https://foreign.invalid"), + ("X-Footwork-App-ID", "foreign-app")): + for value in (None, foreign): + with self.subTest(header=name, value=value): + self.assertEqual(self.request("POST", "/api/reconnect", {}, {name: value})[0], 409) + valid = {"Host": self.server.host, "Origin": self.server.origin, + "X-Footwork-App-ID": self.operator.m["app_id"]}[name] + self.assertEqual(self.request("POST", "/api/reconcile", {}, extra=((name, valid),))[0], 409) + self.assertEqual(self.operator.calls, []) + + def test_controls_require_empty_exact_json_body(self): + for route in ("disconnect", "reconnect", "reconcile"): + for value in ({"replay": True}, [], {"command_id": "other"}): + self.assertEqual(self.request("POST", "/api/" + route, value)[0], 409) + self.assertEqual(self.operator.calls, []) + + def test_fault_schema_is_bounded_and_dispatch_only(self): + for mode in ("drop_after", "delay_after", "duplicate"): + for delay in (0, 2): + self.assertEqual(self.request("POST", "/api/fault", {"mode": mode, "delay_s": delay})[0], 200) + before = list(self.operator.calls) + for value in ({"mode": "other", "delay_s": 0}, {"mode": "duplicate", "delay_s": True}, + {"mode": "duplicate", "delay_s": -1}, {"mode": "duplicate", "delay_s": 3}, + {"mode": "duplicate", "delay_s": 0, "operation": "begin"}): + self.assertEqual(self.request("POST", "/api/fault", value)[0], 409) + self.assertEqual(self.operator.calls, before) + + def test_network_html_read_only_state_and_unknown_routes(self): + status, body, content_type = self.request("GET", "/") + self.assertEqual(status, 200) + self.assertIn("text/html", content_type) + for label in (b"Check receipt", b"Lose next reply", b"Duplicate next command"): + self.assertIn(label, body) + self.assertEqual(self.request("GET", "/api/state")[0], 200) + self.assertEqual(self.request("POST", "/api/reconcile?command=other", {})[0], 409) + self.assertEqual(self.request("GET", "/api/disconnect")[0], 404) + self.assertEqual(self.operator.calls, []) + + def test_http_body_framing_is_closed_before_mutation(self): + for overrides, extra in (({"Content-Type": "text/plain"}, ()), + ({"Content-Length": "4097"}, ()), + ({}, (("Content-Length", "2"),)), + ({}, (("Transfer-Encoding", "chunked"),))): + self.assertEqual(self.request("POST", "/api/reconnect", {}, overrides, extra)[0], 409) + self.assertEqual(self.operator.calls, []) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_operator_runtime.py b/tools/physical-workcell/test_operator_runtime.py new file mode 100644 index 0000000..5bcbbe6 --- /dev/null +++ b/tools/physical-workcell/test_operator_runtime.py @@ -0,0 +1,306 @@ +"""Labelled deterministic conformance; fake decisions are not AI-control evidence.""" +import json +import threading +import time +import unittest +import uuid +from http.client import HTTPConnection +from unittest import mock + +import bench_operator +import operator_runtime as runtime +import order_protocol as protocol + + +def action(name="stop", x=None, y=None): + return {"action": name, "target_x": x, "target_y": y, "intent": "Deterministic conformance fixture"} + + +class OperatorTests(unittest.TestCase): + def setUp(self): + self.root = runtime.BASE / "output" / ("operator-test-" + uuid.uuid4().hex[:10]) + self.patcher = mock.patch.object(runtime, "sources", return_value={"diagnostic_source": "frozen"}) + self.patcher.start() + runtime.initialize(self.root) + self.operator = None + self.server = None + self.thread = None + + def tearDown(self): + if self.server: + self.server.shutdown() + self.server.server_close() + self.thread.join(5) + if self.operator: + self.operator.close() + result = self._outcome.result + failed = any(test is self for test, _ in (*result.failures, *result.errors)) + protocol.create_file(self.root / "conformance.json", protocol.encoded( + {"test_id": self.id(), "passed": not failed, "policy": "explicitly_fake_diagnostic", + "state": protocol.decode(protocol.read_bytes(self.root / "operator.json"))})) + self.patcher.stop() + + def open(self, policy=None, worker=False): + self.operator = runtime.Operator(self.root, policy or (lambda *_: (action(), {})), start_worker=worker) + return self.operator + + def job(self, name="one", **changes): + return {"job_id": name, "label": "Diagnostic job", "target": "A", "disturbance_test": False, **changes} + + def wait(self, predicate): + until = time.monotonic() + 15 + while not predicate() and time.monotonic() < until: + time.sleep(.02) + self.assertTrue(predicate()) + + def test_new_identity_bound_to_separate_persistent_bench(self): + op = self.open() + self.assertNotEqual(op.m["app_id"], op.body.status()["bench_id"]) + self.assertEqual(op.state()["current_frame"]["generation"], 0) + self.assertEqual(op.body.status()["generation"], 0) + self.assertTrue(op.frame().startswith(b"\x89PNG")) + + def test_pause_between_outer_loop_check_and_step_admission_starts_nothing(self): + policy_calls = [] + def policy(*args): + policy_calls.append(args) + return action(), {} + op = self.open(policy) + op.enqueue(self.job()) + # The outer loop's running check already passed; Pause now wins the admission lock. + op.pause() + self.assertFalse(op._step()) + self.assertIsNone(op.body.status()["active_job"]) + self.assertIsNone(op.state()["active_job_id"]) + self.assertEqual(op.state()["jobs"][0]["status"], "queued") + self.assertEqual(op.state()["jobs"][0]["call_count"], 0) + self.assertEqual(op.state()["usage"]["calls"], 0) + self.assertEqual(list((self.root / "calls").glob("*/reservation.json")), []) + self.assertEqual(policy_calls, []) + + def test_get_state_and_frame_do_not_capture_or_call_model(self): + op = self.open(lambda *_: self.fail("GET invoked model")) + before = op.agent.status()["sequence"] + for _ in range(3): + op.state() + op.frame() + self.assertEqual(op.agent.status()["sequence"], before) + + def test_queue_validation_duplicate_and_path_schema(self): + op = self.open() + for value in (self.job("../bad"), self.job(target="C"), self.job(disturbance_test=1), + self.job(command="rm"), self.job(label=""), self.job(disturbance_test=True)): + with self.assertRaises((protocol.Refusal, TypeError)): + op.enqueue(value) + op.enqueue(self.job()) + with self.assertRaises(protocol.Refusal): + op.enqueue(self.job()) + self.assertEqual(len(op.state()["jobs"]), 1) + + def test_stop_decision_pauses_without_completion(self): + op = self.open(worker=True) + op.enqueue(self.job()) + self.wait(lambda: op.state()["runner_status"] == "paused") + self.assertEqual(op.state()["jobs"][0]["status"], "paused") + self.assertEqual(op.body.status()["generation"], 0) + self.assertFalse((self.root / "bench" / "measurements.sqlite").exists()) + + def test_pause_during_model_prevents_late_reply_dispatch(self): + entered, release = threading.Event(), threading.Event() + def policy(*_): + entered.set() + release.wait(5) + return action("move", -.25, 0), {"usage": {"input_tokens": 7, "output_tokens": 3}} + op = self.open(policy, worker=True) + op.enqueue(self.job()) + self.assertTrue(entered.wait(10)) + op.pause() + with self.assertRaises(protocol.Refusal): + op.resume() + release.set() + self.wait(lambda: not op.in_model) + self.wait(lambda: any(e["kind"] == "decision_withheld" for e in op.m["activity"])) + self.assertEqual(op.body.status()["generation"], 0) + self.assertEqual(op.state()["usage"]["calls"], 1) + self.assertEqual(op.state()["usage"]["input_tokens"], 7) + self.assertEqual(len(list((self.root / "agent" / "proposals").glob("*.json"))), 0) + + def test_cancelled_reply_stays_withheld_after_event_clears(self): + # Simulate the narrow interval after owned process exit but before old reply dispatch. + op = self.open() + def policy(*_): + op.pause() + op.in_model = False + op.resume() + return action("move", -.25, 0), {} + op.policy = policy + op.enqueue(self.job()) + self.assertFalse(op._step()) + self.assertEqual(op.body.status()["generation"], 0) + self.assertEqual(len(list((self.root / "agent" / "proposals").glob("*.json"))), 0) + self.assertTrue(any(e["kind"] == "decision_withheld" for e in op.state()["activity"])) + + def test_model_unavailable_pauses_counts_attempt_and_never_completes(self): + def policy(*_): + raise RuntimeError("Diagnostic model unavailable") + op = self.open(policy, worker=True) + op.enqueue(self.job()) + self.wait(lambda: op.state()["runner_status"] == "paused") + self.assertEqual(op.state()["jobs"][0]["call_count"], 1) + self.assertEqual(op.body.status()["generation"], 0) + + def test_model_cannot_claim_completion_or_bypass_guard(self): + op = self.open(lambda *_: (dict(action("finish_reset"), completed=True), {})) + op.enqueue(self.job()) + with self.assertRaises(protocol.Refusal): + op._step() + self.assertEqual(op.body.status()["active_job"]["phase"], "prepare") + self.assertNotEqual(op.state()["jobs"][0]["status"], "completed") + + def test_invalid_finish_reset_refuses_native_guard(self): + op = self.open(lambda *_: (action("finish_reset"), {})) + op.enqueue(self.job()) + op._step() + self.assertEqual(op.m["jobs"][0]["history"][-1]["outcome"], "refused") + self.assertEqual(op.body.status()["active_job"]["phase"], "prepare") + + def test_restart_retains_body_and_queue_without_replay(self): + op = self.open() + op.enqueue(self.job()) + op.close() + self.operator = None + op = self.open(lambda *_: self.fail("restart invoked model"), worker=True) + time.sleep(.1) + self.assertEqual(op.state()["runner_status"], "paused") + self.assertEqual(op.state()["jobs"][0]["status"], "queued") + self.assertEqual(op.body.status()["generation"], 0) + self.assertEqual(op.state()["usage"]["calls"], 0) + + def test_unknown_native_blocks_resume_and_no_reset(self): + op = self.open() + manifest = protocol.decode(protocol.read_bytes(self.root / "bench" / "bench.json")) + manifest["busy"] = {"operation_id": "interrupted", "kind": "move"} + protocol.persist(self.root / "bench" / "bench.json", manifest) + with self.assertRaises(protocol.Refusal): + op.resume() + self.assertEqual(op.state()["runner_status"], "blocked") + self.assertEqual(op.body.status()["state"], "unknown") + + def test_source_change_refuses_mutation(self): + op = self.open() + with (mock.patch.object(runtime, "sources", return_value={"diagnostic_source": "changed"}), + self.assertRaises(protocol.Refusal)): + op.enqueue(self.job()) + self.assertEqual(len(op.state()["jobs"]), 0) + + def test_two_jobs_replay_public_action_fixture_and_disturbance_guard(self): + # Replay a declared previous development trajectory, not a model qualification. + audit = runtime.BASE.parent.parent / "jevdual/results/agent-visual-bench-20261011/AI-action-audit.json" + proposals = iter(json.loads(audit.read_text())["actions"]) + def policy(*_): + recorded = next(proposals)["proposal"] + return {key: recorded[key] for key in ("action", "target_x", "target_y", "intent")}, {} + op = self.open(policy) + op.enqueue(self.job()) + op.enqueue(self.job("two", target="B", disturbance_test=True)) + for _ in range(40): + if not op._step(): + break + self.assertEqual([j["status"] for j in op.state()["jobs"]], ["completed", "completed"]) + self.assertEqual(len(op.body.status()["jobs"]), 2) + self.assertIsNone(op.body.status()["active_job"]) + events = [protocol.decode(protocol.read_bytes(p)) for p in (self.root / "calls").glob("event-*.json")] + self.assertTrue(any("stale" in e["outcome"] for e in events)) + self.assertEqual(op.state()["usage"]["calls"], 29) + self.assertTrue(all(j["measurement"]["orange_pixel_count"] > 0 for j in op.state()["jobs"])) + + def test_frame_identity_changes_when_zone_repaint_keeps_generation(self): + op = self.open() + first_url = op.state()["current_frame"]["image_url"] + op.enqueue(self.job()) + op._step() + second_url = op.state()["current_frame"]["image_url"] + self.assertNotEqual(first_url, second_url) + self.assertEqual(op.state()["current_frame"]["generation"], 0) + with self.assertRaises(protocol.Refusal): + op.frame(first_url) + self.assertTrue(op.frame(second_url).startswith(b"\x89PNG")) + + def test_interrupted_start_intent_blocks_restart_without_replay(self): + op = self.open() + op.enqueue(self.job()) + op.m["active_job_id"] = "one" + op.m["jobs"][0]["status"] = "running" + op._save() + op.close() + self.operator = None + op = self.open(lambda *_: self.fail("replayed interrupted start"), worker=True) + self.assertEqual(op.state()["runner_status"], "blocked") + with self.assertRaises(protocol.Refusal): + op.resume() + self.assertIsNone(op.body.status()["active_job"]) + self.assertEqual(op.body.status()["generation"], 0) + + def test_mutated_operator_manifest_and_fabricated_completion_refuse(self): + op = self.open() + op.enqueue(self.job()) + op.m["jobs"][0]["status"] = "completed" + op._save() + self.assertEqual(op.state()["runner_status"], "blocked") + self.assertEqual(op.state()["jobs"][0]["status"], "unknown") + retained = protocol.decode(protocol.read_bytes(self.root / "operator.json")) + retained["reason"] = "Changed externally" + protocol.persist(self.root / "operator.json", retained) + with self.assertRaises(protocol.Refusal): + op.enqueue(self.job("two")) + + def test_global_reserved_call_budget_blocks_starting_another_job(self): + op = self.open(lambda *_: self.fail("budget-exhausted model request")) + op.enqueue(self.job()) + op.m["usage"]["calls"] = runtime.MAX_TOTAL_CALLS + op._save() + self.assertFalse(op._step()) + self.assertEqual(op.state()["runner_status"], "paused") + self.assertIsNone(op.body.status()["active_job"]) + self.assertFalse(op.state()["usage"]["token_usage_complete"]) + + def test_shutdown_cancelled_model_exit_persists_stopped_after_worker_exits(self): + entered = threading.Event() + def policy(challenge, history, directory, cancel): + entered.set() + cancel.wait(5) + raise RuntimeError("Diagnostic owned process cancellation") + op = self.open(policy, worker=True) + op.enqueue(self.job()) + self.assertTrue(entered.wait(10)) + op.close() + self.operator = None + retained = protocol.decode(protocol.read_bytes(self.root / "operator.json")) + self.assertFalse(op.worker.is_alive()) + self.assertEqual(retained["runner_status"], "stopped") + self.assertEqual(op.body.status()["generation"], 0) + + def test_http_origin_identity_duplicate_keys_and_body_schema(self): + op = self.open() + self.server = bench_operator.Server(("127.0.0.1", 0), op) + self.thread = threading.Thread(target=self.server.serve_forever, daemon=True) + self.thread.start() + headers = {"Origin": self.server.origin, "X-Footwork-App-ID": op.m["app_id"], "Content-Type": "application/json"} + def post(content, override=None): + connection = HTTPConnection(*self.server.server_address, timeout=5) + connection.request("POST", "/api/jobs", content, headers | (override or {})) + response = connection.getresponse() + response.read() + connection.close() + return response.status + self.assertEqual(post(json.dumps(self.job()), {"Origin": "http://foreign.invalid"}), 409) + self.assertEqual(post(json.dumps(self.job()), {"X-Footwork-App-ID": "foreign"}), 409) + self.assertEqual(post('{"job_id":"one","job_id":"two"}'), 409) + self.assertEqual(post(json.dumps(self.job(command="override"))), 409) + self.assertEqual(len(op.state()["jobs"]), 0) + self.assertEqual(post(json.dumps(self.job())), 200) + self.assertEqual(len(op.state()["jobs"]), 1) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_order_app.py b/tools/physical-workcell/test_order_app.py new file mode 100644 index 0000000..2d0f520 --- /dev/null +++ b/tools/physical-workcell/test_order_app.py @@ -0,0 +1,654 @@ +"""SIM5 actual HTTP/SQLite/native-controller backend conformance. + +Private application cases and owned processes are retained under output. +Public controller RGB supplies targets; server verifies real staged evidence. +""" +import hashlib +import json +import socket +import sqlite3 +import subprocess +import sys +import threading +import time +import unittest +import urllib.error +import urllib.parse +import urllib.request +import uuid +from http.client import HTTPConnection +from pathlib import Path + +BASE = Path(__file__).resolve().parent +APP = BASE / 'order_app.py' +CONTROLLER = BASE / 'controller.py' +TERMINAL = {'executed', 'staged', 'cancelled', 'refused', 'unknown'} + + +def encoded(value): + return (json.dumps(value, indent=2, sort_keys=True, allow_nan=False) + '\n').encode() + + +def sha(content): + return hashlib.sha256(content).hexdigest() + + +class OrderApplicationTests(unittest.TestCase): + def setUp(self): + self.directory = BASE / 'output' / ('a5' + uuid.uuid4().hex[:7]) + self.directory.mkdir(mode=0o700) + self.app = self.directory / 'app' + self.processes = [] + self.handles = [] + self.events = [] + self.origin = None + self.app_id = None + self.order_id = 'order-' + uuid.uuid4().hex[:8] + self.work_order = {'version': 1, 'order_id': self.order_id, + 'project': {'namespace': 'test', 'project_id': 'application-conformance'}, + 'part_id': 'block', 'target_zone': {'x_min': .14, 'x_max': .22, 'y_min': -.04, 'y_max': .04}} + self.seed = {**self.work_order, 'revision': 1, 'state': 'open'} + self.native = None + self.control = None + self.incarnation = None + self.lease = None + + def tearDown(self): + cleanup = [] + for process in reversed(self.processes): + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=8) + except subprocess.TimeoutExpired: + process.kill() + process.wait(timeout=4) + cleanup.append({'pid': process.pid, 'exit_code': process.poll()}) + for handle in self.handles: + handle.close() + failed = any(test is self or getattr(test, 'test_case', None) is self + for test, _ in (*self._outcome.result.failures, *self._outcome.result.errors)) + self.retain(self.directory / 'conformance.json', {'test_id': self.id(), 'passed': not failed, + 'events': self.events, 'owned_process_cleanup': cleanup, + 'classification': 'actual HTTP/SQLite/native controller diagnostic, not browser or AI benchmark'}) + + def retain(self, path, value): + path.write_bytes(encoded(value)) + path.chmod(0o600) + return path + + def event(self, value): + self.events.append({'time_unix': time.time(), **value}) + + def command(self, argv): + argv = list(map(str, argv)) + result = subprocess.run(argv, capture_output=True, text=True, timeout=12, check=False) + try: + body = json.loads(result.stdout) + except ValueError: + self.fail(f'non-JSON CLI reply {result.stdout!r} stderr={result.stderr!r}; retained {self.directory}') + self.event({'kind': 'public_cli', 'command': argv, 'exit_code': result.returncode, + 'reply': body, 'stderr': result.stderr}) + return body + + def spawn(self, argv, name): + path = self.directory / (name + '.log') + handle = path.open('xb') + path.chmod(0o600) + self.handles.append(handle) + argv = list(map(str, argv)) + process = subprocess.Popen(argv, stdout=handle, stderr=subprocess.STDOUT) + self.processes.append(process) + self.event({'kind': 'owned_process', 'command': argv, 'pid': process.pid, 'log': str(path)}) + return process + + def initialize_app(self): + path = self.retain(self.directory / 'seed.json', self.seed) + result = self.command([sys.executable, APP, 'init', '--app-dir', self.app, + '--order-file', path, '--order-sha256', sha(path.read_bytes())]) + self.assertNotEqual(result.get('phase'), 'refused', result) + + def serve_app(self, *, restart=False, fault_script=None): + port = urllib.parse.urlsplit(self.origin).port if restart else 0 + process = self.spawn([sys.executable, fault_script or APP, 'serve', '--app-dir', self.app, + '--port', port], 'app-' + str(len(self.processes))) + deadline = time.monotonic() + 8 + while time.monotonic() < deadline: + if process.poll() is not None: + self.fail(f'app startup failed {process.returncode}; retained {self.directory}') + readiness = self.app / 'readiness.json' + if readiness.exists(): + info = json.loads(readiness.read_bytes()) + if info.get('pid') == process.pid: + self.origin = info['origin'] + code, health = self.http('GET', '/api/health') + if code == 200: + self.app_id = health['app_id'] + return process, health + time.sleep(.04) + self.fail(f'app readiness timed out; retained {self.directory}') + + def http(self, method, path, body=None, *, origin=True, extra_headers=None): + headers = {'Accept': 'application/json'} + content = None if body is None else encoded(body) + if content is not None: + headers['Content-Type'] = 'application/json' + if method == 'POST': + headers['X-Footwork-App-ID'] = self.app_id + if origin: + headers['Origin'] = self.origin + headers.update(extra_headers or {}) + request = urllib.request.Request(self.origin + path, data=content, headers=headers, method=method) + try: + response = urllib.request.urlopen(request, timeout=10) + except urllib.error.HTTPError as exc: + response = exc + with response: + status = response.status + value = json.loads(response.read()) + self.event({'kind': 'http', 'method': method, 'path': path, 'request': body, + 'headers': headers, 'status': status, 'reply': value}) + return status, value + + def order_path(self, action=''): + return '/api/orders/' + self.order_id + (('/' + action) if action else '') + + def start(self): + self.initialize_app() + return self.serve_app() + + def bind(self): + code, reply = self.http('POST', self.order_path('bind'), + {'expected_revision': 1, 'binding_operation_id': 'bind-' + uuid.uuid4().hex}) + self.assertEqual(code, 200, reply) + self.assertEqual(reply['app_id'], self.app_id) + self.lease = reply['lease'] + self.assertEqual(self.lease['work_order'], self.work_order) + self.assertEqual(self.lease['expected_revision'], 1) + return self.lease + + def start_native(self, *, pace=0, placed=False, altered_order=None): + lease = self.lease + self.control = Path(lease['controller_dir']) + case = {'version': 1, 'case_id': lease['lease_id'], 'max_actions': 8, + 'scene': {'block_xy': [.175, .004] if placed else [-.138, .004], + 'illumination_scale': 1, 'occlusion_fraction': 0, 'camera_offset_x_m': 0}, + 'work_order': altered_order or lease['work_order']} + path = self.retain(self.directory / ('case-' + lease['lease_id'] + '.json'), case) + result = self.command([sys.executable, BASE / 'episode_runner.py', 'init', '--case-file', path, + '--case-sha256', sha(path.read_bytes()), '--run-dir', lease['run_dir']]) + self.assertEqual(result['phase'], 'active', result) + self.native = self.spawn([sys.executable, CONTROLLER, 'serve', '--run-dir', lease['run_dir'], + '--controller-dir', self.control, '--pace-s', pace], + 'native-' + str(len(self.processes))) + return self.native_ready() + + def native_ready(self): + deadline = time.monotonic() + 8 + while time.monotonic() < deadline: + if self.native.poll() is not None: + self.fail(f'native startup failed; retained {self.directory}') + result = self.controller('health') + if result.get('phase') != 'refused' and result.get('current_incarnation'): + self.incarnation = result['current_incarnation'] + return result + time.sleep(.04) + self.fail('native readiness timed out') + + def controller(self, command, *args): + return self.command([sys.executable, CONTROLLER, command, '--controller-dir', self.control, *args]) + + def observe(self): + result = self.controller('observe') + self.assertEqual(result['phase'], 'ready', result) + return result['observation'] + + def proposal(self, packet, token, *, complete=False, advance=.105): + value = {'version': 1, 'run_dir': self.lease['run_dir'], 'generation': packet['generation'], + 'image_sha256': packet['image_sha256'], 'token': token, + 'block_box': packet['perception']['block_box'], 'decision': 'complete' if complete else 'move'} + if not complete: + value['target'] = {'x': packet['perception']['pusher_center_xy'][0] + advance, 'y': .004} + path = self.retain(self.directory / ('proposal-' + token + '.json'), value) + return path, sha(path.read_bytes()) + + def submit(self, ident, proposal): + result = self.controller('submit', '--operation-id', ident, '--proposal-file', proposal[0], + '--proposal-sha256', proposal[1], '--expected-incarnation', self.incarnation) + self.assertIn(result['phase'], {'accepted', 'running', 'executed', 'staged'}, result) + return result + + def until(self, ident, phases=TERMINAL): + deadline = time.monotonic() + 10 + while time.monotonic() < deadline: + result = self.controller('status', '--operation-id', ident) + if result.get('phase') in phases: + return result + time.sleep(.05) + self.fail(f'controller status timed out for {ident}') + + def stage(self): + contacts = 0 + for index in range(7): + packet = self.observe() + zone = packet['task_intent']['target_zone'] + block = packet['perception']['block_center_xy'] + pusher = packet['perception']['pusher_center_xy'] + inside = (zone['x_min'] + .005 <= block[0] <= zone['x_max'] - .005 and + zone['y_min'] + .005 <= block[1] <= zone['y_max'] - .005) + ident = self.lease['physical_completion_id'] if inside else 'move-' + str(index) + gap = max(0, block[0] - pusher[0] - .055) + advance = min(.105, max(.015, gap + (zone['x_min'] + zone['x_max']) / 2 - block[0])) + self.submit(ident, self.proposal(packet, ident, complete=inside, advance=advance)) + final = self.until(ident) + if inside: + self.assertEqual(final['phase'], 'staged', final) + return final, contacts + self.assertEqual(final['phase'], 'executed', final) + contacts += final['receipt']['contact_statistics']['pusher_block_contact_samples'] + self.fail('RGB feedback did not stage with completion slot reserved') + + def fulfill(self, **changes): + value = {'lease_id': self.lease['lease_id'], + 'completion_operation_id': self.lease['completion_operation_id'], **changes} + return self.http('POST', self.order_path('fulfill'), value) + + def db_rows(self): + db = sqlite3.connect('file:' + str(self.app / 'orders.sqlite3') + '?mode=ro', uri=True) + try: + order = db.execute('SELECT order_id,revision,state,work_order_json FROM orders WHERE order_id=?', + (self.order_id,)).fetchone() + count = db.execute("SELECT COUNT(*) FROM operations WHERE kind='fulfill'").fetchone()[0] + finally: + db.close() + self.event({'kind': 'independent_sqlite_readback', 'order_row': order, 'fulfillment_effect_count': count}) + return order, count + + def test_real_contact_fulfillment_commits_exact_version_and_receipt(self): + self.start() + code, order = self.http('GET', self.order_path()) + self.assertEqual(code, 200) + self.assertEqual(order['order']['revision'], 1) + self.bind() + self.start_native() + physical, contacts = self.stage() + self.assertGreater(contacts, 0) + code, receipt = self.fulfill() + self.assertEqual(code, 200, receipt) + saved, count = self.db_rows() + self.assertEqual((saved[1], saved[2], count), (2, 'fulfilled', 1)) + read_code, readback = self.http('GET', '/api/operations/' + self.lease['completion_operation_id']) + self.assertEqual(read_code, 200) + self.assertEqual(readback['receipt'], receipt['receipt']) + self.assertEqual(physical['receipt']['token'], self.lease['physical_completion_id']) + + def test_concurrent_bind_allocates_one_workspace_per_order_revision(self): + self.start() + barrier = threading.Barrier(3) + replies = [] + errors = [] + requests = [{'expected_revision': 1, 'binding_operation_id': 'parallel-' + uuid.uuid4().hex} + for _ in range(2)] + + def bind_in_parallel(payload): + try: + barrier.wait(timeout=5) + code, reply = self.http('POST', self.order_path('bind'), payload) + replies.append((payload, code, reply)) + except (OSError, ValueError, threading.BrokenBarrierError) as exc: + errors.append(str(exc)) + + threads = [threading.Thread(target=bind_in_parallel, args=(payload,)) for payload in requests] + for thread in threads: + thread.start() + barrier.wait(timeout=5) + for thread in threads: + thread.join(timeout=8) + self.assertTrue(all(not thread.is_alive() for thread in threads)) + self.assertFalse(errors, errors) + self.assertEqual(len(replies), 2) + accepted = [reply for reply in replies if reply[1] == 200] + self.assertEqual(len(accepted), 1, replies) + payload, _, winner = accepted[0] + repeated_code, repeated = self.http('POST', self.order_path('bind'), payload) + self.assertEqual(repeated_code, 200, repeated) + self.assertEqual(repeated['lease'], winner['lease']) + self.assertTrue(repeated['historical']) + conflicting_code, _ = self.http('POST', self.order_path('bind'), {**payload, 'expected_revision': 2}) + self.assertNotEqual(conflicting_code, 200) + extra_code, _ = self.http('POST', self.order_path('bind'), { + 'expected_revision': 1, 'binding_operation_id': 'client-path-' + uuid.uuid4().hex, + 'run_dir': str(self.directory / 'client-picked-workspace')}) + self.assertNotEqual(extra_code, 200) + db = sqlite3.connect('file:' + str(self.app / 'orders.sqlite3') + '?mode=ro', uri=True) + try: + count = db.execute('SELECT COUNT(*) FROM leases WHERE order_id=? AND revision=1', + (self.order_id,)).fetchone()[0] + bind_count = db.execute("SELECT COUNT(*) FROM operations WHERE kind='bind'").fetchone()[0] + finally: + db.close() + self.assertEqual((count, bind_count), (1, 1)) + self.event({'kind': 'independent_sqlite_lease_allocation_readback', + 'lease_count_at_revision': count, 'binding_effect_count': bind_count, + 'server_assigned_workspace': winner['lease']['workspace_dir']}) + + def test_edit_during_actual_motion_refuses_stale_fulfillment(self): + self.changed_during_motion(cancelled=False) + + def test_cancel_during_actual_motion_keeps_cancelled_app_truth(self): + self.changed_during_motion(cancelled=True) + + def changed_during_motion(self, *, cancelled): + self.start() + self.bind() + self.start_native(pace=.003) + ident = 'active-before-order-change' + self.submit(ident, self.proposal(self.observe(), ident)) + self.until(ident, {'running'}) + time.sleep(.3) + action = 'cancel' if cancelled else 'edit' + change = {'expected_revision': 1} + if not cancelled: + change['target_zone'] = {'x_min': .16, 'x_max': .24, 'y_min': -.04, 'y_max': .04} + code, changed = self.http('POST', self.order_path(action), change) + self.assertEqual(code, 200, changed) + self.controller('cancel', '--operation-id', ident, '--expected-incarnation', self.incarnation) + stopped = self.until(ident) + self.assertIn(stopped['phase'], {'cancelled', 'unknown'}) + code, refused = self.fulfill() + self.assertNotEqual(code, 200, refused) + saved, count = self.db_rows() + self.assertEqual(saved[1], 2) + self.assertEqual(saved[2], 'cancelled' if cancelled else 'open') + self.assertEqual(count, 0) + + def test_actual_lost_http_reply_has_one_sqlite_effect_and_historical_repeat(self): + self.start() + self.bind() + self.start_native(placed=True) + self.stage() + content = encoded({'lease_id': self.lease['lease_id'], + 'completion_operation_id': self.lease['completion_operation_id']}) + parsed = urllib.parse.urlsplit(self.origin) + request = (f'POST {self.order_path("fulfill")} HTTP/1.1\r\nHost: 127.0.0.1:{parsed.port}\r\n' + f'Origin: {self.origin}\r\nX-Footwork-App-ID: {self.app_id}\r\n' + f'Content-Type: application/json\r\nContent-Length: {len(content)}\r\n' + 'Connection: close\r\n\r\n').encode() + content + with socket.create_connection(('127.0.0.1', parsed.port), timeout=5) as caller: + caller.sendall(request) + self.event({'kind': 'actual_http_socket_closed_before_reply', 'path': self.order_path('fulfill'), + 'reply_consumed': False, 'body_sha256': sha(content)}) + deadline = time.monotonic() + 6 + while time.monotonic() < deadline: + code, original = self.http('GET', '/api/operations/' + self.lease['completion_operation_id']) + if code == 200: + break + time.sleep(.05) + self.assertEqual(code, 200, original) + self.native.terminate() + self.native.wait(timeout=8) + repeated_code, repeated = self.fulfill() + self.assertEqual(repeated_code, 200, repeated) + self.assertEqual(repeated['receipt'], original['receipt']) + self.assertTrue(repeated['historical']) + saved, count = self.db_rows() + self.assertEqual((saved[1], saved[2], count), (2, 'fulfilled', 1)) + wrong_code, _ = self.fulfill(lease_id=uuid.uuid4().hex) + self.assertNotEqual(wrong_code, 200) + self.assertEqual(self.db_rows()[1], 1) + + def test_client_flags_paths_wrong_ids_and_missing_physical_evidence_refuse(self): + self.start() + self.bind() + for extra in ({'success': True}, {'receipt_path': '/tmp/fake.json'}, + {'app_id': uuid.uuid4().hex}, {'lease_id': uuid.uuid4().hex}, + {'completion_operation_id': 'fulfill-' + uuid.uuid4().hex}): + with self.subTest(extra=extra): + self.assertNotEqual(self.fulfill(**extra)[0], 200) + self.assertNotEqual(self.fulfill()[0], 200) + saved, count = self.db_rows() + self.assertEqual((saved[1], saved[2], count), (1, 'open', 0)) + + def test_other_application_lease_cannot_alias_same_order_and_real_physical_proof(self): + self.start() + self.bind() + self.start_native(placed=True) + self.stage() + original = self.app, self.origin, self.app_id, self.lease + self.app = self.directory / 'other-app' + self.initialize_app() + self.serve_app() + other = self.bind() + self.assertNotEqual(self.app_id, original[2]) + self.assertEqual(other['work_order'], original[3]['work_order']) + self.app, self.origin, self.app_id, self.lease = original + foreign_code, foreign = self.fulfill(lease_id=other['lease_id']) + self.assertNotEqual(foreign_code, 200, foreign) + missing, _ = self.http('GET', '/api/leases/' + other['lease_id']) + self.assertNotEqual(missing, 200) + self.assertEqual(self.db_rows()[1], 0) + own_code, own = self.fulfill() + self.assertEqual(own_code, 200, own) + self.assertEqual(self.db_rows()[1], 1) + + def test_valid_staging_for_wrong_task_cannot_fulfill_lease(self): + self.start() + self.bind() + altered = {**self.work_order, 'target_zone': {'x_min': .16, 'x_max': .24, 'y_min': -.04, 'y_max': .04}} + self.start_native(placed=True, altered_order=altered) + self.assertEqual(self.stage()[0]['phase'], 'staged') + self.assertNotEqual(self.fulfill()[0], 200) + self.assertEqual(self.db_rows()[1], 0) + + def test_valid_staging_for_wrong_order_identity_cannot_fulfill_lease(self): + self.start() + self.bind() + self.start_native(placed=True, altered_order={**self.work_order, 'order_id': self.order_id + '-different'}) + self.assertEqual(self.stage()[0]['phase'], 'staged') + self.assertNotEqual(self.fulfill()[0], 200) + self.assertEqual(self.db_rows()[1], 0) + + def test_executed_motion_under_completion_id_does_not_invent_staging(self): + self.start() + self.bind() + self.start_native() + ident = self.lease['physical_completion_id'] + self.submit(ident, self.proposal(self.observe(), ident)) + moved = self.until(ident) + self.assertEqual(moved['phase'], 'executed') + self.assertGreater(moved['receipt']['contact_statistics']['native_steps'], 0) + self.assertNotEqual(self.fulfill()[0], 200) + self.assertEqual(self.db_rows()[1], 0) + + def test_changed_source_binding_and_exact_store_replacement_refuse_restart(self): + process, _ = self.start() + self.bind() + process.terminate() + process.wait(timeout=8) + port = urllib.parse.urlsplit(self.origin).port + binding = self.app / 'app-binding.json' + original = binding.read_bytes() + saved_binding = self.directory / 'original-app-binding.json' + saved_binding.write_bytes(original) + saved_binding.chmod(0o600) + changed = json.loads(original) + changed['source_fingerprints']['order_app.py'] = '0' * 64 + self.retain(binding, changed) + refusal = self.command([sys.executable, APP, 'serve', '--app-dir', self.app, '--port', port]) + self.assertEqual(refusal['phase'], 'refused', refusal) + self.event({'kind': 'controlled_retained_binding_fault', 'fault': 'changed_app_source_fingerprint', + 'original_path': str(saved_binding), 'runtime_source_changed': False}) + binding.write_bytes(original) + database = self.app / 'orders.sqlite3' + original_database = self.directory / 'original-orders.sqlite3' + old_identity = [database.stat().st_dev, database.stat().st_ino] + database.rename(original_database) + database.write_bytes(original_database.read_bytes()) + database.chmod(0o600) + new_identity = [database.stat().st_dev, database.stat().st_ino] + self.assertNotEqual(new_identity, old_identity) + refusal = self.command([sys.executable, APP, 'serve', '--app-dir', self.app, '--port', port]) + self.assertEqual(refusal['phase'], 'refused', refusal) + self.event({'kind': 'controlled_retained_store_fault', 'fault': 'byte_identical_store_new_file_identity', + 'original_path': str(original_database), 'old_identity': old_identity, 'new_identity': new_identity}) + + def test_staged_history_with_tampered_current_artifact_refuses_first_fulfillment(self): + self.start() + self.bind() + self.start_native(placed=True) + final, _ = self.stage() + artifact = Path(self.lease['run_dir']) / 'order.json' + original = artifact.read_bytes() + saved = self.directory / 'original-physical-order.json' + saved.write_bytes(original) + saved.chmod(0o600) + artifact.write_bytes(original + b' ') + self.event({'kind': 'controlled_current_artifact_fault', 'path': str(artifact), 'original_path': str(saved), + 'original_sha256': sha(original)}) + status = self.controller('status', '--operation-id', self.lease['physical_completion_id']) + self.assertEqual(status['phase'], 'staged') + self.assertEqual(status['current_health'], 'blocked') + self.assertEqual(status['receipt'], final['receipt']) + self.assertNotEqual(self.fulfill()[0], 200) + self.assertEqual(self.db_rows()[1], 0) + + def test_application_controller_restart_retains_exact_store_and_no_redispatch(self): + app_process, first = self.start() + self.bind() + self.start_native(placed=True) + final, _ = self.stage() + before = self.observe() + old_incarnation = self.incarnation + self.native.terminate() + self.native.wait(timeout=8) + app_process.terminate() + app_process.wait(timeout=8) + _, current = self.serve_app(restart=True) + self.assertEqual(current['app_id'], first['app_id']) + self.assertNotEqual(current['server_incarnation'], first['server_incarnation']) + self.native = self.spawn([sys.executable, CONTROLLER, 'serve', '--run-dir', self.lease['run_dir'], + '--controller-dir', self.control], 'native-restart') + self.native_ready() + self.assertNotEqual(self.incarnation, old_incarnation) + retained = self.controller('status', '--operation-id', self.lease['physical_completion_id']) + self.assertEqual(retained['receipt'], final['receipt']) + after = self.observe() + self.assertEqual((after['generation'], after['state_sha256']), + (before['generation'], before['state_sha256'])) + self.assertEqual(self.fulfill()[0], 200) + self.assertEqual(self.db_rows()[1], 1) + + def test_loopback_browser_origin_and_exact_schema_protect_mutations(self): + self.start() + payload = {'expected_revision': 1, 'binding_operation_id': 'origin-' + uuid.uuid4().hex} + for headers, origin in (({'Origin': 'http://example.com'}, True), ({}, False), + ({'Host': 'example.com'}, True)): + with self.subTest(headers=headers, origin=origin): + code, reply = self.http('POST', self.order_path('bind'), payload, origin=origin, extra_headers=headers) + self.assertNotEqual(code, 200, reply) + self.assertEqual(self.db_rows()[1], 0) + + def test_mutations_require_pre_effect_exact_application_identity(self): + self.start() + original = self.app, self.origin, self.app_id + self.app = self.directory / 'foreign-app' + self.initialize_app() + self.serve_app() + foreign_id = self.app_id + self.assertNotEqual(foreign_id, original[2]) + self.app, self.origin, self.app_id = original + for expected_id, action in ((None, 'edit'), (foreign_id, 'cancel')): + with self.subTest(expected_id=expected_id, action=action): + before, before_count = self.db_rows() + payload = {'expected_revision': before[1]} + if action == 'edit': + payload['target_zone'] = {'x_min': .16, 'x_max': .24, 'y_min': -.04, 'y_max': .04} + headers = {'Origin': self.origin, 'Content-Type': 'application/json'} + if expected_id is not None: + headers['X-Footwork-App-ID'] = expected_id + parsed = urllib.parse.urlsplit(self.origin) + connection = HTTPConnection('127.0.0.1', parsed.port, timeout=8) + try: + connection.request('POST', self.order_path(action), body=encoded(payload), headers=headers) + response = connection.getresponse() + code, reply = response.status, json.loads(response.read()) + finally: + connection.close() + after, after_count = self.db_rows() + self.event({'kind': 'expected_app_identity_pre_effect_regression', 'action': action, + 'headers': headers, 'valid_foreign_app_id': foreign_id, + 'request': payload, 'status': code, 'reply': reply, + 'before_row': before, 'after_row': after}) + self.assertNotEqual(code, 200, reply) + self.assertEqual((after, after_count), (before, before_count)) + + def test_real_verified_proof_then_concurrent_edit_loses_version_cas(self): + self.initialize_app() + marker = self.directory / 'real-proof-ready' + release = self.directory / 'allow-cas' + script = self.directory / 'after-real-verification-timing-fault.py' + content = f'''import importlib.util +import sys +import time +from pathlib import Path +sys.path.insert(0, {str(BASE)!r}) +spec = importlib.util.spec_from_file_location("order_app_real_cas_fault", {str(APP)!r}) +app = importlib.util.module_from_spec(spec) +spec.loader.exec_module(app) +original = app.verify_physical +marker = Path({str(marker)!r}) +release = Path({str(release)!r}) +def delayed(lease): + proof = original(lease) + marker.write_bytes(b"real native/controller/artifact proof obtained\\n") + deadline = time.monotonic() + 5 + while not release.exists(): + if time.monotonic() > deadline: + raise RuntimeError("bounded verification/CAS timing fault timed out") + time.sleep(.02) + return proof +app.verify_physical = delayed +sys.exit(app.main()) +''' + script.write_text(content) + script.chmod(0o600) + self.serve_app(fault_script=script) + self.bind() + self.start_native(placed=True) + self.stage() + replies = [] + failures = [] + + def fulfill_in_parallel(): + try: + replies.append(self.fulfill()) + except (OSError, ValueError) as exc: + failures.append(str(exc)) + + thread = threading.Thread(target=fulfill_in_parallel) + thread.start() + try: + deadline = time.monotonic() + 5 + while not marker.exists() and time.monotonic() < deadline: + time.sleep(.03) + self.assertTrue(marker.exists(), f'no real proof marker; retained {self.directory}') + changed_zone = {'x_min': .16, 'x_max': .24, 'y_min': -.04, 'y_max': .04} + code, changed = self.http('POST', self.order_path('edit'), + {'expected_revision': 1, 'target_zone': changed_zone}) + self.assertEqual(code, 200, changed) + self.event({'kind': 'actual_process_with_labeled_timing_fault', + 'fault': 'real_physical_verification_complete_before_cas_release', + 'verification_success_mocked': False, 'marker': str(marker), 'release': str(release)}) + finally: + release.write_bytes(b'continue real CAS after actual edit\n') + thread.join(timeout=8) + self.assertFalse(thread.is_alive()) + self.assertFalse(failures, failures) + self.assertEqual(len(replies), 1) + self.assertNotEqual(replies[0][0], 200, replies) + saved, count = self.db_rows() + self.assertEqual((saved[1], saved[2], count), (2, 'open', 0)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/physical-workcell/test_planner_guard.py b/tools/physical-workcell/test_planner_guard.py new file mode 100644 index 0000000..a76bd28 --- /dev/null +++ b/tools/physical-workcell/test_planner_guard.py @@ -0,0 +1,228 @@ +"""Actual host death while a labelled fake planner refuses to read its pipe.""" +import fcntl +import os +import signal +import subprocess +import sys +import time +import unittest +import uuid +from pathlib import Path + +import order_protocol as protocol +import planner_guard + +BASE = Path(__file__).resolve().parent + + +def fixture(root): + fd = os.open(root / "planner.lock", os.O_CREAT | os.O_RDWR, 0o600) + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + read, write = os.pipe() + protocol.create_file(root / "planner-intent.json", protocol.encoded({"version": 1, "host_pid": os.getpid(), + "owner": "backpressure-fixture", "request_sha256": "b" * 64})) + protocol.create_file(root / "reservation.json", protocol.encoded({"planner_owner": "backpressure-fixture"})) + protocol.create_file(root / "prompt.txt", b"p" * protocol.MAX_BYTES) + protocol.create_file(root / "request.json", protocol.encoded({"flags": [str(root)]})) + with (root / "events.jsonl").open("xb") as out, (root / "stderr.log").open("xb") as err: + child = subprocess.Popen([sys.executable, str(BASE / "planner_guard.py"), "--root", str(root), + "--executable", str(root / "fake-cli"), "--lifetime-fd", str(read), "--lock-fd", str(fd)], + stdin=subprocess.DEVNULL, stdout=out, stderr=err, start_new_session=True, close_fds=True, pass_fds=(read, fd)) + os.close(read) + os.close(fd) + protocol.persist(root / "host-ready.json", {"host_pid": os.getpid(), "guardian_pid": child.pid}) + try: + while True: + time.sleep(.1) + finally: + os.close(write) + + +class PlannerBackpressureTests(unittest.TestCase): + def test_observed_detached_descendant_withholds_closure_without_signalling_unrelated_process(self): + root = BASE / "output" / ("sim14-planner-detached-" + uuid.uuid4().hex[:10]) + root.mkdir(mode=0o700) + fake = root / "fake-cli" + fake.write_text("#!" + sys.executable + "\n" + '''import json,os,subprocess,sys,time +from pathlib import Path +root=Path(sys.argv[-1]) +child=subprocess.Popen([sys.executable,'-c','import time; time.sleep(120)'],start_new_session=True) +(root/'.detached-ready.json').write_text(json.dumps({'pid':os.getpid(),'detached_pid':child.pid})) +(root/'.detached-ready.json').replace(root/'detached-ready.json') +while True: + time.sleep(.1) +''') + fake.chmod(0o700) + sink = (root / "host.log").open("wb") + host = subprocess.Popen([sys.executable, str(Path(__file__).resolve()), "--fixture", str(root)], stdout=sink, stderr=subprocess.STDOUT) + unrelated = subprocess.Popen([sys.executable, "-c", "import time; time.sleep(120)"], start_new_session=True) + known = None + startup = None + evidence = {"fake_policy": True, "passed": False} + try: + deadline = time.monotonic() + 5 + while time.monotonic() < deadline: + if (root / "detached-ready.json").exists() and (root / "planner-descendants.json").exists(): + ready = protocol.decode(protocol.read_bytes(root / "detached-ready.json")) + proof = protocol.decode(protocol.read_bytes(root / "planner-descendants.json")) + known = next((row for row in proof["observed"] if row["pid"] == ready["detached_pid"]), None) + if known: + break + time.sleep(.02) + self.assertIsNotNone(known) + startup = protocol.decode(protocol.read_bytes(root / "planner-process.json")) + self.assertEqual(startup["host_pid"], host.pid) + self.assertNotEqual(known["group"], startup["planner_pgid"]) + self.assertEqual(known["group"], known["pid"]) + host.kill() + self.assertEqual(host.wait(5), -signal.SIGKILL) + deadline = time.monotonic() + 5 + while time.monotonic() < deadline: + try: + os.kill(startup["guardian_pid"], 0) + except ProcessLookupError: + break + time.sleep(.02) + self.assertFalse((root / "planner-closure.json").exists()) + proof = protocol.decode(protocol.read_bytes(root / "planner-descendants.json")) + self.assertFalse(proof["complete"]) + self.assertIn(known["pid"], [row["pid"] for row in proof["remaining"]]) + with self.assertRaisesRegex(protocol.Refusal, "without group cleanup"): + planner_guard.recover(root) + self.assertIsNone(unrelated.poll()) + self.assertTrue(planner_guard._same_process(planner_guard._processes().get(known["pid"]), known)) + evidence.update(passed=True, proof=proof, unrelated_alive=True, detached_alive=True) + finally: + if host.poll() is None: + host.kill() + host.wait(5) + unrelated.terminate() + unrelated.wait(5) + if known and planner_guard._same_process(planner_guard._processes().get(known["pid"]), known): + os.killpg(known["group"], signal.SIGKILL) + if startup: + for key in ("planner_pid", "guardian_pid"): + pid = startup[key] + try: + if os.getpgid(pid) == pid: + os.killpg(pid, signal.SIGKILL) + except ProcessLookupError: + pass + sink.close() + protocol.create_file(root / "conformance.json", protocol.encoded(evidence)) + + def test_nonblocking_delivery_preserves_entire_maximum_bound_prompt(self): + root = BASE / "output" / ("sim14-planner-delivery-" + uuid.uuid4().hex[:10]) + root.mkdir(mode=0o700) + fake = root / "fake-cli" + fake.write_text("#!" + sys.executable + "\n" + '''import hashlib,json,sys +from pathlib import Path +root=Path(sys.argv[-1]) +prompt=sys.stdin.buffer.read() +(root/'delivered.json').write_text(json.dumps({'bytes':len(prompt),'sha256':hashlib.sha256(prompt).hexdigest()})) +''') + fake.chmod(0o700) + sink = (root / "host.log").open("wb") + host = subprocess.Popen([sys.executable, str(Path(__file__).resolve()), "--fixture", str(root)], stdout=sink, stderr=subprocess.STDOUT) + evidence = {"fake_policy": True, "passed": False} + try: + deadline = time.monotonic() + 5 + while not (root / "planner-closure.json").exists() and time.monotonic() < deadline: + time.sleep(.02) + self.assertTrue((root / "planner-closure.json").exists()) + delivered = protocol.decode(protocol.read_bytes(root / "delivered.json")) + prompt = protocol.read_bytes(root / "prompt.txt") + self.assertEqual(delivered, {"bytes": len(prompt), "sha256": protocol.sha(prompt)}) + self.assertEqual(len(prompt), protocol.MAX_BYTES) + closure = protocol.decode(protocol.read_bytes(root / "planner-closure.json")) + self.assertEqual(closure["reason"], "completed") + self.assertTrue(closure["group_closed"]) + evidence.update(passed=True, delivered=delivered, closure=closure) + finally: + host.kill() + host.wait(5) + sink.close() + protocol.create_file(root / "conformance.json", protocol.encoded(evidence)) + + def test_sigkill_host_with_full_nonreading_stdin_retires_exact_owned_group(self): + root = BASE / "output" / ("sim14-planner-backpressure-" + uuid.uuid4().hex[:10]) + root.mkdir(mode=0o700) + fake = root / "fake-cli" + fake.write_text("#!" + sys.executable + "\n" + '''import array,fcntl,json,os,sys,termios,time +from pathlib import Path +root=Path(sys.argv[-1]) +(root/'.nonreading-ready').write_text(str(os.getpid())) +(root/'.nonreading-ready').replace(root/'nonreading-ready') +pending=array.array('i',[0]) +deadline=time.monotonic()+3 +while time.monotonic()=16384: + break + time.sleep(.01) +(root/'.buffered.json').write_text(json.dumps({'bytes':pending[0]})) +(root/'.buffered.json').replace(root/'buffered.json') +while True: + time.sleep(.1) +''') + fake.chmod(0o700) + sink = (root / "host.log").open("wb") + host = subprocess.Popen([sys.executable, str(Path(__file__).resolve()), "--fixture", str(root)], stdout=sink, stderr=subprocess.STDOUT) + startup = None + evidence = {"fake_policy": True, "case": "host-death-during-full-planner-stdin", "passed": False} + try: + deadline = time.monotonic() + 5 + while not (root / "nonreading-ready").exists() and time.monotonic() < deadline: + time.sleep(.02) + self.assertTrue((root / "nonreading-ready").exists()) + startup = protocol.decode(protocol.read_bytes(root / "planner-process.json")) + registered = protocol.decode(protocol.read_bytes(root / "host-ready.json")) + self.assertEqual(startup["host_pid"], host.pid) + self.assertEqual(startup["guardian_pid"], registered["guardian_pid"]) + self.assertEqual(startup["planner_pid"], int((root / "nonreading-ready").read_text())) + self.assertGreater((root / "prompt.txt").stat().st_size, 32768) + self.assertEqual(os.getpgid(startup["planner_pid"]), startup["planner_pgid"]) + deadline = time.monotonic() + 4 + while not (root / "buffered.json").exists() and time.monotonic() < deadline: + time.sleep(.02) + self.assertTrue((root / "buffered.json").exists()) + buffered = protocol.decode(protocol.read_bytes(root / "buffered.json")) + evidence["buffered_bytes_before_host_kill"] = buffered["bytes"] + self.assertGreaterEqual(buffered["bytes"], 16384) + host.kill() + self.assertEqual(host.wait(5), -signal.SIGKILL) + deadline = time.monotonic() + 5 + while not (root / "planner-closure.json").exists() and time.monotonic() < deadline: + time.sleep(.02) + evidence.update(startup=startup, host_exit=host.returncode, + closure_saved=(root / "planner-closure.json").exists()) + self.assertTrue(evidence["closure_saved"], "EOF cleanup stalled behind full planner stdin") + closure = protocol.decode(protocol.read_bytes(root / "planner-closure.json")) + self.assertEqual(closure["reason"], "host liveness pipe closed") + self.assertTrue(closure["group_closed"]) + with self.assertRaises(ProcessLookupError): + os.kill(startup["planner_pid"], 0) + evidence.update(passed=True, closure=closure) + finally: + if host.poll() is None: + host.kill() + host.wait(5) + # Only the just-spawned fixture's registered, separate process + # groups are eligible; no restart cleanup signals stored PIDs. + if startup: + for key in ("planner_pid", "guardian_pid"): + pid = startup[key] + try: + if os.getpgid(pid) == pid: + os.killpg(pid, signal.SIGKILL) + except ProcessLookupError: + pass + sink.close() + protocol.create_file(root / "conformance.json", protocol.encoded(evidence)) + + +if __name__ == "__main__": + if len(sys.argv) == 3 and sys.argv[1] == "--fixture": + fixture(protocol.directory(Path(sys.argv[2]))) + else: + unittest.main() diff --git a/tools/physical-workcell/test_qualify_order_handoff.py b/tools/physical-workcell/test_qualify_order_handoff.py new file mode 100644 index 0000000..c19f53d --- /dev/null +++ b/tools/physical-workcell/test_qualify_order_handoff.py @@ -0,0 +1,148 @@ +"""Qualifier provenance/cleanup boundaries with synthetic files and no qualification launch.""" + +import json +import tempfile +import unittest +from pathlib import Path +from unittest import mock + +import controller +import qualify_order_handoff as qualifier + + +class QualifierBoundaryTests(unittest.TestCase): + def setUp(self): + output = qualifier.BASE / "output" + output.mkdir(mode=0o700, exist_ok=True) + self.temporary = tempfile.TemporaryDirectory(prefix="qualifier-boundary-", dir=output) + self.addCleanup(self.temporary.cleanup) + self.directory = Path(self.temporary.name) + + def fixture(self, bound_socket): + fixture = qualifier.Fixture("test_real_contact_fulfillment_commits_exact_version_and_receipt") + fixture.control = self.directory / "control" + fixture.control.mkdir(mode=0o700) + binding = {"version": 1, "source_kind": controller.body.SOURCE_KIND, + "controller_sha256": qualifier.sha(qualifier.CONTROLLER.read_bytes()), + "controller_dir": str(fixture.control), "socket_path": str(bound_socket)} + (fixture.control / "controller-binding.json").write_text(json.dumps(binding)) + (fixture.control / "controller-binding.json").chmod(0o600) + fixture.expected_native_pid = None + fixture.adapter_stopped = True + fixture.processes = [] + fixture.handles = [] + fixture.events = [] + return fixture + + def test_cleanup_observes_bound_socket_and_refuses_when_it_remains(self): + socket = self.directory / "bound.sock" + socket.touch(mode=0o600) + fixture = self.fixture(socket) + # Endpoint location is a boundary double; controller path safety has its own conformance suite. + with (mock.patch.object(qualifier, "expected_socket_path", return_value=socket, create=True), + self.assertRaisesRegex(RuntimeError, "socket")): + fixture.cleanup() + cleanup = fixture.events[-1] + self.assertTrue(cleanup["socket_exists"]) + self.assertEqual(cleanup["socket_path"], str(socket)) + self.assertTrue(socket.exists()) + + def test_cleanup_verifies_absence_of_exact_bound_socket(self): + socket = self.directory / "absent.sock" + fixture = self.fixture(socket) + with mock.patch.object(qualifier, "expected_socket_path", return_value=socket, create=True): + fixture.cleanup() + cleanup = fixture.events[-1] + self.assertEqual(cleanup["socket_path"], str(socket)) + self.assertFalse(cleanup["socket_exists"]) + self.assertTrue(cleanup["socket_absence_verified"]) + + def test_cleanup_refuses_binding_to_an_unrelated_path(self): + expected = self.directory / "expected.sock" + other = self.directory / "other.sock" + fixture = self.fixture(other) + with (mock.patch.object(qualifier, "expected_socket_path", return_value=expected, create=True), + self.assertRaisesRegex(RuntimeError, "binding")): + fixture.cleanup() + + def assert_wrong_source_refuses_absence(self, key, value): + socket = self.directory / "absent.sock" + fixture = self.fixture(socket) + binding_path = fixture.control / "controller-binding.json" + binding = json.loads(binding_path.read_bytes()) + binding[key] = value + binding_path.write_text(json.dumps(binding)) + with (mock.patch.object(qualifier, "expected_socket_path", return_value=socket), + self.assertRaisesRegex(RuntimeError, "binding")): + fixture.cleanup() + self.assertIsNone(fixture.events[-1]["socket_exists"]) + self.assertFalse(fixture.events[-1]["socket_absence_verified"]) + + def test_wrong_simulation_source_kind_cannot_prove_socket_absence(self): + self.assert_wrong_source_refuses_absence("source_kind", "physical_hardware") + + def test_wrong_controller_source_hash_cannot_prove_socket_absence(self): + self.assert_wrong_source_refuses_absence("controller_sha256", "0" * 64) + + def test_cleanup_dangling_socket_entry_is_not_misreported_absent(self): + socket = self.directory / "dangling.sock" + socket.symlink_to(self.directory / "missing") + fixture = self.fixture(socket) + with (mock.patch.object(qualifier, "expected_socket_path", return_value=socket, create=True), + self.assertRaisesRegex(RuntimeError, "socket")): + fixture.cleanup() + self.assertTrue(fixture.events[-1]["socket_exists"]) + + def test_missing_socket_binding_cannot_prove_absence(self): + fixture = self.fixture(self.directory / "absent.sock") + (fixture.control / "controller-binding.json").unlink() + with self.assertRaisesRegex(RuntimeError, "unconfirmed"): + fixture.cleanup() + self.assertIsNone(fixture.events[-1]["socket_exists"]) + self.assertFalse(fixture.events[-1]["socket_absence_verified"]) + + def test_dirty_bytes_refuse_before_output_creation_or_scenario_launch(self): + base = self.directory / "tools" / "physical-workcell" + base.mkdir(parents=True, mode=0o700) + (base / "output").mkdir(mode=0o700) + (base / "source.py").write_bytes(b"dirty current bytes") + output = base / "output" / "must-not-run" + commit = "a" * 40 + + def git(command, **kwargs): + if command[1:3] == ["rev-parse", "HEAD"]: + return commit if kwargs.get("text") else commit.encode() + return b"committed source bytes" + + with (mock.patch.object(qualifier, "BASE", base), + mock.patch.object(qualifier, "SOURCE_FILES", ("source.py",)), + mock.patch.object(qualifier.subprocess, "check_output", side_effect=git), + mock.patch.object(qualifier, "Fixture", side_effect=AssertionError("qualification launched")) as fixture, + mock.patch("sys.argv", ["qualifier", "--output-dir", str(output), "--source-commit", commit])): + with self.assertRaisesRegex(ValueError, "source differs"): + qualifier.main() + fixture.assert_not_called() + self.assertFalse(output.exists()) + + def test_matching_committed_bytes_produce_content_bound_freeze(self): + base = self.directory / "tools" / "physical-workcell" + base.mkdir(parents=True, mode=0o700) + content = b"committed source bytes" + (base / "source.py").write_bytes(content) + commit = "a" * 40 + with (mock.patch.object(qualifier, "BASE", base), + mock.patch.object(qualifier, "SOURCE_FILES", ("source.py",)), + mock.patch.object(qualifier.subprocess, "check_output", side_effect=[commit, content]) as git): + self.assertEqual(qualifier.freeze(commit), {"source.py": qualifier.sha(content)}) + self.assertEqual(git.call_args.args[0], ["git", "show", f"{commit}:tools/physical-workcell/source.py"]) + + def test_named_commit_mismatch_refuses_before_source_reads(self): + with (mock.patch.object(qualifier.subprocess, "check_output", return_value="a" * 40), + mock.patch.object(qualifier.protocol, "read_bytes") as read): + with self.assertRaisesRegex(ValueError, "current HEAD"): + qualifier.freeze("b" * 40) + read.assert_not_called() + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_score_operator.py b/tools/physical-workcell/test_score_operator.py new file mode 100644 index 0000000..e71f9ac --- /dev/null +++ b/tools/physical-workcell/test_score_operator.py @@ -0,0 +1,39 @@ +"""Adversarial scorer conformance; these fixtures are not model-run evidence.""" +import unittest +import uuid + +import order_protocol as protocol +import score_operator + + +class ScorerTests(unittest.TestCase): + def setUp(self): + self.root = protocol.directory(score_operator.BASE / "output" / ("scorer-test-" + uuid.uuid4().hex[:10]), create=True) + (self.root / "bench").mkdir(mode=0o700) + (self.root / "agent").mkdir(mode=0o700) + (self.root / "bench/operations").mkdir(mode=0o700) + protocol.persist(self.root / "bench/bench.json", {"operations": {}}) + protocol.persist(self.root / "agent/agent.json", {}) + + def calls(self, reason): + return [{"job_id": "diagnostic", "call": 1, "outcome": "refused", "reason": reason, + "decision": {"action": "measure"}}, + {"job_id": "diagnostic", "call": 2, "outcome": "executed", "decision": {"action": "move"}}] + + def test_refused_measure_and_later_move_do_not_prove_any_force(self): + result = score_operator.fault_score(self.root, self.calls("observation is stale or foreign; no requested effect dispatched")) + self.assertFalse(result["passed"]) + self.assertIn("native force", result["reason"]) + + def test_out_of_zone_refusal_does_not_count_as_stale_even_with_force(self): + receipt = {"operation_id": "force", "job_id": "diagnostic"} + content = protocol.encoded({"receipt": receipt, "request": {"kind": "disturb"}}) + protocol.create_file(self.root / "bench/operations/force.json", content) + protocol.persist(self.root / "bench/bench.json", {"operations": {"force": protocol.sha(content)}}) + result = score_operator.fault_score(self.root, self.calls("block is outside the target region")) + self.assertFalse(result["passed"]) + self.assertIn("exact stale", result["reason"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_score_workbench_crashes.py b/tools/physical-workcell/test_score_workbench_crashes.py new file mode 100644 index 0000000..1af5967 --- /dev/null +++ b/tools/physical-workcell/test_score_workbench_crashes.py @@ -0,0 +1,163 @@ +"""Pure retained-evidence negative controls; never actual-model qualification.""" +import copy +import sqlite3 +import unittest +import uuid +from pathlib import Path +from unittest import mock + +import body_client +import body_protocol as wire +import order_protocol as protocol +import score_workbench_crashes as scorer + + +def command(): + return {"protocol": wire.PROTOCOL, "adapter_id": "adapter", "body_id": "body", "source_sha256": "0" * 64, + "client_id": "client", "lease_id": "lease", "epoch": 1, "command_id": "command-one", + "operation": "dispatch", "arguments": {"proposal_id": "proposal-one"}} + + +def receipt(request): + return {**{key: request[key] for key in ("protocol", "adapter_id", "body_id", "source_sha256", "command_id")}, + "request_sha256": protocol.sha(protocol.encoded(request)), "state": "completed", "result": {}, "reason": None, + "receipt_scope": "retained_command_result", "recorded_epoch": request["epoch"]} + + +class CrashScorerTests(unittest.TestCase): + def test_only_native_gap_accepts_database_not_yet_created_without_writing(self): + root = scorer.BASE / "output" / ("sim13-score-missing-db-" + uuid.uuid4().hex[:10]) + root.mkdir(mode=0o700) + result = scorer.measurement_score(root, native_gap=True) + self.assertEqual(result["count"], 0) + self.assertEqual(result["rows"], []) + self.assertIs(result["database_created"], False) + self.assertEqual(list(root.iterdir()), []) + with self.assertRaises(sqlite3.OperationalError): + scorer.measurement_score(root) + self.assertEqual(list(root.iterdir()), []) + protocol.create_file(root / "measurements.sqlite", b"") + with self.assertRaises(sqlite3.OperationalError): + scorer.measurement_score(root, native_gap=True) + + def test_terminal_envelope_rejects_digest_identity_and_epoch_substitution(self): + request = command() + good = receipt(request) + self.assertTrue(scorer.envelope_bound(request, good)) + for changes in ({"request_sha256": "1" * 64}, {"body_id": "foreign"}, {"recorded_epoch": 2}, {"recorded_epoch": True}): + with self.subTest(changes=changes): + self.assertFalse(scorer.envelope_bound(request, {**good, **changes})) + + def boundary_fixture(self): + request = command() + token = request["command_id"] + identity = {**{key: request[key] for key in ("protocol", "adapter_id", "body_id", "source_sha256", "epoch")}, "simulation": True} + public = {"runner_status": "blocked", "model_request_pending": False, "recovery": {"command_id": token}} + before = {"host": {"app_id": "app", "body_identity": identity, "sequence": 10, + "stage": {"operation": "dispatch", "arguments": request["arguments"], "command_id": token, "before_commands": []}}, + "client": {"commands": {token: {"request_sha256": protocol.sha(protocol.encoded(request)), "send_count": 1, "operation": "dispatch"}}, + "pending_command_ids": [token], "lease": {"lease_id": "lease", "epoch": 1}}, "posts": [{"sequence": 4, "command_id": token}], + "public": public, "processes": {"body": 101}, "body_identity": identity, + "body_command": {"terminal": receipt(request)}} + offline = copy.deepcopy(before) + offline["processes"]["body"] = None + restored = copy.deepcopy(before) + restored["host"]["sequence"] = 20 + restored["processes"]["body"] = 202 + restored["body_identity"]["epoch"] = 2 + boundary = {"case": "body-uncertain", "role": "body", "command_id": token, "request_sha256": protocol.sha(protocol.encoded(request)), + "gate": None, "pre_snapshot": "before.json", "offline_snapshot": "offline.json", "restored_snapshot": "restored.json", + "old_epoch": 1, "new_epoch": 2, "process": {"pid": 101, "group": 101, "exit_observed": True, "returncode": -9, + "children_before": [], "children_after": []}} + snapshots = {"before.json": before, "offline.json": offline, "restored.json": restored} + events = [{"seq": 21, "kind": "reconciled", "outcome": token}, {"seq": 22, "kind": "resume"}] + transport = {token: {"request": request, "result": receipt(request)}} + return boundary, identity, transport, events, snapshots + + def test_crash_boundary_rejects_successor_command_before_recovery(self): + args = self.boundary_fixture() + with mock.patch.object(scorer, "read", side_effect=lambda path: args[4][path.name]): + self.assertTrue(scorer.boundary_score(Path("/diagnostic"), *args[:4])["passed"]) + args[4]["restored.json"]["client"]["commands"]["new-command"] = {"request_sha256": "2" * 64, "send_count": 1, "operation": "observe"} + with self.assertRaisesRegex(protocol.Refusal, "introduced"): + scorer.boundary_score(Path("/diagnostic"), *args[:4]) + + def test_crash_boundary_rejects_wrong_token_boot_and_owned_children(self): + for mutation in ("token", "boot", "child"): + args = self.boundary_fixture() + if mutation == "token": + args[0]["request_sha256"] = "2" * 64 + elif mutation == "boot": + args[0]["new_epoch"] = 3 + else: + args[0]["process"]["children_before"] = [{"pid": 303}] + with self.subTest(mutation=mutation), mock.patch.object(scorer, "read", side_effect=lambda path, snapshots=args[4]: snapshots[path.name]), self.assertRaises(protocol.Refusal): + scorer.boundary_score(Path("/diagnostic"), *args[:4]) + + def test_receipt_after_resume_is_refused(self): + args = self.boundary_fixture() + args[3][0]["seq"], args[3][1]["seq"] = 23, 21 + with mock.patch.object(scorer, "read", side_effect=lambda path: args[4][path.name]), self.assertRaisesRegex(protocol.Refusal, "precede"): + scorer.boundary_score(Path("/diagnostic"), *args[:4]) + + def test_auxiliary_exact_state_is_not_admission_authority(self): + request = command() + identity = {key: request[key] for key in ("body_id", "adapter_id", "source_sha256", "epoch")} + state = {"state": "ready", "identity": identity} + evidence = {**identity, "state_sha256": protocol.sha(protocol.encoded(state))} + proof = {"seq": 1, "kind": "admission_proof", "outcome": "dispatch", "time": 9.5, "evidence": evidence} + relay = [{"request": {"method": "GET", "path": "/state", "started_time": 9, "headers": body_client.pinned_headers(request)}, + "upstream": {"status": 200, "time": 9.2}, "response": protocol.encoded(state)}, + {"request": {"method": "POST", "path": "/lease", "started_time": 9.6, "headers": {}}, "upstream": {"status": 200}, + "response": protocol.encoded({"lease_id": "lease"}), "payload": protocol.encoded({"expected_epoch": 1})}] + transport = {"command-one": {"request": request, "received": {"sequence": 4}, "relay": {"request": {"started_time": 10.1}}}} + self.assertTrue(scorer.fresh_admissions(transport, relay, [proof])[0]["passed"]) + relay[0]["request"]["headers"] = {"X-Footwork-Health-Probe": "1"} + with self.assertRaisesRegex(protocol.Refusal, "auxiliary"): + scorer.fresh_admissions(transport, relay, [proof]) + relay[0]["request"]["headers"] = {**body_client.pinned_headers(request), "X-Source-Sha256": "1" * 64} + with self.assertRaisesRegex(protocol.Refusal, "different boot"): + scorer.fresh_admissions(transport, relay, [proof]) + + def test_native_gap_browser_cannot_claim_completion_or_successor(self): + state = {"app_id": "app", "connection": {"body_id": "body"}, "runner_status": "blocked", "recovery": {"command_id": "command-one"}} + names = ("crash-offline", "crash-restored", "crash-blocked", "crash-still-blocked") + steps = [{"name": name, "state": copy.deepcopy(state), "browser_action": "Observed native gap", "visible_text": "Outcome uncertain", + "client_commands": 4} for name in names] + client = {"commands": {str(index): {} for index in range(4)}} + boundary = {"command_id": "command-one"} + with mock.patch.object(scorer, "read", return_value={"steps": steps}): + self.assertTrue(scorer.ui_score(Path("/diagnostic"), "body-native-gap", boundary, state, client)["passed"]) + steps[-1]["state"]["runner_status"] = "running" + with self.assertRaisesRegex(protocol.Refusal, "released"): + scorer.ui_score(Path("/diagnostic"), "body-native-gap", boundary, state, client) + + def test_dead_host_browser_cache_requires_exact_provenance_and_is_not_live_state(self): + root = scorer.BASE / "output" / ("sim13-score-offline-" + uuid.uuid4().hex[:10]) + (root / "snapshots").mkdir(parents=True, mode=0o700) + before = {"app_id": "app", "runner_status": "running", "jobs": [], "connection": {"body_id": "body"}, + "recovery": {"state": "none", "command_id": None}, "model_request_pending": False, + "active_job_id": "job", "current_frame": {"available": True}, "usage": {"calls": 1}} + selected = {key: value for key, value in before.items() if key != "usage"} + protocol.create_file(root / "snapshots/before.json", protocol.encoded({"public": before})) + protocol.create_file(root / "snapshots/offline.json", protocol.encoded({"diagnostic": "owned dead host snapshot"})) + state = {**selected, "runner_status": "blocked", "recovery": {"state": "uncertain", "command_id": "command-one"}} + offline = {"name": "crash-offline", "state": None, "offline": True, "visible_text": "Disconnected: Failed to fetch", + "browser_action": "Observed owned host SIGKILL", "cached_public": selected, "pins": {"app_id": "app", "body_id": "body"}, + "snapshot_provenance": {"name": "offline.json", "sha256": protocol.sha(protocol.read_bytes(root / "snapshots/offline.json"))}} + steps = [offline, {"name": "crash-restored", "state": state, "visible_text": "Outcome uncertain", "browser_action": "Reloaded", "client_commands": 4}, + {"name": "crash-recovered", "state": {**state, "runner_status": "paused", "recovery": {"state": "recovered", "command_id": "command-one"}}, + "visible_text": "Receipt recovered", "browser_action": "Clicked Check receipt", "client_commands": 4}, + {"name": "crash-resumed", "state": {**state, "runner_status": "running"}, "visible_text": "Running", "browser_action": "Clicked Resume"}, + {"name": "crash-completed", "state": {**state, "jobs": []}, "visible_text": "Completed", "browser_action": "Observed", "client_commands": 5}] + protocol.create_file(root / "ui-ledger.json", protocol.encoded({"steps": steps})) + boundary = {"command_id": "command-one", "pre_snapshot": "before.json", "offline_snapshot": "offline.json"} + client = {"commands": {str(index): {} for index in range(5)}} + self.assertTrue(scorer.ui_score(root, "host-inflight", boundary, state, client)["passed"]) + offline["state"] = state + with mock.patch.object(scorer, "read", return_value={"steps": steps}), self.assertRaisesRegex(protocol.Refusal, "live"): + scorer.ui_score(root, "host-inflight", boundary, state, client) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_workbench_crash_harness.py b/tools/physical-workcell/test_workbench_crash_harness.py new file mode 100644 index 0000000..b797655 --- /dev/null +++ b/tools/physical-workcell/test_workbench_crash_harness.py @@ -0,0 +1,205 @@ +"""Mechanical owned-process/wire controls; no AI or browser qualification.""" +import concurrent.futures +import os +import signal +import subprocess +import threading +import time +import unittest +import uuid +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from unittest import mock + +import body_client +import order_protocol as protocol +import qualify_workbench_crashes as harness + + +class CrashHarnessTests(unittest.TestCase): + def setUp(self): + self.root = protocol.directory(harness.BASE / "output" / ("sim13-harness-" + uuid.uuid4().hex[:12]), create=True) + self.children = [] + + def tearDown(self): + for process in self.children: + if process.poll() is None: + os.killpg(process.pid, signal.SIGKILL) + process.wait(timeout=3) + + def control(self, **changes): + return {"sequence": 1, "action": "crash-role", "role": "workbench", "requested_time": 1.0, + "expect": {"app_id": "app", "body_id": "body", "epoch": 1, "runner_status": "blocked", + "active_job_id": "job", "command_id": "command", "model_request_pending": False}, **changes} + + def owned(self): + trial = harness.Trial(self.root, "diagnostic-not-a-source-freeze", "host-uncertain") + process = subprocess.Popen([str(harness.BASE / ".venv/bin/python"), "-c", "import time; time.sleep(20)"], start_new_session=True) + self.children.append(process) + record = {"role": "workbench", "boot": 1, "pid": process.pid, "group": os.getpgid(process.pid)} + trial.processes.append((process, record)) + trial.active["workbench"] = process + return trial, process, record + + def test_exact_registered_group_is_sigkilled_and_reaped(self): + trial, process, _ = self.owned() + result = trial.kill("workbench") + self.assertEqual(process.returncode, -signal.SIGKILL) + self.assertTrue(result["exit_observed"]) + self.assertEqual(result["children_before"], []) + self.assertEqual(result["children_after"], []) + self.assertIsNone(trial.active["workbench"]) + + def test_foreign_role_group_or_missing_registration_never_signalled(self): + trial, process, record = self.owned() + for case in ("role", "group", "missing"): + with self.subTest(case=case): + original = dict(record) + if case == "role": + record["role"] = "body" + elif case == "group": + record["group"] = process.pid + 1 + else: + trial.processes.clear() + with self.assertRaises(protocol.Refusal): + trial.kill("workbench") + self.assertIsNone(process.poll()) + record.clear() + record.update(original) + trial.processes = [(process, record)] + + def test_control_schema_rejects_stale_or_unscoped_crashes(self): + self.assertEqual(harness.validate_control(self.control())["action"], "crash-role") + for value in (self.control(sequence=True), self.control(role=None), self.control(pid=123), + self.control(expect={**self.control()["expect"], "runner_status": "running"}), + self.control(expect={**self.control()["expect"], "model_request_pending": True}), + self.control(action="start-role"), self.control(action="finish", role="body")): + with self.assertRaises((protocol.Refusal, TypeError)): + harness.validate_control(value) + + def test_indexed_receipt_requires_exact_digest_and_identity(self): + token = "diagnostic-command" + directory = protocol.directory(self.root / "body" / "commands" / protocol.sha(token.encode()), create=True) + request = {"command_id": token, "operation": "dispatch", "body_id": "body", "adapter_id": "adapter", "source_sha256": "a" * 64} + result = {**request, "state": "completed", "request_sha256": protocol.sha(protocol.encoded(request))} + content = protocol.encoded(result) + protocol.create_file(directory / "result.json", content) + protocol.persist(directory / "intent.json", {"request": request, "request_sha256": result["request_sha256"], "result_sha256": protocol.sha(content)}) + self.assertTrue(harness.indexed_command(self.root, token)["digest_valid"]) + protocol.persist(directory / "intent.json", {"request": request, "request_sha256": result["request_sha256"], "result_sha256": "f" * 64}) + with self.assertRaises(protocol.Refusal): + harness.indexed_command(self.root, token) + + def test_stopped_body_snapshot_does_not_query_body_endpoint(self): + trial = harness.Trial(self.root, "diagnostic", "body-native-gap") + trial.browser_origin, trial.body_origin, trial.identity = "http://127.0.0.1:1", "http://127.0.0.1:2", {} + for name in ("snapshots", "operator", "operator/client", "operator/link/events"): + protocol.directory(self.root / name, create=True) + host = {key: None for key in ("body_identity", "stage", "frame", "usage")} + host.update(app_id="app", runner_status="running", sequence=0) + protocol.persist(self.root / "operator" / "operator.json", host) + protocol.persist(self.root / "operator" / "client" / "client.json", {"commands": {}, "pending_command_ids": [], "lease": None, "identity": {}}) + with mock.patch.object(harness, "get", return_value={"app_id": "app"}) as getter: + _, snapshot = trial.snapshot("before", body_reads=False) + self.assertIsNone(snapshot["body_identity"]) + self.assertIsNone(snapshot["body_state"]) + self.assertEqual(getter.call_count, 1) + self.assertEqual(getter.call_args.args[:2], (trial.browser_origin, "/api/state")) + + def test_manual_crash_does_not_apply_to_automatic_gate_case(self): + protocol.directory(self.root / "control", create=True) + protocol.persist(self.root / "ready.json", {"case": "body-native-gap", "browser_origin": "http://127.0.0.1:1"}) + public = {"app_id": "app", "connection": {"body_id": "body", "epoch": 1}, "runner_status": "blocked", + "active_job_id": "job", "recovery": {"command_id": "command"}, "model_request_pending": False} + with mock.patch.object(harness, "get", return_value=public), self.assertRaises(protocol.Refusal): + harness.request(self.root, "crash-role", "body") + self.assertEqual(list((self.root / "control").iterdir()), []) + + def test_restart_waits_for_current_pinned_boot_before_snapshot(self): + trial = harness.Trial(self.root, "diagnostic", "body-uncertain") + protocol.directory(self.root / "control", create=True) + trial.browser_origin, trial.body_origin = "http://127.0.0.1:1", "http://127.0.0.1:2" + trial.identity = {"protocol": "footwork.body/1", "body_id": "body", "adapter_id": "adapter", + "source_sha256": "a" * 64, "simulation": True, "epoch": 1} + trial.boundary = {"role": "body", "command_id": "command", "restored_snapshot": None} + trial.stops["body"] = True + trial.active["body"] = None + public = {"runner_status": "blocked", "recovery": {"command_id": "command"}, "model_request_pending": False, + "connection": {"body_id": "body", "adapter_id": "adapter", "epoch": 1}} + current = {**public, "connection": {**public["connection"], "epoch": 2}} + snapshot = {"body_identity": {**trial.identity, "epoch": 2}} + with mock.patch.object(trial, "launch"), mock.patch.object(trial, "wait_public", return_value=snapshot["body_identity"]), mock.patch.object(harness, "get", side_effect=[public,current]) as getter, mock.patch.object(harness.time, "sleep"), mock.patch.object(trial, "snapshot", return_value=("restored.json",snapshot)) as capture: + trial.control({"sequence": 1, "action": "start-role", "role": "body", "expect": None, "requested_time": 1.0}) + self.assertEqual(getter.call_count, 2) + self.assertEqual(capture.call_count, 1) + self.assertEqual(trial.boundary["new_epoch"], 2) + + def test_host_offline_record_is_cached_provenance_not_live_authority(self): + protocol.directory(self.root / "snapshots", create=True) + public = {"app_id": "app", "runner_status": "running", "jobs": [], + "connection": {"body_id": "body"}, "recovery": {}, "model_request_pending": False, + "active_job_id": None, "current_frame": None, "activity": [{"intent": "Excluded from bounded UI ledger"}]} + protocol.persist(self.root / "ready.json", {"app_id": "app", "body_id": "body", "browser_origin": "http://127.0.0.1:1"}) + protocol.persist(self.root / "snapshots" / "before.json", {"public": public}) + protocol.persist(self.root / "snapshots" / "offline.json", {"client": {"commands": {}, "pending_command_ids": []}}) + protocol.persist(self.root / "crash-ledger.json", {"boundaries": [{"role": "workbench", "pre_snapshot": "before.json", + "offline_snapshot": "offline.json", "restored_snapshot": None}]}) + (self.root / "offline.txt").write_text("Disconnected; last observation retained") + with mock.patch.object(harness, "get", side_effect=OSError("owned host unavailable")): + harness.record(self.root, "crash-offline", self.root / "offline.txt", "Observed browser after owned SIGKILL") + step = harness.read(self.root / "ui-ledger.json")["steps"][0] + self.assertIsNone(step["state"]) + self.assertTrue(step["offline"]) + self.assertEqual(step["cached_public"]["runner_status"], "running") + self.assertNotIn("activity", step["cached_public"]) + self.assertEqual(step["snapshot_provenance"]["sha256"], protocol.sha(protocol.read_bytes(self.root / "snapshots" / "offline.json"))) + with mock.patch.object(harness, "get", return_value={**public, "app_id": "foreign"}), self.assertRaises(protocol.Refusal): + harness.record(self.root, "foreign-online", self.root / "offline.txt", "Foreign application rejected") + self.assertEqual(len(harness.read(self.root / "ui-ledger.json")["steps"]), 1) + + def test_external_relay_holds_exact_completed_reply_without_retry(self): + calls = [] + request = {"command_id": "fixture-command", "operation": "dispatch", "body_id": "body", "epoch": 1, + "arguments": {"proposal_id": "fixture-proposal"}} + response = {"state": "completed", "result": {"outcome": "executed"}} + payload, content = protocol.encoded(request), protocol.encoded(response) + + class Handler(BaseHTTPRequestHandler): + def log_message(self, *_): + pass + + def do_POST(self): + calls.append(self.rfile.read(int(self.headers["Content-Length"]))) + self.send_response(200) + self.send_header("Content-Length", str(len(content))) + self.end_headers() + self.wfile.write(content) + + upstream = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + thread = threading.Thread(target=upstream.serve_forever, kwargs={"poll_interval": .01}) + thread.start() + relay = harness.HeldReplyRelay(f"http://127.0.0.1:{upstream.server_port}", self.root / "relay").start() + try: + with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool: + future = pool.submit(body_client.http_call, relay.origin, "POST", "/commands", {}, payload, 4) + until = time.monotonic() + 3 + while not (relay.root / "barrier.json").exists() and time.monotonic() < until: + time.sleep(.01) + self.assertTrue((relay.root / "barrier.json").exists()) + marker = harness.read(relay.root / "barrier.json") + self.assertFalse(future.done()) + self.assertEqual(marker["request_sha256"], protocol.sha(payload)) + self.assertEqual(marker["response_sha256"], protocol.sha(content)) + relay.release.set() + self.assertEqual(future.result(timeout=3), (200, content)) + self.assertEqual(calls, [payload]) + finally: + relay.release.set() + relay.close() + upstream.shutdown() + thread.join(3) + upstream.server_close() + self.assertEqual(harness.read(relay.root / "cleanup.json")["active_workers"], 0) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_workbench_crash_runtime.py b/tools/physical-workcell/test_workbench_crash_runtime.py new file mode 100644 index 0000000..eaba4ca --- /dev/null +++ b/tools/physical-workcell/test_workbench_crash_runtime.py @@ -0,0 +1,400 @@ +"""Real SIGKILL host qualification at an externally withheld, known body reply. + +The deterministic planners are diagnostic only, never paid model evidence. +""" +import argparse +import json +import os +import signal +import socket +import subprocess +import sys +import threading +import time +import unittest +import uuid +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from unittest.mock import patch + +import body_client +import body_network +import codex_policy +import network_operator_runtime as runtime +import order_protocol as protocol + +BASE = Path(__file__).resolve().parent + + +def free_port(): + with socket.socket() as transport: + transport.bind(("127.0.0.1", 0)) + return transport.getsockname()[1] + + +def descendants(pid): + result = subprocess.run(["ps", "-axo", "pid=,ppid=,comm="], capture_output=True, text=True, check=True) + table = {} + for line in result.stdout.splitlines(): + parts = line.strip().split(maxsplit=2) + if len(parts) == 3: + table[int(parts[0])] = {"pid": int(parts[0]), "ppid": int(parts[1]), "name": Path(parts[2]).name} + owned = {pid} + while True: + added = {child for child, info in table.items() if info["ppid"] in owned} - owned + if not added: + break + owned.update(added) + return [table[child] for child in sorted(owned - {pid})] + + +def fixture(root, port, mode): + def policy(challenge, *_): + log = root / "diagnostic-policy.jsonl" + previous = log.read_text().splitlines() if log.exists() else [] + with log.open("a") as sink: + sink.write(json.dumps({"challenge_id": challenge["challenge_id"], + "generation": challenge["observation"]["generation"]}) + "\n") + sink.flush() + os.fsync(sink.fileno()) + return {"action": "move" if not previous else "stop", "target_x": -.25 if not previous else None, + "target_y": 0 if not previous else None, "intent": "Deterministic hard-crash conformance"}, {} + + if mode == "server": + import network_bench_operator + network_bench_operator.serve(root, port, policy) + return + if mode in {"planner", "planner-before"}: + import network_bench_operator + original_launch = codex_policy.subprocess.Popen + def launch_guard(*args, **kwargs): + argv = args[0] + if mode == "planner-before" and str(runtime.BASE / "planner_guard.py") in argv: + protocol.create_file(root / "pre-guardian.json", protocol.encoded({"pid": os.getpid()})) + os.kill(os.getpid(), signal.SIGSTOP) + return original_launch(*args, **kwargs) + with patch.object(codex_policy, "preflight", return_value={"available": True, "cli_version": codex_policy.EXPECTED_CLI}), \ + patch.object(codex_policy.shutil, "which", return_value=str(root.parent / "fake-cli")), \ + patch.object(codex_policy.subprocess, "Popen", side_effect=launch_guard): + network_bench_operator.serve(root, port, codex_policy.decide) + return + op = runtime.Operator(root, policy, start_worker=False) + original = protocol.persist + def stop_after_event(path, value): + if path == root / "operator.json" and value["activity"][-1]["kind"] == "diagnostic_tail": + protocol.create_file(root / "tail-barrier.json", protocol.encoded({"pid": os.getpid(), "sequence": value["sequence"]})) + os.kill(os.getpid(), signal.SIGSTOP) + return original(path, value) + protocol.persist = stop_after_event + op._event("diagnostic_tail", outcome="Known event file, not yet published in host manifest") + raise AssertionError("publication-tail fixture continued without its external controller") + + +class ReceiptGate: + """External fault owner; never changes command bytes or body receipts.""" + def __init__(self, upstream): + self.upstream = upstream + self.held, self.release = threading.Event(), threading.Event() + self.lock = threading.Lock() + self.records = [] + owner = self + class Handler(BaseHTTPRequestHandler): + def log_message(self, *_): + pass + def handle_request(self): + payload = self.rfile.read(int(self.headers.get("Content-Length", "0"))) + headers = {key: self.headers[key] for key in ("X-Body-Id", "X-Adapter-Id", "X-Source-Sha256") if key in self.headers} + try: + status, reply = body_client.http_call(owner.upstream, self.command, self.path, headers, + payload if self.command == "POST" else None, timeout=15, maximum=body_client.MAX_IMAGE) + request = protocol.decode(payload) if self.command == "POST" else None + entry = {"method": self.command, "path": self.path, "request": request, + "request_sha256": protocol.sha(payload), "response_sha256": protocol.sha(reply), "status": status} + with owner.lock: + owner.records.append(entry) + if request and request.get("operation") == "dispatch" and not owner.held.is_set(): + envelope = protocol.decode(reply) + if envelope["state"] != "completed": + raise AssertionError("gate requires independent body completion before host death") + owner.envelope = envelope + owner.held.set() + owner.release.wait(20) + self.send_response(status) + self.send_header("Content-Length", str(len(reply))) + self.send_header("Content-Type", "image/png" if self.path.startswith("/images/") else "application/json") + self.end_headers() + self.wfile.write(reply) + except (OSError, BrokenPipeError): + self.close_connection = True + do_GET = handle_request + do_POST = handle_request + self.server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + self.server.daemon_threads = True + self.origin = f"http://127.0.0.1:{self.server.server_address[1]}" + self.thread = threading.Thread(target=self.server.serve_forever, kwargs={"poll_interval": .1}, daemon=True) + self.thread.start() + + def close(self): + self.release.set() + self.server.shutdown() + self.thread.join(3) + self.server.server_close() + + +class WorkbenchCrashRuntimeTests(unittest.TestCase): + def setUp(self): + self.root = BASE / "output" / ("sim13-host-crash-" + uuid.uuid4().hex[:10]) + self.root.mkdir(mode=0o700) + self.body_root = self.root / "body" + body_network.initialize(self.body_root, diagnostic_enabled=True) + self.body = body_network.BodyService(self.body_root).start() + self.gate = ReceiptGate(self.body.origin) + self.host = self.root / "host" + runtime.initialize(self.host, self.gate.origin) + self.processes, self.sinks = [], [] + self.evidence = {"policy": "explicitly_fake_diagnostic", "case": self.id(), "processes": []} + self.op = None + + def tearDown(self): + self.gate.release.set() + if self.op: + self.op.close() + for child in self.processes: + if child.poll() is None: + child.terminate() + try: + child.wait(10) + except subprocess.TimeoutExpired: + child.kill() + child.wait(5) + self.evidence["processes"].append({"pid": child.pid, "exit_code": child.returncode}) + self.gate.close() + self.body.close() + for sink in self.sinks: + sink.close() + self.evidence["relay"] = self.gate.records + self.evidence["host_state"] = body_client.read_journal(self.host / "operator.json") + failed = any(test is self for test, _ in (*self._outcome.result.failures, *self._outcome.result.errors)) + self.evidence["passed"] = not failed + protocol.create_file(self.root / "conformance.json", protocol.encoded(self.evidence)) + + def launch(self, port, mode="server"): + sink = (self.root / f"child-{len(self.processes)}.log").open("wb") + self.sinks.append(sink) + child = subprocess.Popen([sys.executable, str(Path(__file__).resolve()), "--fixture", mode, + "--root", str(self.host), "--port", str(port)], stdout=sink, stderr=subprocess.STDOUT) + self.processes.append(child) + return child + + def wait(self, predicate, timeout=15): + until = time.monotonic() + timeout + while time.monotonic() < until: + if predicate(): + return + time.sleep(.02) + self.fail("owned crash fixture did not reach its declared boundary") + + def read_state(self, origin): + try: + status, content = body_client.http_call(origin, "GET", "/api/state", timeout=1) + return protocol.decode(content) if status == 200 else None + except (OSError, ValueError): + return None + + def post(self, origin, app_id, route, value): + status, content = body_client.http_call(origin, "POST", route, + {"Origin": origin, "X-Footwork-App-ID": app_id, "Content-Type": "application/json"}, protocol.encoded(value)) + self.assertEqual(status, 200, content.decode()) + return protocol.decode(content) + + def test_sigkill_host_after_known_body_effect_recovers_exact_receipt_without_replay(self): + port = free_port() + origin = f"http://127.0.0.1:{port}" + child = self.launch(port) + self.wait(lambda: self.read_state(origin) is not None) + initial = self.read_state(origin) + self.post(origin, initial["app_id"], "/api/jobs", + {"job_id": "crash-one", "label": "Owned host crash", "target": "A", "disturbance_test": False}) + self.assertTrue(self.gate.held.wait(15)) + before = body_client.read_journal(self.host / "client" / "client.json") + token = self.gate.envelope["command_id"] + self.assertEqual(before["pending_command_ids"], [token]) + self.assertEqual(self.body.body.status()["generation"], 1) + self.evidence["children_at_dispatch_gate"] = descendants(child.pid) + self.assertEqual(self.evidence["children_at_dispatch_gate"], []) + child.kill() + self.assertEqual(child.wait(5), -signal.SIGKILL) + retained = body_client.read_journal(self.host / "operator.json") + self.assertEqual(retained["stage"]["operation"], "dispatch") + self.assertNotIn(token, retained["applied_commands"]) + self.gate.release.set() + restarted = self.launch(port) + self.wait(lambda: self.read_state(origin) is not None) + recovered = self.read_state(origin) + self.assertEqual(recovered["app_id"], initial["app_id"]) + self.assertEqual(recovered["runner_status"], "blocked") + self.assertEqual(recovered["recovery"]["command_id"], token) + self.assertEqual(len(body_client.read_journal(self.host / "client" / "client.json")["commands"]), len(before["commands"])) + calls = (self.host / "diagnostic-policy.jsonl").read_text().splitlines() + self.assertEqual(len(calls), 1) + self.assertEqual(self.read_state(origin)["jobs"][0]["status"], "unknown") + settled = self.post(origin, initial["app_id"], "/api/reconcile", {}) + self.assertEqual(settled["runner_status"], "paused") + self.assertEqual(settled["recovery"]["state"], "recovered") + self.assertEqual(body_client.read_journal(self.host / "operator.json")["applied_commands"].count(token), 1) + self.assertEqual(len(body_client.read_journal(self.host / "client" / "client.json")["commands"]), len(before["commands"])) + self.post(origin, initial["app_id"], "/api/resume", {}) + self.wait(lambda: self.read_state(origin)["runner_status"] == "paused") + calls = [json.loads(line) for line in (self.host / "diagnostic-policy.jsonl").read_text().splitlines()] + self.assertEqual(len(calls), 2) + self.assertNotEqual(calls[0]["challenge_id"], calls[1]["challenge_id"]) + self.assertEqual(calls[1]["generation"], 1) + self.assertEqual(self.body.body.status()["generation"], 1) + self.assertEqual(body_client.read_journal(self.host / "client" / "client.json")["commands"][token]["send_count"], 1) + dispatches = [r for r in self.gate.records if r["request"] and r["request"].get("operation") == "dispatch"] + self.assertEqual([r["request"]["command_id"] for r in dispatches], [token]) + self.assertEqual(self.gate.envelope["recorded_epoch"], self.body.epoch) + self.evidence.update(command_id=token, before_count=len(before["commands"]), challenges=calls, + recovered=recovered, settled=settled, original_body_receipt=self.gate.envelope) + restarted.terminate() + self.assertEqual(restarted.wait(10), 0) + + def test_publication_tail_sigkill_repairs_only_audit_index_without_replay(self): + child = self.launch(0, "publication-tail") + self.wait(lambda: (self.host / "tail-barrier.json").exists()) + marker = protocol.decode(protocol.read_bytes(self.host / "tail-barrier.json")) + saved = body_client.read_journal(self.host / "operator.json") + self.assertEqual(marker["sequence"], saved["sequence"] + 1) + self.assertTrue((self.host / "events" / f"{marker['sequence']:06d}.json").exists()) + child.kill() + self.assertEqual(child.wait(5), -signal.SIGKILL) + self.op = runtime.Operator(self.host, lambda *_: self.fail("tail restart ran planner"), start_worker=False) + self.assertEqual(self.op.state()["runner_status"], "paused") + self.assertEqual(self.op.m["activity"][-1]["kind"], "journal_recovered") + self.op.resume() + self.assertEqual(self.op.state()["runner_status"], "running") + self.evidence["recovery"] = {"boundary": "event-fsync-before-manifest-publication", + "sequence": marker["sequence"], "effect_commands": 0} + self.assertEqual(len(self.op._journal()["commands"]), 0) + + def test_active_planner_host_sigkill_retires_group_and_resume_uses_fresh_image(self): + fake = self.root / "fake-cli" + fake.write_text("#!" + sys.executable + "\n" + '''import json,os,subprocess,sys,time +from pathlib import Path +args=sys.argv[1:] +out=Path(args[args.index('--output-last-message')+1]) +call=out.parent +sys.stdin.read() +if call.name.endswith('-01'): + child=subprocess.Popen([sys.executable,'-c','import time; time.sleep(120)']) + (call/'child-ready.json').write_text(json.dumps({'pid':os.getpid(),'child_pid':child.pid})) + while True: + print(json.dumps({'type':'diagnostic_heartbeat'}),flush=True) + time.sleep(.05) +out.write_text(json.dumps({'action':'stop','target_x':None,'target_y':None,'intent':'Fresh fake planner after crash.'})) +print(json.dumps({'type':'turn.completed','usage':{'input_tokens':20,'output_tokens':10}})) +''') + fake.chmod(0o700) + port = free_port() + origin = f"http://127.0.0.1:{port}" + child = self.launch(port, "planner") + self.wait(lambda: self.read_state(origin) is not None) + initial = self.read_state(origin) + self.post(origin, initial["app_id"], "/api/jobs", + {"job_id": "planner-crash", "label": "Active planner crash", "target": "A", "disturbance_test": False}) + call = self.host / "calls" / "planner-crash-01" + self.wait(lambda: (call / "child-ready.json").exists()) + startup = protocol.decode(protocol.read_bytes(call / "planner-process.json")) + pids = protocol.decode(protocol.read_bytes(call / "child-ready.json")) + self.assertEqual(startup["planner_pid"], pids["pid"]) + self.assertEqual(os.getpgid(pids["pid"]), startup["planner_pgid"]) + self.assertEqual(os.getpgid(pids["child_pid"]), startup["planner_pgid"]) + before = body_client.read_journal(self.host / "client" / "client.json") + challenge = protocol.decode(protocol.read_bytes(call / "reservation.json"))["challenge"] + owner = body_client.read_journal(self.host / "operator.json")["planner_owner"] + child.kill() # Host PID only: the separate guardian must detect EOF. + self.assertEqual(child.wait(5), -signal.SIGKILL) + self.wait(lambda: (call / "planner-closure.json").exists(), timeout=12) + closure = protocol.decode(protocol.read_bytes(call / "planner-closure.json")) + self.assertEqual(closure["reason"], "host liveness pipe closed") + self.assertTrue(closure["group_closed"]) + for pid in pids.values(): + with self.assertRaises(ProcessLookupError): + os.kill(pid, 0) + before_bytes = (call / "events.jsonl").read_bytes() + self.launch(port, "planner") + self.wait(lambda: self.read_state(origin) is not None) + paused = self.read_state(origin) + self.assertEqual(paused["runner_status"], "paused") + self.assertEqual(paused["jobs"][0]["status"], "paused") + self.assertEqual(paused["activity"][-1]["kind"], "planner_retired") + self.assertNotEqual(body_client.read_journal(self.host / "operator.json")["planner_owner"], owner) + self.assertEqual(len(body_client.read_journal(self.host / "client" / "client.json")["commands"]), len(before["commands"])) + self.assertFalse((call / "decision.json").exists()) + self.post(origin, initial["app_id"], "/api/resume", {}) + next_call = self.host / "calls" / "planner-crash-02" + self.wait(lambda: (next_call / "decision.json").exists()) + next_challenge = protocol.decode(protocol.read_bytes(next_call / "reservation.json"))["challenge"] + self.assertNotEqual(next_challenge["challenge_id"], challenge["challenge_id"]) + self.assertEqual((call / "events.jsonl").read_bytes(), before_bytes) + self.assertEqual(self.body.body.status()["generation"], 0) + self.assertFalse(any(r["request"] and r["request"].get("operation") == "dispatch" for r in self.gate.records)) + self.evidence.update(guardian=startup, closure=closure, paused=paused, no_dispatch=True, + challenge_before=challenge["challenge_id"], challenge_after=next_challenge["challenge_id"]) + + def test_host_sigkill_before_guardian_launch_keeps_reserved_budget_without_replay(self): + fake = self.root / "fake-cli" + fake.write_text("#!" + sys.executable + "\n" + '''import json,sys +from pathlib import Path +args=sys.argv[1:] +sys.stdin.read() +out=Path(args[args.index('--output-last-message')+1]) +out.write_text(json.dumps({'action':'stop','target_x':None,'target_y':None,'intent':'Fresh pre-launch recovery.'})) +print(json.dumps({'type':'turn.completed','usage':{'input_tokens':20,'output_tokens':10}})) +''') + fake.chmod(0o700) + port = free_port() + origin = f"http://127.0.0.1:{port}" + child = self.launch(port, "planner-before") + self.wait(lambda: self.read_state(origin) is not None) + initial = self.read_state(origin) + self.post(origin, initial["app_id"], "/api/jobs", + {"job_id": "planner-before", "label": "Planner pre-launch crash", "target": "A", "disturbance_test": False}) + self.wait(lambda: (self.host / "pre-guardian.json").exists()) + call = self.host / "calls" / "planner-before-01" + self.assertTrue((call / "planner-intent.json").exists()) + self.assertFalse((call / "planner-process.json").exists()) + before = body_client.read_journal(self.host / "client" / "client.json") + self.assertEqual(descendants(child.pid), []) + child.kill() + self.assertEqual(child.wait(5), -signal.SIGKILL) + self.launch(port, "planner") + self.wait(lambda: self.read_state(origin) is not None) + paused = self.read_state(origin) + self.assertEqual(paused["runner_status"], "paused") + self.assertEqual(paused["jobs"][0]["call_count"], 1) + self.assertEqual(paused["usage"]["calls"], 1) + self.assertEqual(paused["activity"][-1]["kind"], "planner_retired") + self.assertEqual(len(body_client.read_journal(self.host / "client" / "client.json")["commands"]), len(before["commands"])) + self.post(origin, initial["app_id"], "/api/resume", {}) + next_call = self.host / "calls" / "planner-before-02" + self.wait(lambda: (next_call / "decision.json").exists()) + first = protocol.decode(protocol.read_bytes(call / "reservation.json"))["challenge"] + second = protocol.decode(protocol.read_bytes(next_call / "reservation.json"))["challenge"] + self.assertNotEqual(first["challenge_id"], second["challenge_id"]) + self.assertFalse((call / "decision.json").exists()) + self.assertEqual(self.body.body.status()["generation"], 0) + self.evidence.update(paused=paused, pre_guardian_kill=True, no_dispatch=True, + challenge_before=first["challenge_id"], challenge_after=second["challenge_id"]) + + +if __name__ == "__main__": + if "--fixture" in sys.argv: + parser = argparse.ArgumentParser() + parser.add_argument("--fixture", choices=("server", "publication-tail", "planner", "planner-before"), required=True) + parser.add_argument("--root", type=Path, required=True) + parser.add_argument("--port", type=int, required=True) + args = parser.parse_args() + fixture(args.root, args.port, args.fixture) + else: + unittest.main() diff --git a/tools/physical-workcell/test_workbench_health.py b/tools/physical-workcell/test_workbench_health.py new file mode 100644 index 0000000..e721b79 --- /dev/null +++ b/tools/physical-workcell/test_workbench_health.py @@ -0,0 +1,168 @@ +"""Diagnostic socket/byte conformance; no UI, model, or native-state trial.""" +import http.client +import threading +import time +import unittest +import uuid +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer + +import body_client +import order_protocol as protocol +import qualify_workbench_health as harness +import score_workbench_health as scorer + +IDENTITY = {"protocol": "footwork.body/1", "body_id": "body-test", "adapter_id": "adapter-test", + "source_sha256": "a" * 64, "epoch": 1, "simulation": True} + + +class HealthHarnessTests(unittest.TestCase): + def setUp(self): + self.root = protocol.directory(harness.BASE / "output" / ("sim12-harness-" + uuid.uuid4().hex[:12]), create=True) + self.calls, self.lock = [], threading.Lock() + owner = self + + class Handler(BaseHTTPRequestHandler): + def log_message(self, *_): + pass + + def do_GET(self): + self.reply() + + def do_POST(self): + self.reply() + + def reply(self): + length = int(self.headers.get("Content-Length", 0)) + payload = self.rfile.read(length) + with owner.lock: + owner.calls.append({"method": self.command, "path": self.path, "payload": payload, + "headers": dict(self.headers)}) + value = IDENTITY if self.path == "/identity" else {"identity": IDENTITY, "state": "ready"} + content = protocol.encoded(value) + self.send_response(200) + self.send_header("Content-Length", str(len(content))) + self.end_headers() + self.wfile.write(content) + + self.server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + self.thread = threading.Thread(target=self.server.serve_forever, kwargs={"poll_interval": .01}) + self.thread.start() + self.relay = harness.HealthRelay(f"http://127.0.0.1:{self.server.server_port}", self.root / "health-relay").start() + + def tearDown(self): + self.relay.close() + self.server.shutdown() + self.thread.join(3) + self.server.server_close() + self.assertFalse(self.thread.is_alive()) + cleanup = protocol.decode(protocol.read_bytes(self.relay.root / "cleanup.json")) + self.assertTrue(cleanup["server_thread_exited"]) + self.assertEqual(cleanup["active_workers"], 0) + self.assertLessEqual(cleanup["peak_workers"], harness.MAX_WORKERS) + + def read(self, path, tagged=False): + headers = {**body_client.pinned_headers(IDENTITY), **({"X-Footwork-Health-Probe": "1"} if tagged else {})} + started = time.monotonic() + status, content = body_client.http_call(self.relay.origin, "GET", path, headers, timeout=4) + return status, content, time.monotonic() - started + + def test_only_tagged_identity_and_state_receive_actual_delay(self): + for path in ("/identity", "/state"): + with self.subTest(path=path): + _, plain, _ = self.read(path) + status, tagged, elapsed = self.read(path, tagged=True) + self.assertEqual(status, 200) + self.assertEqual(tagged, plain) + self.assertGreaterEqual(elapsed, 1.2) + records, valid = scorer.relay_records(self.root) + self.assertTrue(valid) + self.assertEqual([record["request"]["injected_delay_s"] for record in records], [0, 1.2, 0, 1.2]) + self.assertEqual(len(self.calls), 4) + + def test_post_bytes_identity_headers_immediate_and_no_retry(self): + payload = b'{"operation":"dispatch","command_id":"diagnostic-command","literal":"exact bytes"}' + headers = body_client.pinned_headers(IDENTITY) + status, _ = body_client.http_call(self.relay.origin, "POST", "/commands", headers, payload, timeout=4) + self.assertEqual(status, 200) + self.assertEqual(len(self.calls), 1) + self.assertEqual(self.calls[0]["payload"], payload) + for key, value in headers.items(): + self.assertEqual(self.calls[0]["headers"][key], value) + records, valid = scorer.relay_records(self.root) + self.assertTrue(valid) + self.assertEqual(records[0]["request"]["operation"], "dispatch") + self.assertEqual(records[0]["request"]["injected_delay_s"], 0) + self.assertEqual(records[0]["request"]["upstream_sends"], 1) + + def test_probe_annotation_cannot_change_effect_or_image_routes(self): + for method, path, payload in (("POST", "/commands", b'{"operation":"dispatch"}'), + ("GET", "/images/diagnostic", None)): + with self.assertRaises((OSError, http.client.HTTPException)): + body_client.http_call(self.relay.origin, method, path, {"X-Footwork-Health-Probe": "1"}, payload, timeout=2) + self.assertEqual(self.calls, []) + self.assertEqual(self.relay.sequence, 0) + + def test_owned_control_selects_state_without_changing_normal_reads(self): + protocol.persist(self.root / "health-relay-control.json", {"enabled": True, "paths": ["/state"]}) + self.read("/identity", tagged=True) + self.read("/state", tagged=True) + self.read("/state") + records, valid = scorer.relay_records(self.root) + self.assertTrue(valid) + self.assertEqual([record["request"]["injected_delay_s"] for record in records], [0, 1.2, 0]) + + +class AdmissionScorerTests(unittest.TestCase): + def fixture(self): + remote = {"state": "ready", "identity": IDENTITY} + state = {"request": {"method": "GET", "path": "/state", "tagged_health": False, "sequence": 1, + "headers": body_client.pinned_headers(IDENTITY), "started_monotonic": 1.0}, + "upstream": {"http_status": 200, "wall": 2.0, "finished_monotonic": 2.0}, + "content": protocol.encoded(remote)} + lease = {"request": {"method": "POST", "path": "/lease", "sequence": 2, + "started_monotonic": 3.0, "started_wall": 3.0}, + "upstream": {"http_status": 200, "finished_monotonic": 3.1}, + "content": protocol.encoded({"lease_id": "lease-1"}), + "payload": protocol.encoded({"expected_epoch": 1})} + command = {"request": {"method": "POST", "path": "/commands", "sequence": 3, "started_monotonic": 4.0}, + "upstream": {"finished_monotonic": 5.0}, + "payload": protocol.encoded({"command_id": "command-1", "lease_id": "lease-1", "epoch": 1, + "operation": "dispatch"})} + event = {"kind": "admission_proof", "seq": 1, "time": 2.1, "outcome": "dispatch", + "evidence": {**{key: IDENTITY[key] for key in ("body_id", "adapter_id", "source_sha256", "epoch")}, + "state_sha256": protocol.sha(protocol.encoded(remote))}} + return [state, lease, command], [event] + + def test_exact_fresh_untagged_state_before_lease_and_command_passes(self): + records, events = self.fixture() + self.assertTrue(scorer.fresh_admissions(records, events, IDENTITY)[0]["passed"]) + + def test_tagged_stale_foreign_or_unbound_proof_fails(self): + for case in ("tagged", "late", "foreign", "missing", "wrong_header", "wrong_epoch"): + records, events = self.fixture() + if case == "tagged": + records[0]["request"]["tagged_health"] = True + elif case == "late": + records[0]["upstream"]["finished_monotonic"] = 3.5 + elif case == "foreign": + events[0]["evidence"]["body_id"] = "foreign" + elif case == "missing": + events = [] + elif case == "wrong_header": + records[0]["request"]["headers"]["X-Body-Id"] = "foreign" + else: + events[0]["evidence"]["epoch"] = 2 + with self.subTest(case=case): + self.assertFalse(scorer.fresh_admissions(records, events, IDENTITY)[0]["passed"]) + + def test_one_proof_cannot_authorize_a_second_command(self): + records, events = self.fixture() + duplicate = {**records[2], "request": {**records[2]["request"], "started_monotonic": 6.0, "sequence": 4}} + records.append(duplicate) + reports = scorer.fresh_admissions(records, events, IDENTITY) + self.assertTrue(reports[0]["passed"]) + self.assertFalse(reports[1]["passed"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_workbench_link.py b/tools/physical-workcell/test_workbench_link.py new file mode 100644 index 0000000..e6f7568 --- /dev/null +++ b/tools/physical-workcell/test_workbench_link.py @@ -0,0 +1,451 @@ +"""Retained workbench-link checks with real TCP and unchanged native authority.""" +import concurrent.futures +import contextlib +import http.client +import socket +import threading +import time +import unittest +import uuid +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from unittest import mock + +import body_client +import body_network +import order_protocol as storage +import workbench_link + +BASE = Path(__file__).resolve().parent +ZONE = {"x_min": .04, "x_max": .10, "y_min": -.025, "y_max": .025} +PROBE_HEADER = "X-Footwork-Health-Probe" + + +class DelayedHealthGateway: + """Test-only real TCP response barrier; never extends the product fault API.""" + def __init__(self, upstream, trickle=False): + self.upstream = upstream + self.release = threading.Event() + self.lock = threading.Lock() + self.calls = [] + self.active = {} + self.peak = {} + owner = self + + class Handler(BaseHTTPRequestHandler): + def log_message(self, *_): + pass + + def do_GET(self): + tagged = self.headers.get(PROBE_HEADER) == "1" + with owner.lock: + owner.calls.append({"path": self.path, "tagged": tagged}) + if tagged: + owner.active[self.path] = owner.active.get(self.path, 0) + 1 + owner.peak[self.path] = max(owner.peak.get(self.path, 0), owner.active[self.path]) + try: + if tagged and not trickle: + owner.release.wait(3) + headers = {key: value for key, value in self.headers.items() if key.lower() != "host"} + status, content = body_client.http_call(owner.upstream, "GET", self.path, headers, timeout=2) + self.send_response(status) + self.send_header("Content-Length", str(len(content))) + self.send_header("Connection", "close") + self.end_headers() + if tagged and trickle: + for byte in content: + try: + self.wfile.write(bytes([byte])) + self.wfile.flush() + except OSError: + break + time.sleep(.03) + else: + with contextlib.suppress(OSError): + self.wfile.write(content) + finally: + if tagged: + with owner.lock: + owner.active[self.path] -= 1 + + self.server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + self.origin = f"http://127.0.0.1:{self.server.server_port}" + self.thread = threading.Thread(target=self.server.serve_forever, kwargs={"poll_interval": .05}) + self.thread.start() + + def close(self): + self.release.set() + self.server.shutdown() + self.server.server_close() + self.thread.join() + + +class WorkbenchLinkTests(unittest.TestCase): + def setUp(self): + self.root = BASE / "output" / ("sim10-link-" + uuid.uuid4().hex[:12]) + self.root.mkdir(mode=0o700) + body_network.initialize(self.root / "body", diagnostic_enabled=True) + self.server = body_network.BodyService(self.root / "body").start() + self.link = workbench_link.WorkbenchLink(self.server.origin, self.root / "link").start() + self.client = body_client.BodyClient(self.root / "client", self.link.origin, timeout=2) + self.evidence = [] + + def tearDown(self): + self.client.close() + self.link.close() + self.server.close() + result = self._outcome.result + failed = any(test is self for test, _ in (*result.failures, *result.errors)) + storage.create_file(self.root / "conformance.json", storage.encoded({"test_id": self.id(), "passed": not failed, + "scenario_kind": "actual_tcp_retained_link_native_body", "evidence": self.evidence})) + + def events(self): + return [storage.decode(storage.read_bytes(path)) for path in sorted((self.root / "link" / "events").glob("*.json"))] + + def prepare(self): + self.client.acquire() + self.client.start_job("job", ZONE) + challenge = self.client.observe() + return self.client.propose(challenge["challenge_id"], "move", "bounded real native approach", -.19, 0) + + def command_id(self, proposal_id): + manifest = body_client.read_journal(self.root / "client" / "client.json") + matches = [] + for command_id in manifest["commands"]: + request = storage.decode(storage.read_bytes(self.root / "client" / "requests" / (command_id + ".json"))) + if request["operation"] == "dispatch" and request["arguments"]["proposal_id"] == proposal_id: + matches.append(command_id) + self.assertEqual(len(matches), 1) + return matches[0] + + def test_drop_after_effect_is_unknown_and_read_only_recovery_has_one_forward(self): + proposal = self.prepare() + self.link.arm("drop_after") + with self.assertRaises(body_client.Uncertain) as caught: + self.client.dispatch(proposal["proposal_id"]) + command_id = caught.exception.command_id + self.assertIsNone(self.link.state()["fault"]) + self.assertEqual(self.server.state()["bench"]["generation"], 1) + receipt = self.client.reconcile(command_id) + self.evidence.append(receipt) + self.assertEqual(receipt["state"], "completed") + self.assertEqual(receipt["result"]["outcome"], "executed") + forwards = [event for event in self.events() if event["kind"] == "forwarded" and event.get("command_id") == command_id] + self.assertEqual(len(forwards), 1) + self.assertEqual(self.server.state()["bench"]["generation"], 1) + + def test_duplicate_and_cached_terminal_requests_never_add_native_effects(self): + proposal = self.prepare() + self.link.arm("duplicate") + result = self.client.dispatch(proposal["proposal_id"]) + command_id = self.command_id(proposal["proposal_id"]) + manifest = body_client.read_journal(self.root / "client" / "client.json") + request = storage.read_bytes(self.root / "client" / "requests" / (command_id + ".json")) + status, content = body_client.http_call(self.link.origin, "POST", "/commands", + body_client.pinned_headers(manifest["identity"]), request) + cached = storage.decode(content) + self.evidence.append(cached) + self.assertEqual(status, 200) + self.assertEqual(cached["result"], result) + self.assertEqual(self.server.state()["bench"]["generation"], 1) + forwards = [event for event in self.events() if event["kind"] == "forwarded" and event.get("command_id") == command_id] + self.assertEqual(len(forwards), 3) + duplicate = [event for event in self.events() if event["kind"] == "duplicate_replies"] + self.assertEqual(len(duplicate), 1) + self.assertTrue(duplicate[0]["same_reply"]) + + def test_offline_and_reconnect_never_forward_the_saved_command(self): + self.client.acquire(duration_s=.1) + self.link.set_connected(False) + with self.assertRaises(body_client.Uncertain) as caught: + self.client.observe() + token = caught.exception.command_id + with self.assertRaises(body_client.Uncertain): + self.client.reconcile(token) + before = len([event for event in self.events() if event["kind"] == "forwarded"]) + self.link.set_connected(True) + self.assertEqual(len([event for event in self.events() if event["kind"] == "forwarded"]), before) + self.assertEqual(self.server.state()["bench"]["generation"], 0) + time.sleep(.15) + result = self.client.reconcile(token) + self.evidence.append(result) + self.assertEqual(result["state"], "not_accepted") + self.assertFalse([event for event in self.events() if event["kind"] == "forwarded" and event.get("command_id") == token]) + self.assertTrue(any(event["kind"] == "disconnected_request" and event["path"].startswith("/commands/") + for event in self.events())) + + def test_restart_retains_fixed_endpoint_offline_and_payload_history(self): + self.prepare() + self.link.set_connected(False) + origin = self.link.origin + before = {p.name: p.read_bytes() for p in (self.root / "link" / "requests").iterdir()} + self.link.close() + self.link = workbench_link.WorkbenchLink(self.server.origin, self.root / "link").start() + self.assertEqual(self.link.origin, origin) + self.assertFalse(self.link.state()["connected"]) + self.assertEqual({p.name: p.read_bytes() for p in (self.root / "link" / "requests").iterdir()}, before) + self.assertEqual(self.server.state()["bench"]["generation"], 0) + self.link.set_connected(True) + self.assertIn("challenge_id", self.client.observe()) + self.assertEqual(self.server.state()["bench"]["generation"], 0) + self.evidence.append(self.link.state()) + + def test_restart_cancels_armed_fault_without_forwarding(self): + self.link.arm("duplicate") + origin = self.link.origin + self.link.close() + self.link = workbench_link.WorkbenchLink(self.server.origin, self.root / "link").start() + self.assertEqual(self.link.origin, origin) + self.assertIsNone(self.link.state()["fault"]) + self.assertTrue(any(event["kind"] == "fault_cancelled_on_restart" for event in self.events())) + self.assertFalse([event for event in self.events() if event["kind"] == "forwarded"]) + self.assertEqual(self.server.state()["bench"]["generation"], 0) + + def test_delay_does_not_hold_state_or_disconnect_lock(self): + proposal = self.prepare() + self.link.arm("delay_after", delay_s=.2) + self.client.timeout = .05 + with self.assertRaises(body_client.Uncertain) as caught: + self.client.dispatch(proposal["proposal_id"]) + token = caught.exception.command_id + started = time.monotonic() + snapshot = self.link.state() + self.link.set_connected(False) + self.assertLess(time.monotonic() - started, .1) + self.assertTrue(snapshot["connected"]) + self.assertFalse(self.link.state()["connected"]) + self.link.set_connected(True) + self.client.timeout = 2 + result = self.client.reconcile(token) + self.evidence.append(result) + self.assertEqual(result["state"], "completed") + self.assertEqual(self.server.state()["bench"]["generation"], 1) + + def test_header_rejection_never_forwards_or_admits_dispatch(self): + proposal = self.prepare() + manifest = body_client.read_journal(self.root / "client" / "client.json") + pinned = manifest["identity"] + request = {"protocol": body_client.PROTOCOL, "adapter_id": pinned["adapter_id"], "body_id": pinned["body_id"], + "source_sha256": pinned["source_sha256"], "client_id": manifest["client_id"], + "lease_id": manifest["lease"]["lease_id"], "epoch": manifest["lease"]["epoch"], + "command_id": "command-invalid-" + uuid.uuid4().hex, "operation": "dispatch", + "arguments": {"proposal_id": proposal["proposal_id"]}} + payload = storage.encoded(request) + host, port = body_client.endpoint(self.link.origin) + good = [("Host", f"{host}:{port}"), ("Content-Type", "application/json"), + *body_client.pinned_headers(pinned).items(), ("Content-Length", str(len(payload)))] + attempts = [good + [(name, dict(good)[name])] for name in ("Host", "Content-Type", "X-Body-Id")] + attempts += [good + [("Origin", "http://foreign.invalid")], + [(k, "foreign.invalid" if k == "Host" else v) for k, v in good]] + before = {str(p.relative_to(self.root / "body" / "commands")): p.read_bytes() + for p in (self.root / "body" / "commands").rglob("*") if p.is_file()} + for headers in attempts: + wire = ("POST /commands HTTP/1.1\r\n" + "".join(f"{k}: {v}\r\n" for k, v in headers) + "\r\n").encode() + payload + with socket.create_connection((host, port), timeout=2) as connection: + connection.sendall(wire) + try: + reply = connection.recv(1024) + except ConnectionResetError: + reply = b"" + self.assertEqual(reply, b"") + self.assertEqual({str(p.relative_to(self.root / "body" / "commands")): p.read_bytes() + for p in (self.root / "body" / "commands").rglob("*") if p.is_file()}, before) + self.assertEqual(self.server.state()["bench"]["generation"], 0) + self.assertFalse([event for event in self.events() if event["kind"] == "forwarded" and + event.get("command_id") == request["command_id"]]) + self.evidence.append({"rejected_command": request, "invalid_headers": attempts}) + + def test_identity_endpoint_lock_and_state_copy_guards(self): + snapshot = self.link.state() + snapshot["connected"] = False + self.assertTrue(self.link.state()["connected"]) + with self.assertRaises(storage.Refusal): + workbench_link.WorkbenchLink(self.server.origin, self.root / "link").start() + origin = self.link.origin + self.link.close() + with self.assertRaises(storage.Refusal): + workbench_link.WorkbenchLink("http://127.0.0.1:1", self.root / "link").start() + with self.assertRaises(storage.Refusal): + workbench_link.WorkbenchLink(self.server.origin, self.root / "link", port=1).start() + self.link = workbench_link.WorkbenchLink(self.server.origin, self.root / "link").start() + self.assertEqual(self.link.origin, origin) + with self.assertRaises(storage.Refusal): + self.link.set_connected(1) + with self.assertRaises(storage.Refusal): + self.link.arm("drop_before") + with self.assertRaises(storage.Refusal): + self.link.arm("delay_after", delay_s=3) + + def test_idle_polling_does_not_exhaust_retained_command_journal(self): + self.client.acquire() + before = len(self.events()) + for _ in range(10): + self.client.status() + self.assertEqual(len(self.events()), before) + self.evidence.append({"ordinary_state_polls": 10, "additional_journal_events": 0}) + + def test_slow_auxiliary_probes_are_single_flight_and_never_reuse_late_results(self): + gateway = DelayedHealthGateway(self.server.origin) + link = workbench_link.WorkbenchLink(gateway.origin, self.root / "slow-link").start() + pinned = body_client.read_journal(self.root / "client" / "client.json")["identity"] + headers = {**body_client.pinned_headers(pinned), PROBE_HEADER: "1"} + def probe(path): + started = time.monotonic() + status, content = body_client.http_call(link.origin, "GET", path, headers, timeout=2) + return path, status, storage.decode(content), time.monotonic() - started + pool = concurrent.futures.ThreadPoolExecutor(max_workers=6) + futures = [] + try: + futures = [pool.submit(probe, path) for path in ("/state", "/identity", "/state", "/identity", "/state", "/identity")] + deadline = time.monotonic() + 1 + while time.monotonic() < deadline: + with gateway.lock: + if len(gateway.calls) >= 2: + break + time.sleep(.005) + status, fresh = body_client.http_call(link.origin, "GET", "/state", body_client.pinned_headers(pinned), timeout=2) + self.assertEqual(status, 200) + self.assertEqual(storage.decode(fresh)["identity"], pinned) + # The barrier outlasts the .8-second caller budget, then releases all owned transports. + time.sleep(.8) + repeated = [probe(path) for path in ("/state", "/identity")] + gateway.release.set() + results = [future.result() for future in futures] + repeated + with gateway.lock: + peak = dict(gateway.peak) + tagged_before = sum(call["tagged"] for call in gateway.calls) + self.evidence.append({"results": results, "upstream_peak_by_path": peak, + "tagged_upstream_calls": tagged_before, "independent_full_status": status}) + self.assertEqual(peak, {"/state": 1, "/identity": 1}) + self.assertEqual(tagged_before, 2) + for path, status, result, elapsed in results: + self.assertEqual(status, 504) + self.assertEqual(result, {"version": 1, "kind": "health_read_pending", "path": path}) + self.assertLess(elapsed, .8) + # A subsequent probe obtains a new upstream response rather than the discarded late one. + deadline = time.monotonic() + 2 + while time.monotonic() < deadline: + with gateway.lock: + if not any(gateway.active.values()): + break + time.sleep(.005) + for path in ("/state", "/identity"): + deadline = time.monotonic() + 2 + while True: + _, status, _, _ = probe(path) + if status == 200 or time.monotonic() >= deadline: + break + time.sleep(.005) + self.assertEqual(status, 200) + with gateway.lock: + self.assertEqual(sum(call["tagged"] for call in gateway.calls), 4) + finally: + gateway.release.set() + pool.shutdown(wait=True) + link.close() + gateway.close() + + def test_probe_annotation_is_refused_before_any_upstream_dispatch(self): + proposal = self.prepare() + manifest = body_client.read_journal(self.root / "client" / "client.json") + pinned = manifest["identity"] + request = {"protocol": body_client.PROTOCOL, "adapter_id": pinned["adapter_id"], "body_id": pinned["body_id"], + "source_sha256": pinned["source_sha256"], "client_id": manifest["client_id"], + "lease_id": manifest["lease"]["lease_id"], "epoch": manifest["lease"]["epoch"], + "command_id": "command-invalid-probe-" + uuid.uuid4().hex, "operation": "dispatch", + "arguments": {"proposal_id": proposal["proposal_id"]}} + payload = storage.encoded(request) + host, port = body_client.endpoint(self.link.origin) + before = {str(p.relative_to(self.root / "body" / "commands")): p.read_bytes() + for p in (self.root / "body" / "commands").rglob("*") if p.is_file()} + cases = [("GET", "/state", ["0"]), ("GET", "/identity", ["1", "1"]), + ("GET", "/identity?probe=1", ["1"]), ("GET", "/commands/missing", ["1"]), + ("POST", "/commands", ["1"])] + forwards = sum(event["kind"] == "forwarded" for event in self.events()) + for method, path, annotations in cases: + content = payload if method == "POST" else b"" + headers = [("Host", f"{host}:{port}"), *body_client.pinned_headers(pinned).items(), + ("Content-Length", str(len(content))), ("Content-Type", "application/json"), + *[(PROBE_HEADER, value) for value in annotations]] + wire = (f"{method} {path} HTTP/1.1\r\n" + "".join(f"{k}: {v}\r\n" for k, v in headers) + "\r\n").encode() + content + with socket.create_connection((host, port), timeout=2) as connection: + connection.sendall(wire) + try: + reply = connection.recv(1024) + except ConnectionResetError: + reply = b"" + self.assertEqual(reply, b"") + self.assertEqual(sum(event["kind"] == "forwarded" for event in self.events()), forwards) + self.assertEqual({str(p.relative_to(self.root / "body" / "commands")): p.read_bytes() + for p in (self.root / "body" / "commands").rglob("*") if p.is_file()}, before) + self.assertEqual(self.server.state()["bench"]["generation"], 0) + self.evidence.append({"refused_probe_routes": cases, "generation": 0}) + + def test_probe_cleanup_joins_upstream_readers_before_releasing_root(self): + gateway = DelayedHealthGateway(self.server.origin) + link = workbench_link.WorkbenchLink(gateway.origin, self.root / "cleanup-link").start() + origin = link.origin + finished = threading.Event() + closer = None + try: + status, _ = body_client.http_call(origin, "GET", "/identity", {PROBE_HEADER: "1"}, timeout=2) + self.assertEqual(status, 504) + owned = [flight["thread"] for flight in link._probe_flights.values()] + def close(): + link.close() + finished.set() + closer = threading.Thread(target=close) + closer.start() + self.assertFalse(finished.wait(.1)) + with self.assertRaises(storage.Refusal): + workbench_link.WorkbenchLink(gateway.origin, self.root / "cleanup-link").start() + gateway.release.set() + self.assertTrue(finished.wait(2)) + closer.join() + self.assertTrue(all(not thread.is_alive() for thread in owned)) + events = [storage.decode(storage.read_bytes(path)) for path in sorted((self.root / "cleanup-link" / "events").glob("*.json"))] + self.assertEqual(events[-1]["kind"], "closed") + self.assertEqual(events[-1]["health_readers_alive"], 0) + self.assertEqual(events[-1]["health_readers_peak"], 1) + frozen = {path.name: path.read_bytes() for path in (self.root / "cleanup-link" / "events").iterdir()} + time.sleep(.05) + self.assertEqual(frozen, {path.name: path.read_bytes() for path in (self.root / "cleanup-link" / "events").iterdir()}) + link = workbench_link.WorkbenchLink(gateway.origin, self.root / "cleanup-link").start() + self.assertEqual(link.origin, origin) + status, _ = body_client.http_call(origin, "GET", "/identity", {PROBE_HEADER: "1"}, timeout=2) + self.assertEqual(status, 200) + self.evidence.append({"joined_readers": len(owned), "restart_origin": origin, "closed": events[-1]}) + finally: + gateway.release.set() + if closer is not None: + closer.join(timeout=3) + link.close() + gateway.close() + + def test_probe_hard_failure_and_absolute_deadline_keep_transport_loss_semantics(self): + gateway = DelayedHealthGateway(self.server.origin, trickle=True) + link = workbench_link.WorkbenchLink(gateway.origin, self.root / "deadline-link").start() + try: + with mock.patch.object(workbench_link, "HEALTH_HARD_SECONDS", .15): + started = time.monotonic() + with self.assertRaises((OSError, http.client.HTTPException)): + body_client.http_call(link.origin, "GET", "/identity", {PROBE_HEADER: "1"}, timeout=2) + self.assertLess(time.monotonic() - started, .6) + link.close() + self.assertFalse(link._probe_flights) + self.evidence.append({"short_owned_hard_deadline_s": .15, "outcome": "transport_loss"}) + finally: + gateway.release.set() + link.close() + gateway.close() + # The production deadline remains thirty seconds, while an already dead body fails promptly. + self.assertEqual(workbench_link.HEALTH_HARD_SECONDS, 30) + self.server.close() + started = time.monotonic() + with self.assertRaises((OSError, http.client.HTTPException)): + body_client.http_call(self.link.origin, "GET", "/identity", {PROBE_HEADER: "1"}, timeout=2) + self.assertLess(time.monotonic() - started, .6) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_workbench_recovery_guards.py b/tools/physical-workcell/test_workbench_recovery_guards.py new file mode 100644 index 0000000..d28dbae --- /dev/null +++ b/tools/physical-workcell/test_workbench_recovery_guards.py @@ -0,0 +1,153 @@ +"""Bounded negative fixtures for audit recovery and planner ownership proofs.""" +import os +import tempfile +import unittest +from pathlib import Path + +import network_operator_runtime as runtime +import order_protocol as protocol +import planner_guard + + +class RecoveryGuardTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory(prefix="sim14-recovery-", dir=protocol.OUTPUT) + self.root = Path(self.temp.name) + (self.root / "events").mkdir(mode=0o700) + self.op = runtime.Operator.__new__(runtime.Operator) + self.op.root = self.root + self.op.m = {"sequence": 0, "activity": []} + + def tearDown(self): + self.temp.cleanup() + + def event(self, seq): + value = {"seq": seq, "time": 1.0, "kind": "rpc_applied", "intent": "audit only", "outcome": "dispatch"} + protocol.create_file(self.root / "events" / f"{seq:06d}.json", protocol.encoded(value)) + return value + + def test_contiguous_audit_tail_preserves_stage_and_applied_identity(self): + first = self.event(1) + self.event(2) + self.op.m.update(sequence=1, activity=[first], stage={"command_id": "original"}, applied_commands=[]) + self.assertEqual(self.op._recover_events(), 1) + self.assertEqual(self.op.m["sequence"], 2) + self.assertEqual(self.op.m["stage"], {"command_id": "original"}) + self.assertEqual(self.op.m["applied_commands"], []) + + def test_missing_event_refuses(self): + self.event(2) + with self.assertRaisesRegex(protocol.Refusal, "contiguous"): + self.op._recover_events() + + def test_malformed_tail_refuses(self): + protocol.create_file(self.root / "events" / "000001.json", b'{"seq":1,"seq":2}') + with self.assertRaises(protocol.Refusal): + self.op._recover_events() + + def test_indexed_activity_conflict_refuses(self): + self.event(1) + self.op.m.update(sequence=1, activity=[]) + with self.assertRaisesRegex(protocol.Refusal, "differs"): + self.op._recover_events() + + def test_missing_indexed_file_refuses(self): + self.op.m["sequence"] = 1 + with self.assertRaisesRegex(protocol.Refusal, "incomplete"): + self.op._recover_events() + + def test_symlink_event_refuses(self): + protocol.create_file(self.root / "target", protocol.encoded({})) + (self.root / "events" / "000001.json").symlink_to(self.root / "target") + with self.assertRaises(protocol.Refusal): + self.op._recover_events() + + def planner(self): + call = self.root / "call" + call.mkdir(mode=0o700) + intent = {"version": 1, "host_pid": os.getpid(), "owner": "owner", "request_sha256": "a" * 64} + protocol.create_file(call / "planner-intent.json", protocol.encoded(intent)) + protocol.create_file(call / "reservation.json", protocol.encoded({"planner_owner": "owner"})) + return call, intent + + def test_before_guardian_launch_has_no_old_writer(self): + call, _ = self.planner() + recovered = planner_guard.recover(call) + self.assertEqual(recovered["reason"], "host exited before planner startup") + self.assertTrue(recovered["group_closed"]) + + def test_guardian_start_without_closure_is_ambiguous(self): + call, intent = self.planner() + protocol.create_file(call / "planner-process.json", protocol.encoded({**intent, "guardian_pid": 123})) + with self.assertRaisesRegex(protocol.Refusal, "without group cleanup"): + planner_guard.recover(call) + + def test_inconsistent_closure_refuses_without_pid_signals(self): + call, intent = self.planner() + base = {k: intent[k] for k in ("host_pid", "owner", "request_sha256")} + startup = {**base, "guardian_pid": os.getpid(), "process_exit_observed": False, "group_closed": False} + protocol.create_file(call / "planner-process.json", protocol.encoded(startup)) + closure = {**startup, "owner": "different", "group_closed": True, "process_exit_observed": True, + "exit_code": 0, "reason": "completed"} + protocol.create_file(call / "planner-closure.json", protocol.encoded(closure)) + with self.assertRaisesRegex(protocol.Refusal, "inconsistent"): + planner_guard.recover(call) + + def test_reservation_owner_conflict_refuses(self): + call, _ = self.planner() + protocol.persist(call / "reservation.json", {"planner_owner": "changed"}) + with self.assertRaisesRegex(protocol.Refusal, "reservation owner"): + planner_guard.recover(call) + + def test_orphan_guardian_artifacts_refuse(self): + call = self.root / "call" + call.mkdir(mode=0o700) + protocol.create_file(call / "planner-process.json", protocol.encoded({"guardian_pid": os.getpid()})) + with self.assertRaisesRegex(protocol.Refusal, "have no intent"): + planner_guard.recover(call) + + def spawned_planner_proof(self): + call, intent = self.planner() + base = {k: intent[k] for k in ("host_pid", "owner", "request_sha256")} + startup = {**base, "guardian_pid": os.getpid(), "process_exit_observed": False, "group_closed": False, + "planner_pid": 12345, "planner_pgid": 12345} + protocol.create_file(call / "planner-process.json", protocol.encoded(startup)) + protocol.create_file(call / "planner-closure.json", protocol.encoded({k: v for k, v in { + **startup, "group_closed": True, "process_exit_observed": True, "reason": "completed", "exit_code": 0}.items() + if k not in {"planner_pid", "planner_pgid"}})) + proof = {**base, "guardian_pid": os.getpid(), "version": 1, "complete": True, + "observed": [{"pid": 12345, "parent": os.getpid(), "group": 12345, "uid": os.getuid(), + "started": "Bounded identity fixture"}], "remaining": []} + return call, proof + + def test_spawned_planner_without_descendant_proof_refuses(self): + call, _ = self.spawned_planner_proof() + with self.assertRaises(protocol.Refusal): + planner_guard.recover(call) + + def test_inconsistent_or_incomplete_descendant_proof_refuses(self): + call, proof = self.spawned_planner_proof() + for change in ({"owner": "other"}, {"complete": False}, {"remaining": [{}]}, {"version": True}, + {"guardian_pid": os.getpid() + 1}, {"observed": [{}]}): + with self.subTest(change=change): + protocol.persist(call / "planner-descendants.json", {**proof, **change}) + with self.assertRaises(protocol.Refusal): + planner_guard.recover(call) + + def test_empty_complete_descendant_proof_refuses(self): + call, proof = self.spawned_planner_proof() + protocol.persist(call / "planner-descendants.json", {**proof, "observed": []}) + with self.assertRaisesRegex(protocol.Refusal, "inconsistent"): + planner_guard.recover(call) + + def test_truncated_or_unbound_leader_descendant_proof_refuses(self): + call, proof = self.spawned_planner_proof() + for change in ({"pid": 54321}, {"group": 54321}, {"parent": os.getpid() + 1}): + with self.subTest(change=change): + protocol.persist(call / "planner-descendants.json", {**proof, "observed": [{**proof["observed"][0], **change}]}) + with self.assertRaisesRegex(protocol.Refusal, "spawned leader"): + planner_guard.recover(call) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_workbench_recovery_trials.py b/tools/physical-workcell/test_workbench_recovery_trials.py new file mode 100644 index 0000000..a6e9800 --- /dev/null +++ b/tools/physical-workcell/test_workbench_recovery_trials.py @@ -0,0 +1,200 @@ +"""Pure diagnostic/scorer controls; no paid model or product actions.""" +import tempfile +import unittest +from pathlib import Path + +import crash_operator as diagnostic +import order_protocol as protocol +import qualify_workbench_recovery as harness +import score_workbench_recovery as scorer + + +class RecoveryTrialTests(unittest.TestCase): + def setUp(self): + self.temp = tempfile.TemporaryDirectory(prefix="sim14-trial-control-", dir=Path(__file__).parent / "output") + self.root = Path(self.temp.name) + self.host = self.root / "operator" + (self.host / "events").mkdir(parents=True) + (self.host / "client/responses").mkdir(parents=True) + self.token = "original-dispatch" + protocol.persist(self.host / "operator.json", {"sequence": 12, "stage": {"operation": "dispatch", "command_id": self.token, "context": {"action": {"action": "move"}}}}) + envelope = {"command_id": self.token, "request_sha256": "a" * 64, "result": {"outcome": "executed"}} + reply = protocol.encoded(envelope) + digest = protocol.sha(reply) + protocol.create_file(self.host / "client/responses" / (self.token + "-" + digest + ".json"), reply) + protocol.persist(self.host / "client/client.json", {"commands": {self.token: {"state": "completed", "request_sha256": "a" * 64, + "response_sha256": digest}}}) + self.event = {"seq": 13, "kind": "rpc_applied", "outcome": "dispatch: " + self.token} + + def tearDown(self): + self.temp.cleanup() + + def gate(self, event=None): + return diagnostic.publication_gate(self.host, 123, self.host / "events/000013.json", protocol.encoded(event or self.event)) + + def test_publication_gate_binds_retained_original_stage_and_durable_event(self): + value = self.gate() + self.assertEqual(value["command_id"], self.token) + self.assertEqual(value["event_sequence"], value["manifest_sequence"] + 1) + self.assertEqual(value["event_sha256"], protocol.sha(protocol.encoded(self.event))) + self.assertEqual(value["manifest_sha256"], protocol.sha(protocol.read_bytes(self.host / "operator.json"))) + + def test_non_dispatch_event_cannot_arm_gate(self): + self.assertIsNone(self.gate({"seq": 13, "kind": "rpc_applied", "outcome": "observe: other"})) + + def test_wrong_command_or_sequence_refuses_diagnostic(self): + for event in ({**self.event, "outcome": "dispatch: another"}, {**self.event, "seq": 14}): + with self.assertRaises((protocol.Refusal, KeyError)): + self.gate(event) + + def test_manual_pid_crashes_are_not_accepted(self): + for action, role in (("crash-role", "workbench"), ("start-role", "body"), ("finish", "workbench")): + with self.assertRaises(protocol.Refusal): + harness.request(self.root, action, role) + + def planner_boundary(self): + (self.root / "snapshots").mkdir() + call = self.host / "calls/job-01" + call.mkdir(parents=True) + closure = {"owner": "incarnation", "guardian_pid": 333, "process_exit_observed": True, "group_closed": True} + protocol.persist(call / "planner-closure.json", closure) + snap = {"client": {"commands": {"old-observe": {"send_count": 1}}}, "posts": ["old-observe"], + "host": {"app_id": "app", "sequence": 4, "planner_owner": "incarnation"}, "body_identity": {"body_id": "body"}, "time": 20, + "public": {"model_request_pending": False, "runner_status": "paused", "recovery": {"command_id": None}}} + protocol.persist(self.root / "snapshots/before.json", snap) + protocol.persist(self.root / "snapshots/restored.json", {**snap, "host": {**snap["host"], "planner_owner": "new-incarnation"}}) + boundary = {"case": "host-planning", "pre_snapshot": "before.json", "restored_snapshot": "restored.json", + "process": {"pid": 123, "returncode": -9, "signal_target": "host_pid_only"}, + "gate": {"pid": 123, "call_directory": "job-01", "planner": {"owner": "incarnation", "guardian_pid": 333}, + "process_identities": {"guardian": {"pid": 333, "group": 333, "started": "old", "command": "guard", "parent": 123}, + "planner": {"pid": 334, "group": 334, "started": "old", "command": "codex", "parent": 333}}}} + boundary["gate"]["captured_descendants"] = list(boundary["gate"]["process_identities"].values()) + boundary["process"]["children_before"] = boundary["gate"]["captured_descendants"] + protocol.persist(self.root / "planner-retirement.json", {"old_processes_absent": True, "old_group_absent": True, + "captured_descendants": boundary["gate"]["captured_descendants"], "captured_groups": [333, 334], + "every_captured_group_absent": True, "captured_group_current_rows": [], + "same_group_current_rows": [], "old_identities": boundary["gate"]["process_identities"], "old_owner": "incarnation", + "new_owner": "new-incarnation", "time": 19, "old_call_files": {"planner-closure.json": protocol.sha(protocol.read_bytes(call / "planner-closure.json"))}}) + transport = {"old-observe": {"relay": {"request": {"started_time": 1}}}, + "new-observe": {"relay": {"request": {"started_time": 31}}, "received": {"sequence": 2}, + "request": {"operation": "observe"}, "result": {"result": {"challenge_id": "fresh"}}}} + events = [{"kind": "resume", "seq": 5, "time": 30}] + return boundary, transport, events, closure + + def test_planner_boundary_requires_closure_before_fresh_explicit_resume(self): + boundary, transport, events, _ = self.planner_boundary() + value = scorer.boundary_score(self.root, boundary, transport, events) + self.assertTrue(value["passed"]) + self.assertEqual(value["fresh_challenge"], "fresh") + with self.assertRaises(protocol.Refusal): + scorer.boundary_score(self.root, boundary, transport, []) + + def test_unclosed_guardian_and_replayed_command_are_refused(self): + boundary, transport, events, closure = self.planner_boundary() + path = self.host / "calls/job-01/planner-closure.json" + protocol.persist(path, {**closure, "group_closed": False}) + with self.assertRaises(protocol.Refusal): + scorer.boundary_score(self.root, boundary, transport, events) + protocol.persist(path, closure) + snap = harness.read(self.root / "snapshots/restored.json") + snap["client"]["commands"]["old-observe"]["send_count"] = 2 + protocol.persist(self.root / "snapshots/restored.json", snap) + with self.assertRaises(protocol.Refusal): + scorer.boundary_score(self.root, boundary, transport, events) + + def test_process_absence_uses_start_identity_and_checks_whole_group(self): + old = {"pid": 334, "group": 334, "started": "old", "command": "codex", "parent": 333} + reused = {**old, "started": "new", "group": 900} + evidence = harness.absence_evidence([reused], [old], 334) + self.assertTrue(evidence["old_processes_absent"]) + self.assertTrue(evidence["old_group_absent"]) + self.assertEqual(evidence["same_pid_current_rows"], [reused]) + remaining = {**old, "pid": 777} + self.assertFalse(harness.absence_evidence([remaining], [old], 334)["old_group_absent"]) + + def test_changed_retired_call_files_or_live_group_fail_independent_scoring(self): + boundary, transport, events, _ = self.planner_boundary() + path = self.root / "planner-retirement.json" + proof = harness.read(path) + protocol.persist(path, {**proof, "every_captured_group_absent": False}) + with self.assertRaises(protocol.Refusal): + scorer.boundary_score(self.root, boundary, transport, events) + protocol.persist(path, proof) + protocol.persist(self.host / "calls/job-01/planner-closure.json", {"changed": True}) + with self.assertRaises((protocol.Refusal, KeyError)): + scorer.boundary_score(self.root, boundary, transport, events) + + def test_reparented_detached_writer_cannot_be_inferred_to_have_exited(self): + old = {"pid": 700, "uid": 501, "parent": 334, "group": 701, "started": "old", "command": "snapshot-writer"} + escaped = {**old, "parent": 1, "group": 900, "command": "another-executable-after-exec"} + evidence = harness.captured_absence([escaped], [old]) + self.assertFalse(evidence["old_processes_absent"]) + self.assertTrue(evidence["every_captured_group_absent"]) + self.assertEqual(evidence["same_pid_current_rows"], [escaped]) + + def test_merge_retains_every_group_across_sets_id_or_exec(self): + old = {"pid": 700, "uid": 501, "parent": 334, "group": 701, "started": "old", "command": "snapshot-writer"} + later = {**old, "parent": 1, "group": 900, "command": "changed-by-exec"} + captured = harness.merge_captures([old], [later]) + self.assertEqual({row["group"] for row in captured}, {701, 900}) + evidence = harness.captured_absence([{**old, "pid": 702}], captured) + self.assertFalse(evidence["every_captured_group_absent"]) + self.assertEqual(evidence["captured_groups"], [701, 900]) + + def test_descendant_capture_and_retirement_covers_secondary_groups(self): + guardian = {"pid": 333, "parent": 123, "group": 333, "started": "old", "command": "guard"} + planner = {"pid": 334, "parent": 333, "group": 334, "started": "old", "command": "codex"} + detached = {"pid": 700, "parent": 334, "group": 700, "started": "old", "command": "zsh"} + captured = harness.captured_descendants([guardian, planner, detached], 123) + self.assertEqual(captured, [guardian, planner, detached]) + evidence = harness.captured_absence([detached], captured) + self.assertFalse(evidence["every_captured_group_absent"]) + self.assertEqual(evidence["captured_groups"], [333, 334, 700]) + + def test_guardian_descendant_proof_requires_bound_completion_and_snapshot_disabled(self): + call = self.host / "calls/descendant-control" + call.mkdir(parents=True) + binding = {"host_pid": 123, "owner": "old-owner", "request_sha256": "a" * 64} + protocol.persist(call / "planner-intent.json", binding) + protocol.persist(call / "planner-process.json", {**binding, "guardian_pid": 333, "planner_pid": 334}) + protocol.persist(call / "planner-closure.json", {**binding, "guardian_pid": 333}) + proof = {**binding, "guardian_pid": 333, "version": 1, "complete": True, "remaining": [], + "observed": [{"pid": 334, "uid": 501, "parent": 333, "group": 334, "started": "old"}]} + protocol.persist(call / "planner-descendants.json", proof) + protocol.persist(call / "request.json", {"flags": ["--disable", "shell_snapshot"]}) + self.assertEqual(scorer.descendant_score(call), proof) + protocol.persist(call / "planner-descendants.json", {**proof, "complete": False}) + with self.assertRaises(protocol.Refusal): + scorer.descendant_score(call) + protocol.persist(call / "planner-descendants.json", proof) + protocol.persist(call / "request.json", {"flags": ["--disable", "shell_tool"]}) + with self.assertRaises(protocol.Refusal): + scorer.descendant_score(call) + + def test_receipt_recovery_keeps_original_token_while_clearing_pending(self): + boundary, _, _, _ = self.planner_boundary() + boundary.update(case="host-publication", command_id=self.token) + paused = {"runner_status": "paused", "model_request_pending": False, + "recovery": {"state": "uncertain", "command_id": self.token}} + steps = [{"name": "crash-offline", "time": 21, "offline": True, "state": None, "visible_text": "Disconnected"}, + {"name": "restored", "time": 22, "offline": False, "state": paused, "visible_text": "Uncertain paused", "client_commands": 4}, + {"name": "recovered", "time": 23, "offline": False, "state": {**paused, "recovery": {"state": "recovered", "command_id": self.token}}, + "visible_text": "Receipt recovered", "client_commands": 4, "pending_command_ids": []}, + {"name": "resumed", "time": 24, "offline": False, "state": {"runner_status": "running"}, "visible_text": "Running"}, + {"name": "completed", "time": 25, "offline": False, "state": {"jobs": [{"status": "completed"}]}, "visible_text": "Completed"}] + protocol.persist(self.root / "ui-ledger.json", {"steps": steps}) + self.assertTrue(scorer.ui_score(self.root, boundary)["passed"]) + steps[2]["client_commands"] += 1 + protocol.persist(self.root / "ui-ledger.json", {"steps": steps}) + with self.assertRaises(protocol.Refusal): + scorer.ui_score(self.root, boundary) + + def test_reuses_exact_prior_wire_relay_and_indexed_command(self): + import qualify_workbench_crashes as previous + self.assertIs(harness.HeldReplyRelay, previous.HeldReplyRelay) + self.assertIs(harness.indexed_command, previous.indexed_command) + self.assertIs(harness.descendants, previous.descendants) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_workbench_restart_harness.py b/tools/physical-workcell/test_workbench_restart_harness.py new file mode 100644 index 0000000..ec90298 --- /dev/null +++ b/tools/physical-workcell/test_workbench_restart_harness.py @@ -0,0 +1,73 @@ +"""Focused harness guards; disposable owned processes, no model or body launches.""" +import copy +import subprocess +import sys +import unittest +import uuid +from pathlib import Path + +import order_protocol as protocol +import qualify_workbench_restarts as harness + +PUBLIC = {"app_id": "app", "connection": {"body_id": "body"}, "runner_status": "running", + "model_request_pending": True, "active_job_id": "job", "usage": {"calls": 1}, + "recovery": {"command_id": None}} + + +class RestartHarnessGuards(unittest.TestCase): + def test_exact_planning_and_uncertain_predicates(self): + self.assertTrue(harness.matches(PUBLIC, "planning")) + self.assertFalse(harness.matches(PUBLIC, "uncertain")) + uncertain = copy.deepcopy(PUBLIC) + uncertain.update(runner_status="blocked", model_request_pending=False) + uncertain["recovery"]["command_id"] = "command" + self.assertTrue(harness.matches(uncertain, "uncertain")) + self.assertFalse(harness.matches(uncertain, "planning")) + + def test_malformed_controls_refuse_before_snapshots_or_signals(self): + control = {"sequence": 1, "action": "stop-role", "role": "workbench", "predicate": "planning", + "expect": harness.expected(PUBLIC), "requested_time": 1} + self.assertEqual(harness.validate_control(control), control) + for key, value in (("sequence", True), ("sequence", -1), ("action", "kill"), ("role", "other"), + ("predicate", "none"), ("expect", None)): + malformed = {**control, key: value} + with self.assertRaises(protocol.Refusal): + harness.validate_control(malformed) + bad = copy.deepcopy(control) + bad["expect"]["model_request_pending"] = 1 + with self.assertRaises(protocol.Refusal): + harness.validate_control(bad) + + def test_stop_only_signals_registered_owned_handle_and_reaps_it(self): + process = subprocess.Popen([sys.executable, "-c", "import time; time.sleep(30)"]) + try: + trial = object.__new__(harness.Trial) + trial.active, trial.processes, trial.ledger = {"body": process}, [], {} + with self.assertRaises(protocol.Refusal): + trial.stop("body") + self.assertIsNone(process.poll()) + record = {"role": "body", "pid": process.pid} + trial.processes.append((process, record)) + stopped = trial.stop("body") + self.assertTrue(stopped["exit_observed"]) + self.assertIsNone(trial.active["body"]) + with self.assertRaises(protocol.Refusal): + trial.stop("body") + finally: + if process.poll() is None: + process.terminate() + process.wait(timeout=3) + + def test_snapshot_reader_keeps_128k_journal_and_wire_limit_separate(self): + directory = Path(__file__).resolve().parent / "output" / ("sim11-reader-" + uuid.uuid4().hex[:12]) + directory.mkdir(mode=0o700) + path = directory / "journal.json" + path.write_bytes(protocol.encoded({"payload": "x" * 70000})) + path.chmod(0o600) + self.assertEqual(len(harness.read(path)["payload"]), 70000) + with self.assertRaises(protocol.Refusal): + protocol.decode(path.read_bytes()) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/test_workcell.py b/tools/physical-workcell/test_workcell.py new file mode 100644 index 0000000..de1e52a --- /dev/null +++ b/tools/physical-workcell/test_workcell.py @@ -0,0 +1,219 @@ +"""Conformance on the actual pinned native physics engine; no hardware or camera.""" + +import importlib.util +import json +import math +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +BASE = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("physical_workcell", BASE / "workcell.py") +cell = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(cell) + + +class WorkcellTests(unittest.TestCase): + def setUp(self): + cell.OUTPUT.mkdir(mode=0o700, exist_ok=True) + self.tmp = tempfile.TemporaryDirectory(prefix="conformance-", dir=cell.OUTPUT) + self.addCleanup(self.tmp.cleanup) + self.work = Path(self.tmp.name) + self.root = cell.directory(self.work / "run", create=True) + self.order = {"version": 1, "order_id": "fixture-order", "project": {"namespace": "codex_app", "project_id": "fixture"}, + "part_id": "block", "target_zone": {"x_min": .18, "x_max": .26, "y_min": -.08, "y_max": .08}} + self.order_path = self.work / "input.json" + self.content = cell.encoded(self.order) + cell.create_file(self.order_path, self.content) + self.sha = cell.sha(self.content) + self.before = cell.initialize(self.root, str(self.order_path), self.sha) + + def cli(self, *args): + return subprocess.run([sys.executable, str(BASE / "workcell.py"), *args], + capture_output=True, text=True, check=False, timeout=30) + + def push(self): + return cell.act(self.root, "push", 0, .15, 0) + + def test_before_condition_false_and_completion_creates_nothing(self): + self.assertEqual(cell.mujoco.__version__, "3.15.0") + self.assertEqual(self.before["source_kind"], "mujoco_simulation") + self.assertFalse(self.before["physical_condition_verified"]) + with self.assertRaises(cell.Refusal): + cell.complete(self.root, "stage") + self.assertFalse((self.root / "order.json").exists()) + self.assertFalse(cell.op_path(self.root, "stage").exists()) + + def test_real_contact_changes_block_and_enables_independent_computer_result(self): + result = self.push() + after = result["observation"] + self.assertEqual(result["operation"]["phase"], "executed") + self.assertGreater(after["block_xyz"][0] - self.before["block_xyz"][0], .25) + self.assertTrue(after["physical_condition_verified"]) + evidence = result["operation"]["controller_evidence"] + self.assertGreater(evidence["pusher_block_contact_samples"], 0) + self.assertLessEqual(evidence["max_axis_actuator_force_n"], 4) + self.assertGreater(evidence["native_steps"], 0) + done = cell.complete(self.root, "stage") + self.assertEqual(done["operation"]["phase"], "staged") + self.assertTrue(done["observation"]["current_software_artifact_check"]["ok"]) + order = json.loads((self.root / "order.json").read_bytes()) + self.assertEqual(order["state"], "staged") + self.assertEqual(order["source_kind"], "mujoco_simulation") + self.assertEqual(order["simulator_state_sha256"], after["state_sha256"]) + self.assertTrue(order["physical_predicate"]["block_center_in_zone"]) + + def test_noncontact_command_does_not_fulfill_order(self): + result = cell.act(self.root, "short", 0, -.25, 0) + self.assertEqual(result["operation"]["phase"], "executed") + self.assertEqual(result["operation"]["controller_evidence"]["pusher_block_contact_samples"], 0) + self.assertFalse(result["observation"]["physical_condition_verified"]) + with self.assertRaises(cell.Refusal): + cell.complete(self.root, "stage") + + def test_fresh_process_continuity_repeat_and_one_digital_result(self): + args = ("act", "--run-dir", str(self.root), "--token", "push", "--expected-generation", "0", + "--target-x", "0.15", "--target-y", "0") + result = self.cli(*args) + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + after = json.loads(result.stdout)["observation"] + observed = self.cli("observe", "--run-dir", str(self.root)) + self.assertEqual(observed.returncode, 0, observed.stdout + observed.stderr) + self.assertEqual(json.loads(observed.stdout)["state_sha256"], after["state_sha256"]) + self.assertEqual(json.loads(observed.stdout)["block_xyz"], after["block_xyz"]) + before_repeat = {p.name: p.read_bytes() for p in self.root.iterdir() if p.name != "lock"} + repeat = self.cli(*args) + self.assertEqual(repeat.returncode, 0, repeat.stdout + repeat.stderr) + self.assertEqual(json.loads(repeat.stdout)["observation"]["simulation_time_s"], after["simulation_time_s"]) + self.assertEqual(before_repeat, {p.name: p.read_bytes() for p in self.root.iterdir() if p.name != "lock"}) + complete_args = ("complete", "--run-dir", str(self.root), "--token", "stage") + self.assertEqual(self.cli(*complete_args).returncode, 0) + before_repeat = {p.name: p.read_bytes() for p in self.root.iterdir() if p.name != "lock"} + self.assertEqual(self.cli(*complete_args).returncode, 0) + self.assertEqual(before_repeat, {p.name: p.read_bytes() for p in self.root.iterdir() if p.name != "lock"}) + self.assertEqual(len(list(self.root.glob("order.json"))), 1) + + def test_token_arguments_stale_generation_and_workspace_refuse_before_step(self): + self.push() + before = cell.observation(self.root) + for args in (("push", 0, .14, 0), ("fresh", 0, .15, 0), ("fresh", 1, .9, 0), + ("fresh", 1, .15, -.3), ("fresh", 1, math.nan, 0), ("fresh", 1, math.inf, 0)): + with self.subTest(args=args), self.assertRaises(cell.Refusal): + cell.act(self.root, *args) + self.assertEqual(cell.observation(self.root)["state_sha256"], before["state_sha256"]) + self.assertFalse(cell.op_path(self.root, "fresh").exists()) + + def test_lost_motion_receipt_blocks_replay_and_completion(self): + original = cell.utils.persist + + def lose(path, record): + if path.name.startswith("op-") and record.get("phase") == "executed": + raise OSError("injected lost motion receipt") + original(path, record) + + with patch.object(cell.utils, "persist", side_effect=lose): + result = self.push() + self.assertEqual(result["operation"]["phase"], "unknown") + self.assertEqual(result["observation"]["claims"], []) + self.assertFalse(result["observation"]["physical_condition_verified"]) + with patch.object(cell, "motion") as motion: + again = self.push() + motion.assert_not_called() + self.assertEqual(again["operation"]["phase"], "unknown") + with self.assertRaises(cell.Refusal): + cell.act(self.root, "another", 1, .1, 0) + with self.assertRaises(cell.Refusal): + cell.complete(self.root, "stage") + + def test_interrupted_state_publication_does_not_implicitly_reset_or_reexecute(self): + with patch.object(cell, "save_state", side_effect=OSError("injected state write failure")): + result = self.push() + self.assertEqual(result["observation"]["phase"], "unknown") + self.assertEqual(result["observation"]["state_authority"], "retained_unresolved_operation") + self.assertEqual(result["observation"]["simulation_time_s"], self.before["simulation_time_s"]) + self.assertFalse(result["observation"]["physical_condition_verified"]) + with patch.object(cell, "motion") as motion: + self.push() + motion.assert_not_called() + + def test_lost_software_receipt_never_promotes_valid_bytes_or_replays(self): + self.push() + original = cell.utils.persist + + def lose(path, record): + if path.name.startswith("op-") and record.get("phase") == "staged": + raise OSError("injected lost staging receipt") + original(path, record) + + with patch.object(cell.utils, "persist", side_effect=lose): + result = cell.complete(self.root, "stage") + self.assertEqual(result["operation"]["phase"], "unknown") + self.assertTrue((self.root / "order.json").exists()) + before = (self.root / "order.json").stat().st_mtime_ns + self.assertEqual(cell.complete(self.root, "stage")["operation"]["phase"], "unknown") + self.assertEqual((self.root / "order.json").stat().st_mtime_ns, before) + + def test_tampered_or_missing_result_preserves_history_without_recreation(self): + self.push() + cell.complete(self.root, "stage") + path = self.root / "order.json" + path.write_bytes(b"changed") + observed = cell.observation(self.root) + self.assertEqual(observed["phase"], "unknown") + self.assertEqual(observed["historical_software_outcome"], "staged") + self.assertFalse(observed["current_software_artifact_check"]["ok"]) + path.unlink() + repeat = cell.complete(self.root, "stage") + self.assertEqual(repeat["operation"]["historical_software_outcome"], "staged") + self.assertEqual(repeat["observation"]["phase"], "unknown") + self.assertFalse(path.exists()) + with self.assertRaises(cell.Refusal): + cell.complete(self.root, "another-completion") + + def test_model_state_order_and_runtime_tamper_refuse_implicit_reset(self): + files = (self.root / "model.xml", self.root / "work-order.json", cell.state_path(self.root, 0), self.order_path) + for path in files: + with self.subTest(path=path): + before = path.read_bytes() + path.write_bytes(before + b" ") + with self.assertRaises(cell.Refusal): + cell.observation(self.root) + path.write_bytes(before) + with patch.object(cell.mujoco, "__version__", "different"), self.assertRaises(cell.Refusal): + cell.observation(self.root) + self.assertEqual(cell.observation(self.root)["generation"], 0) + + def test_init_lookup_is_repeat_safe_and_changed_order_refused(self): + self.push() + observed = cell.initialize(self.root, str(self.order_path), self.sha) + self.assertEqual(observed["generation"], 1) + self.assertTrue(observed["block_center_in_zone"]) + changed = dict(self.order, order_id="different") + content = cell.encoded(changed) + self.order_path.write_bytes(content) + with self.assertRaises(cell.Refusal): + cell.initialize(self.root, str(self.order_path), cell.sha(content)) + + def test_malformed_and_nonfinite_inputs_fail_without_traceback(self): + with self.assertRaises(cell.Refusal): + cell.decoded(b'{"value":NaN}') + with self.assertRaises(cell.Refusal): + cell.decoded(b'{"version":1,"version":1}') + result = self.cli("act", "--run-dir", str(self.root), "--token", "bad", "--expected-generation", "0", + "--target-x", "nan", "--target-y", "0") + self.assertEqual(result.returncode, 2) + self.assertEqual(json.loads(result.stdout)["phase"], "refused") + self.assertNotIn("Traceback", result.stderr) + + def test_cross_process_lock_prevents_second_owner(self): + with cell.utils.locked(self.root): + result = self.cli("observe", "--run-dir", str(self.root)) + self.assertEqual(result.returncode, 2) + self.assertIn("another process", result.stdout) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/physical-workcell/workbench_link.py b/tools/physical-workcell/workbench_link.py new file mode 100644 index 0000000..3cd0f08 --- /dev/null +++ b/tools/physical-workcell/workbench_link.py @@ -0,0 +1,392 @@ +"""Retained loopback workbench link; reconnect and restart never replay requests. + +Command payload bytes and identity headers pass unchanged. One-shot faults are +local diagnostics. Ordinary read-only identity/state polling is not journaled. +Tagged auxiliary reads are single flight per path; late replies are discarded. +""" +import concurrent.futures +import contextlib +import copy +import fcntl +import http.client +import os +import socket +import stat +import threading +import time +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from urllib.parse import urlsplit + +import body_client +import order_protocol as protocol + +MODES = {"drop_after", "delay_after", "duplicate"} +MAX_EVENTS = 16384 +HEALTH_HEADER = "X-Footwork-Health-Probe" +HEALTH_SOFT_SECONDS = .6 +HEALTH_HARD_SECONDS = 30 + + +class WorkbenchLink: + def __init__(self, origin, root, port=0): + body_client.endpoint(origin) + if type(port) is not int or not 0 <= port <= 65535: + raise protocol.Refusal("link port must be a bounded integer") + self.upstream = origin + self.root = protocol.directory(root, create=True) + self.port = port + self.origin = None + self._mutex = threading.RLock() + self._manifest = None + self._server = self._thread = self._fd = None + self._probe_flights = {} + self._probe_closing = False + self._probe_peak = 0 + + def _probe_read(self, path, headers, flight): + """Owned read-only socket, with an absolute deadline even for trickled replies.""" + host, port = body_client.endpoint(self.upstream) + connection = http.client.HTTPConnection(host, port, timeout=HEALTH_HARD_SECONDS) + socket_lock, expired = threading.Lock(), threading.Event() + owned_socket = [None] + + def expire(): + expired.set() + with socket_lock: + transport = owned_socket[0] + if transport is not None: + with contextlib.suppress(OSError): + transport.shutdown(socket.SHUT_RDWR) + + timer = threading.Timer(max(0, flight["hard_deadline"] - time.monotonic()), expire) + timer.start() + try: + connection.connect() + with socket_lock: + owned_socket[0] = connection.sock + if expired.is_set(): + raise TimeoutError("auxiliary body read exceeded its hard deadline") + connection.request("GET", path, headers=headers) + response = connection.getresponse() + length = response.getheader("Content-Length") + if length is not None and (not length.isdigit() or int(length) > protocol.MAX_BYTES): + raise protocol.Refusal("body response exceeds its bounded size") + content = response.read(protocol.MAX_BYTES + 1) + if expired.is_set(): + raise TimeoutError("auxiliary body read exceeded its hard deadline") + if len(content) > protocol.MAX_BYTES: + raise protocol.Refusal("body response exceeds its bounded size") + if 300 <= response.status < 400: + raise protocol.Refusal("body redirects are refused") + flight["reply"] = response.status, content + except (OSError, http.client.HTTPException, protocol.Refusal, KeyError, TypeError, ValueError) as exc: + flight["error"] = exc + finally: + connection.close() + timer.cancel() + timer.join() + flight["finished_at"] = time.monotonic() + flight["done"].set() + # No journal writes or response reuse after an auxiliary caller expires. + + def _probe(self, path, headers): + pending = (504, protocol.encoded({"version": 1, "kind": "health_read_pending", "path": path})) + deadline = time.monotonic() + HEALTH_SOFT_SECONDS + with self._mutex: + if self._probe_closing: + raise protocol.Refusal("link is closing") + previous = self._probe_flights.get(path) + if previous is not None: + if previous["thread"].is_alive(): + return pending + del self._probe_flights[path] + flight = {"done": threading.Event(), "hard_deadline": time.monotonic() + HEALTH_HARD_SECONDS} + thread = threading.Thread(target=self._probe_read, args=(path, dict(headers), flight), + name="workbench-health-" + path.removeprefix("/")) + flight["thread"] = thread + self._probe_flights[path] = flight + thread.start() + self._probe_peak = max(self._probe_peak, sum(item["thread"].is_alive() for item in self._probe_flights.values())) + if not flight["done"].wait(max(0, deadline - time.monotonic())) or flight["finished_at"] > deadline: + return pending + if "error" in flight: + raise flight["error"] + return flight["reply"] + + def _public(self, manifest=None): + m = self._manifest if manifest is None else manifest + return {key: copy.deepcopy(m[key]) for key in ("connected", "origin", "upstream", "fault")} + + def state(self): + with self._mutex: + if self._manifest is None: + raise protocol.Refusal("link has not started") + return self._public() + + def _event(self, kind, _changes=None, **data): + with self._mutex: + m = {**self._manifest, **(_changes or {})} + if m["sequence"] >= MAX_EVENTS: + raise protocol.Refusal("link event budget exhausted") + sequence = m["sequence"] + 1 + event = {"sequence": sequence, "kind": kind, "previous_sha256": m["event_sha256"], + "state_snapshot": self._public(m), **data} + content = protocol.encoded(event) + protocol.create_file(self.root / "events" / f"{sequence:06d}.json", content) + updated = {**m, "sequence": sequence, "event_sha256": protocol.sha(content)} + protocol.persist(self.root / "link.json", updated) + self._manifest = updated + return event + + def _restore(self): + m = protocol.decode(protocol.read_bytes(self.root / "link.json")) + protocol.fields(m, ("version", "origin", "upstream", "connected", "fault", "sequence", "event_sha256", "source_sha256"), + "retained link") + source_sha = protocol.sha(protocol.read_bytes(__file__)) + if (type(m["version"]) is not int or m["version"] != 1 or m["upstream"] != self.upstream or + m["source_sha256"] != source_sha or type(m["connected"]) is not bool or + type(m["sequence"]) is not int or not 1 <= m["sequence"] <= MAX_EVENTS): + raise protocol.Refusal("retained link identity, source or state changed") + _, port = body_client.endpoint(m["origin"]) + if self.port not in (0, port): + raise protocol.Refusal("retained link must use its exact original port") + previous, tail, payloads = None, None, set() + for sequence in range(1, m["sequence"] + 1): + content = protocol.read_bytes(self.root / "events" / f"{sequence:06d}.json") + event = protocol.decode(content) + if event.get("sequence") != sequence or event.get("previous_sha256") != previous: + raise protocol.Refusal("link event history changed") + if event["kind"] == "received": + payload_path = self.root / "requests" / f"{sequence:06d}.bin" + if protocol.sha(protocol.read_bytes(payload_path)) != event["payload_sha256"]: + raise protocol.Refusal("link request payload history changed") + payloads.add(payload_path.name) + previous, tail = protocol.sha(content), event + if (previous != m["event_sha256"] or tail["state_snapshot"] != {key: m[key] for key in + ("connected", "origin", "upstream", "fault")} or + len(list((self.root / "events").iterdir())) != m["sequence"] or + {path.name for path in (self.root / "requests").iterdir()} != payloads): + raise protocol.Refusal("retained link manifest or immutable journal changed") + if m["fault"] is not None: + self._validate_fault(**m["fault"]) + self._manifest, self.origin = m, m["origin"] + return port + + def start(self): + if self._server is not None: + raise protocol.Refusal("link already started") + fd = os.open(self.root / "link.lock", os.O_RDWR | os.O_CREAT | os.O_NOFOLLOW, 0o600) + server = None + try: + info = os.fstat(fd) + if not stat.S_ISREG(info.st_mode) or info.st_uid != os.getuid() or info.st_mode & 0o077: + raise protocol.Refusal("link lock must be an owned private regular file") + try: + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError as exc: + raise protocol.Refusal("another process owns this link") from exc + retained = (self.root / "link.json").exists() + if retained: + port = self._restore() + else: + if any(path.name != "link.lock" for path in self.root.iterdir()): + raise protocol.Refusal("new link requires an empty owned directory") + (self.root / "events").mkdir(mode=0o700) + (self.root / "requests").mkdir(mode=0o700) + port = self.port + owner = self + + class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def log_message(self, *_): + pass + + def do_GET(self): + self.handle_request() + + def do_POST(self): + self.handle_request() + + def drop(self): + self.close_connection = True + with contextlib.suppress(OSError): + self.connection.shutdown(socket.SHUT_RDWR) + self.connection.close() + + def handle_request(self): + self.connection.settimeout(30) + try: + payload, operation, command_id, headers, audit, probe = self.validate_request() + with owner._mutex: + if not owner._manifest["connected"]: + if audit: + owner._event("disconnected_request", method=self.command, path=self.path, + operation=operation, command_id=command_id, + payload_sha256=protocol.sha(payload)) + self.drop() + return + fault = owner._manifest["fault"] + fault = copy.deepcopy(fault) if fault and fault["operation"] == operation else None + if audit: + sequence = owner._manifest["sequence"] + 1 + protocol.create_file(owner.root / "requests" / f"{sequence:06d}.bin", payload) + owner._event("received", _changes={"fault": None} if fault else None, + operation=operation, command_id=command_id, method=self.command, path=self.path, payload_sha256=protocol.sha(payload), fault=fault) + mode = fault["mode"] if fault else None + + def forward(copy_index): + if audit: + owner._event("forwarded", command_id=command_id, copy_index=copy_index, + payload_sha256=protocol.sha(payload)) + status, content = body_client.http_call(owner.upstream, self.command, self.path, headers, + payload if self.command == "POST" else None, + timeout=30, maximum=body_client.MAX_IMAGE) + if audit: + owner._event("upstream_reply", command_id=command_id, copy_index=copy_index, + http_status=status, response_sha256=protocol.sha(content)) + return status, content + + if probe: + status, content = owner._probe(self.path, headers) + elif mode == "duplicate": + with concurrent.futures.ThreadPoolExecutor(max_workers=2) as pool: + replies = [pool.submit(forward, index) for index in (1, 2)] + responses = [reply.result() for reply in replies] + status, content = responses[0] + owner._event("duplicate_replies", command_id=command_id, same_reply=responses[0] == responses[1]) + else: + status, content = forward(1) + if mode == "delay_after": + owner._event("delay_after", command_id=command_id, delay_s=fault["delay_s"]) + time.sleep(fault["delay_s"]) + if mode == "drop_after": + owner._event("dropped_after", command_id=command_id) + self.drop() + return + self.send_response(status) + self.send_header("Content-Type", "image/png" if self.path.startswith("/images/") else "application/json") + self.send_header("Content-Length", str(len(content))) + self.send_header("Connection", "close") + self.close_connection = True + self.end_headers() + self.wfile.write(content) + if audit: + owner._event("reply_sent", command_id=command_id, http_status=status) + except (OSError, http.client.HTTPException, protocol.Refusal, KeyError, TypeError, ValueError) as exc: + with contextlib.suppress(Exception): + owner._event("transport_failure", reason=str(exc)) + self.drop() + + def validate_request(self): + parsed = urlsplit(self.path) + if (self.headers.get_all("Host") != [owner.origin.removeprefix("http://")] or + self.headers.get("Origin") is not None or self.headers.get("Transfer-Encoding") is not None or + parsed.scheme or parsed.netloc or parsed.fragment or not self.path.startswith("/")): + raise protocol.Refusal("link requires exact loopback Host and no browser Origin") + read_route = parsed.path in {"/identity", "/state"} or parsed.path.startswith(("/commands/", "/images/")) + if (self.command == "GET" and not read_route) or (self.command == "POST" and self.path not in {"/lease", "/commands"}): + raise protocol.Refusal("unsupported link route") + probes = self.headers.get_all(HEALTH_HEADER, []) + if probes and (probes != ["1"] or self.command != "GET" or self.path not in {"/identity", "/state"}): + raise protocol.Refusal("auxiliary health annotation requires one exact read-only route") + for name in ("X-Body-Id", "X-Adapter-Id", "X-Source-Sha256"): + values = self.headers.get_all(name, []) + if len(values) > 1 or (parsed.path != "/identity" and len(values) != 1): + raise protocol.Refusal("one exact body identity header is required") + lengths = self.headers.get_all("Content-Length", []) + if len(lengths) > 1 or (lengths and (not lengths[0].isascii() or not lengths[0].isdigit() or + int(lengths[0]) > protocol.MAX_BYTES)): + raise protocol.Refusal("link payload exceeds its bounded size") + length = int(lengths[0]) if lengths else 0 + if self.command == "POST" and (length < 1 or self.headers.get_all("Content-Type") != ["application/json"]): + raise protocol.Refusal("link requires one exact bounded JSON body") + if self.command == "GET" and length: + raise protocol.Refusal("link read requests cannot have a payload") + payload = self.rfile.read(length) + if len(payload) != length: + raise protocol.Refusal("link payload is truncated") + operation = command_id = None + if self.command == "POST": + decoded = protocol.decode(payload) + if not isinstance(decoded, dict): + raise protocol.Refusal("link command payload must be an object") + if self.path == "/commands": + operation, command_id = decoded.get("operation"), decoded.get("command_id") + protocol.identity(operation) + protocol.identity(command_id) + else: + operation = "lease" + headers = {key: value for key, value in self.headers.items() if key.lower() not in + {"host", "connection", "content-length", "transfer-encoding"}} + headers["Content-Length"] = str(length) + return payload, operation, command_id, headers, parsed.path not in {"/identity", "/state"}, bool(probes) + + server = ThreadingHTTPServer(("127.0.0.1", port), Handler) + server.daemon_threads = False + if not retained: + self.origin = f"http://127.0.0.1:{server.server_port}" + self._manifest = {"version": 1, "origin": self.origin, "upstream": self.upstream, "connected": True, + "fault": None, "sequence": 0, "event_sha256": None, + "source_sha256": protocol.sha(protocol.read_bytes(__file__))} + elif self._manifest["fault"] is not None: + cancelled = self._manifest["fault"] + self._event("fault_cancelled_on_restart", _changes={"fault": None}, fault=cancelled) + self._event("started", origin=self.origin, upstream=self.upstream, restarted=retained) + self._server, self._fd = server, fd + self._probe_closing = False + self._thread = threading.Thread(target=server.serve_forever, kwargs={"poll_interval": .1}, daemon=True) + self._thread.start() + return self + except Exception: + if server is not None: + server.server_close() + os.close(fd) + raise + + def set_connected(self, connected): + if type(connected) is not bool: + raise protocol.Refusal("connected state must be boolean") + with self._mutex: + if self._server is None: + raise protocol.Refusal("link is not running") + previous = self._manifest["connected"] + self._event("connected" if connected else "disconnected", _changes={"connected": connected}, previous_connected=previous, + replayed_requests=0) + return self._public() + + @staticmethod + def _validate_fault(mode, operation="dispatch", delay_s=.2): + if mode not in MODES or operation not in {"start_job", "observe", "propose", "dispatch", "disturb", "lease"}: + raise protocol.Refusal("unsupported workbench link diagnostic fault") + delay = protocol.finite(delay_s) + if not 0 <= delay <= 2: + raise protocol.Refusal("link delay exceeds its two-second budget") + return {"mode": mode, "operation": operation, "delay_s": delay} + + def arm(self, mode, operation="dispatch", delay_s=.2): + fault = self._validate_fault(mode, operation, delay_s) + with self._mutex: + if self._server is None or self._manifest["fault"] is not None: + raise protocol.Refusal("link must be running with no outstanding fault") + self._event("armed", _changes={"fault": fault}, **fault) + return self._public() + + def close(self): + if self._server is not None: + with self._mutex: + self._probe_closing = True + self._server.shutdown() + try: + self._server.server_close() + self._thread.join(timeout=2) + # Tagged callers finish at the soft bound; their upstream reads remain owned. + # Each worker's absolute hard deadline closes its socket before this join returns. + for flight in self._probe_flights.values(): + flight["thread"].join() + self._probe_flights.clear() + self._event("closed", health_readers_alive=0, health_readers_peak=self._probe_peak) + finally: + self._server = None + os.close(self._fd) + self._fd = None diff --git a/tools/physical-workcell/workcell.py b/tools/physical-workcell/workcell.py new file mode 100644 index 0000000..29a1035 --- /dev/null +++ b/tools/physical-workcell/workcell.py @@ -0,0 +1,634 @@ +"""Native MuJoCo body, bounded contact actions, and conditional local work-order result.""" + +import argparse +import contextlib +import hashlib +import importlib.util +import json +import math +import os +import re +import stat +import struct +import sys +import time +import zlib +from pathlib import Path + +import mujoco +import numpy as np + +BASE = Path(__file__).resolve().parent +OUTPUT = BASE / "output" +SCENE = BASE / "scene.xml" +FOOTWORK = BASE.parents[1] / "jevdual" / ".venv" / "bin" / "footwork" +SPEC = importlib.util.spec_from_file_location("camera_utils", BASE.parent / "camera-capture-experiment" / "capture.py") +utils = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(utils) +CASE_SPEC = importlib.util.spec_from_file_location("episode_case_contract", BASE / "episode_cases.py") +cases = importlib.util.module_from_spec(CASE_SPEC) +CASE_SPEC.loader.exec_module(cases) +Refusal = utils.Refusal +PINNED_VERSION = "3.15.0" +SOURCE_KIND = "mujoco_simulation" +MAX_FILE = 1024 * 1024 +CONTROLLER = {"timestep_s": 0.002, "speed_m_s": 0.15, "settle_s": 1.0, + "force_limit_n_per_axis": 4.0, "wall_timeout_s": 10.0, "max_steps": 3000, + "x_min": -0.32, "x_max": 0.18, "y_min": -0.15, "y_max": 0.15, + "pusher_origin_x": -0.3, "block_z_min": 0.02, "block_z_max": 0.08} +DIGEST = re.compile(r"[0-9a-f]{64}\Z") +IDENTITY = re.compile(r"[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}\Z") + + +def sha(data): + return hashlib.sha256(data).hexdigest() + + +def encoded(value): + return (json.dumps(value, sort_keys=True, indent=2, allow_nan=False) + "\n").encode() + + +def read_bytes(path): + try: + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK) + with os.fdopen(fd, "rb") as source: + before = os.fstat(source.fileno()) + if not stat.S_ISREG(before.st_mode) or before.st_size > MAX_FILE: + raise Refusal("retained file must be regular and within the 1 MiB limit") + data = source.read(MAX_FILE + 1) + after = os.fstat(source.fileno()) + if len(data) > MAX_FILE or (before.st_size, before.st_mtime_ns, before.st_ctime_ns) != ( + after.st_size, after.st_mtime_ns, after.st_ctime_ns + ): + raise Refusal("retained file changed or exceeded its size limit") + return data + except OSError as exc: + raise Refusal("retained file is missing, symlinked or unreadable") from exc + + +def decoded(data): + def unique(pairs): + result = {} + for key, value in pairs: + if key in result: + raise Refusal("duplicate JSON key") + result[key] = value + return result + + try: + return json.loads(data, object_pairs_hook=unique, + parse_constant=lambda value: (_ for _ in ()).throw(Refusal("nonfinite JSON value"))) + except (ValueError, UnicodeError) as exc: + raise Refusal("invalid retained JSON") from exc + + +def create_file(path, data): + fd = os.open(path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) + with os.fdopen(fd, "wb") as sink: + sink.write(data) + sink.flush() + os.fsync(sink.fileno()) + + +def directory(path, create=False): + root = Path(os.path.abspath(os.fspath(path))) + if root == OUTPUT or OUTPUT not in root.parents: + raise Refusal("run must be a subdirectory of physical-workcell/output") + current = OUTPUT + for part in (None, *root.relative_to(OUTPUT).parts): + if part is not None: + current = current / part + if create: + try: + current.mkdir(mode=0o700) + except FileExistsError: + pass + info = current.lstat() + if not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid() or info.st_mode & 0o077: + raise Refusal("run directories must be owned, private and free of symlinks") + return root + + +def fields(value, keys, label): + if not isinstance(value, dict) or set(value) != set(keys): + raise Refusal(f"{label} has invalid fields") + + +def number(value): + if type(value) not in (int, float) or not math.isfinite(value): + raise Refusal("coordinates and state values must be finite numbers") + return float(value) + + +def order_schema(order): + fields(order, ("version", "order_id", "project", "part_id", "target_zone"), "work order") + if type(order["version"]) is not int or order["version"] != 1 or order["part_id"] != "block": + raise Refusal("only version 1 block orders are supported") + fields(order["project"], ("namespace", "project_id"), "project identity") + for value in (order["order_id"], *order["project"].values()): + if not isinstance(value, str) or not IDENTITY.fullmatch(value): + raise Refusal("order and project identities must be exact bounded strings") + zone = order["target_zone"] + fields(zone, ("x_min", "x_max", "y_min", "y_max"), "target zone") + for value in zone.values(): + number(value) + if not (-0.35 <= zone["x_min"] < zone["x_max"] <= 0.35 and + -0.2 <= zone["y_min"] < zone["y_max"] <= 0.2): + raise Refusal("target zone must be a bounded nonempty tabletop rectangle") + return order + + +def fingerprints(): + if mujoco.__version__ != PINNED_VERSION: + raise Refusal("runtime must use pinned MuJoCo 3.15.0") + return {"mujoco_version": mujoco.__version__, "model_sha256": sha(read_bytes(SCENE)), + "implementation_sha256": sha(read_bytes(Path(__file__))), + "case_validator_sha256": sha(read_bytes(BASE / "episode_cases.py")), + "controller_sha256": sha(encoded(CONTROLLER)), + "state_spec": int(mujoco.mjtState.mjSTATE_INTEGRATION)} + + +def state_path(root, generation): + return root / f"state-{generation:06d}.json" + + +def save_state(root, model, data, generation): + spec = mujoco.mjtState.mjSTATE_INTEGRATION + values = np.empty(mujoco.mj_stateSize(model, spec), dtype=np.float64) + mujoco.mj_getState(model, data, values, spec) + if not np.isfinite(values).all(): + raise Refusal("native integration state contains nonfinite values") + content = encoded({"source_kind": SOURCE_KIND, "state_spec": int(spec), "values": values.tolist()}) + create_file(state_path(root, generation), content) + return sha(content) + + +def model_for(root, order): + model = mujoco.MjModel.from_xml_path(str(root / "model.xml")) + # This non-contact visual rectangle comes from the bound order, never changes dynamics. + zone = order["target_zone"] + marker = model.geom("inspection_zone") + marker.pos[:2] = [(zone["x_min"] + zone["x_max"]) / 2, (zone["y_min"] + zone["y_max"]) / 2] + marker.size[:2] = [(zone["x_max"] - zone["x_min"]) / 2, (zone["y_max"] - zone["y_min"]) / 2] + return model + + +def load(root): + manifest = decoded(read_bytes(root / "manifest.json")) + if (not isinstance(manifest, dict) or type(manifest.get("version")) is not int or + manifest["version"] != 1 or manifest.get("source_kind") != SOURCE_KIND): + raise Refusal("workcell manifest is invalid") + if not isinstance(manifest.get("phase"), str) or manifest["phase"] not in {"ready", "acting", "completing", "completed", "unknown"}: + raise Refusal("workcell phase is invalid") + generation = manifest.get("generation") + if type(generation) is not int or not 0 <= generation <= 999999: + raise Refusal("state generation is invalid") + if manifest.get("fingerprints") != fingerprints(): + raise Refusal("model, controller, implementation or native runtime fingerprint changed") + case_for(root, manifest) + for key in ("state_sha256", "work_order_sha256"): + if not isinstance(manifest.get(key), str) or not DIGEST.fullmatch(manifest[key]): + raise Refusal("retained digest is invalid") + if manifest["phase"] == "completed" and (not isinstance(manifest.get("result_sha256"), str) or not DIGEST.fullmatch(manifest["result_sha256"])): + raise Refusal("software result digest is invalid") + if sha(read_bytes(root / "model.xml")) != manifest["fingerprints"]["model_sha256"]: + raise Refusal("retained model changed") + order_bytes = read_bytes(root / "work-order.json") + if sha(order_bytes) != manifest["work_order_sha256"]: + raise Refusal("retained work order changed") + original = manifest.get("work_order_path") + if not isinstance(original, str) or not Path(original).is_absolute() or sha(read_bytes(Path(original))) != manifest["work_order_sha256"]: + raise Refusal("original work order is missing or changed") + order = order_schema(decoded(order_bytes)) + model = model_for(root, order) + state_bytes = read_bytes(state_path(root, generation)) + if sha(state_bytes) != manifest["state_sha256"]: + raise Refusal("retained integration state changed; implicit reset is forbidden") + state = decoded(state_bytes) + fields(state, ("source_kind", "state_spec", "values"), "integration state") + spec = mujoco.mjtState.mjSTATE_INTEGRATION + if state["source_kind"] != SOURCE_KIND or state["state_spec"] != int(spec) or not isinstance(state["values"], list): + raise Refusal("integration state schema is invalid") + if len(state["values"]) != mujoco.mj_stateSize(model, spec): + raise Refusal("integration state length does not match native model") + values = np.array([number(value) for value in state["values"]], dtype=np.float64) + data = mujoco.MjData(model) + mujoco.mj_setState(model, data, values, spec) + mujoco.mj_forward(model, data) + return manifest, order, model, data + + +def op_path(root, token): + if not isinstance(token, str) or not utils.TOKEN.fullmatch(token): + raise Refusal("operation token must be an exact bounded safe string") + return root / f"op-{sha(token.encode())}.json" + + +def load_op(path): + op = decoded(read_bytes(path)) + if not isinstance(op, dict) or not isinstance(op.get("phase"), str) or op["phase"] not in {"pending", "unknown", "executed", "staged"}: + raise Refusal("operation record is malformed") + if not isinstance(op.get("token"), str) or not utils.TOKEN.fullmatch(op["token"]) or not isinstance(op.get("request"), dict): + raise Refusal("operation identity is malformed") + if op.get("source_kind") != SOURCE_KIND or op_path(path.parent, op["token"]) != path: + raise Refusal("operation source or token-file binding is invalid") + request = op["request"] + if request.get("kind") == "act": + fields(request, ("kind", "expected_generation", "target_x", "target_y"), "motion request") + if type(request["expected_generation"]) is not int or request["expected_generation"] < 0: + raise Refusal("retained motion generation is invalid") + number(request["target_x"]) + number(request["target_y"]) + if op["phase"] == "staged": + raise Refusal("motion cannot be a software-staging receipt") + elif request.get("kind") == "complete": + fields(request, ("kind", "work_order_sha256"), "completion request") + if not isinstance(request["work_order_sha256"], str) or not DIGEST.fullmatch(request["work_order_sha256"]) or op["phase"] == "executed": + raise Refusal("retained completion receipt is invalid") + else: + raise Refusal("retained operation kind is invalid") + return op + + +def unresolved(root, manifest): + return manifest["phase"] in {"acting", "completing", "unknown"} or any( + load_op(path)["phase"] in {"pending", "unknown"} for path in root.glob("op-*.json") + ) + + +def poses(model, data, order): + block = data.xpos[model.body("block").id].copy() + pusher = data.xpos[model.body("pusher").id].copy() + zone = order["target_zone"] + in_zone = (zone["x_min"] <= block[0] <= zone["x_max"] and zone["y_min"] <= block[1] <= zone["y_max"] and + CONTROLLER["block_z_min"] <= block[2] <= CONTROLLER["block_z_max"]) + return {"block_xyz": block.tolist(), "pusher_xyz": pusher.tolist(), "block_center_in_zone": bool(in_zone)} + + +def artifact_check(root, expected_sha): + rc, stdout, _ = utils.run_process([str(FOOTWORK), "check-artifact", str(root / "order.json"), + "--expected-file", str(root / "expected-order.json"), + "--expected-sha256", expected_sha], 20) + result = decoded(stdout.encode()) + if not isinstance(result, dict) or type(result.get("ok")) is not bool or rc not in (0, 6) or result["ok"] != (rc == 0): + raise Refusal("Footwork checker result is invalid") + return result + + +def observation(root, loaded=None): + manifest, order, model, data = loaded or load(root) + uncertain = unresolved(root, manifest) + state = poses(model, data, order) + result = {"source_kind": SOURCE_KIND, "generation": manifest["generation"], "frame": "sim_world", + "state_sha256": manifest["state_sha256"], "simulation_time_s": float(data.time), + "phase": "unknown" if uncertain else manifest["phase"], "order_id": order["order_id"], + "target_zone": order["target_zone"], **state, + "state_authority": "retained_unresolved_operation" if uncertain else "current_persisted_simulator_state", + "physical_condition_verified": state["block_center_in_zone"] and not uncertain, + "claims": [] if uncertain else ["native_simulator_state_observation"]} + if manifest["phase"] == "completed" and not uncertain: + checked = artifact_check(root, manifest["result_sha256"]) + result.update(historical_software_outcome="staged", current_software_artifact_check=checked) + if not checked["ok"]: + result.update(phase="unknown", claims=[]) + return result + + +def case_for(root, manifest=None): + manifest = manifest or decoded(read_bytes(root / "manifest.json")) + binding = manifest.get("episode_case") + if binding is None: + return None + fields(binding, ("case_id", "case_file", "case_sha256", "max_actions"), "retained episode binding") + try: + case, _, content = cases.load(binding["case_file"], binding["case_sha256"]) + retained = cases.read_bytes(root / "episode-case.json") + if retained != content or binding["case_id"] != case["case_id"] or binding["max_actions"] != case["max_actions"]: + raise Refusal("retained case binding or bytes changed") + return case + except cases.CaseError as exc: + raise Refusal(str(exc)) from exc + + +def action_budget(root, manifest=None): + manifest = manifest or decoded(read_bytes(root / "manifest.json")) + case = case_for(root, manifest) + admitted = sum(load_op(path)["request"]["kind"] in {"act", "complete"} for path in root.glob("op-*.json")) + maximum = case["max_actions"] if case else None + return {"max_actions": maximum, "admitted_dispatches": admitted, + "remaining": None if maximum is None else max(0, maximum - admitted)} + + +def validate_episode_marker(root, binding): + path = root / "episode.json" + if not path.exists() and not path.is_symlink(): + return + marker = decoded(read_bytes(path)) + fields(marker, ("version", "episode_id", "run_dir", "case_path", "case_sha256", "participant", + "started_time_unix", "phase", "fingerprint"), "episode initialization marker") + if (type(marker["version"]) is not int or marker["version"] != 1 or marker["phase"] != "initializing" or + marker["run_dir"] != str(root) or marker["case_path"] != binding["case_file"] or + marker["case_sha256"] != binding["case_sha256"] or + marker["participant"] not in {"fresh_host", "deterministic_rgb"} or + not isinstance(marker["episode_id"], str) or not re.fullmatch(r"[0-9a-f]{32}", marker["episode_id"])): + raise Refusal("episode initialization marker is not bound to this case/run") + if number(marker["started_time_unix"]) <= 0: + raise Refusal("episode start time must be a positive finite timestamp") + fields(marker["fingerprint"], ("runner_sha256", "vision"), "episode runner fingerprint") + if not isinstance(marker["fingerprint"]["runner_sha256"], str) or not DIGEST.fullmatch(marker["fingerprint"]["runner_sha256"]) or not isinstance(marker["fingerprint"]["vision"], dict): + raise Refusal("episode initialization fingerprint is invalid") + + +def initialize(root, work_order, work_order_sha256, *, episode_case=None, case_binding=None): + if not DIGEST.fullmatch(work_order_sha256): + raise Refusal("work-order SHA256 must be an exact lowercase digest") + source = read_bytes(Path(work_order)) + if sha(source) != work_order_sha256: + raise Refusal("work order no longer matches its bound digest") + order = order_schema(decoded(source)) + original = str(Path(os.path.abspath(work_order))) + if (root / "manifest.json").exists() or (root / "manifest.json").is_symlink(): + loaded = load(root) + if (loaded[0]["work_order_sha256"] != work_order_sha256 or loaded[0]["work_order_path"] != original or + loaded[0].get("episode_case") != case_binding): + raise Refusal("run already belongs to another work order") + return observation(root, loaded) + allowed = {"lock", "episode.json", "episode-case.json", "episode-work-order.json"} if episode_case else {"lock"} + if any(path.name not in allowed for path in root.iterdir()): + raise Refusal("unreceipted run files exist; implicit initialization/reset is forbidden") + fingerprint = fingerprints() + create_file(root / "model.xml", read_bytes(SCENE)) + create_file(root / "work-order.json", source) + model = model_for(root, order) + data = mujoco.MjData(model) + if episode_case is not None: + # The caller-specified initial state is applied only on first initialization, never act/load. + start = model.jnt_qposadr[model.joint("block_free").id] + data.qpos[start:start + 2] = episode_case["scene"]["block_xy"] + for _ in range(round(CONTROLLER["settle_s"] / CONTROLLER["timestep_s"])): + mujoco.mj_step(model, data) + mujoco.mj_forward(model, data) + state_sha = save_state(root, model, data, 0) + manifest = {"version": 1, "source_kind": SOURCE_KIND, "phase": "ready", "generation": 0, + "work_order_path": original, "work_order_sha256": work_order_sha256, + "fingerprints": fingerprint, "state_sha256": state_sha} + if case_binding is not None: + manifest["episode_case"] = case_binding + utils.persist(root / "manifest.json", manifest) + return observation(root) + + +def initialize_episode(root, case_file, case_sha256): + try: + case, source_path, content = cases.load(case_file, case_sha256) + except cases.CaseError as exc: + raise Refusal(str(exc)) from exc + binding = {"case_id": case["case_id"], "case_file": str(source_path), "case_sha256": case_sha256, + "max_actions": case["max_actions"]} + order_path = root / "episode-work-order.json" + order_bytes = encoded(case["work_order"]) + if (root / "manifest.json").exists() or (root / "manifest.json").is_symlink(): + return initialize(root, str(order_path), sha(order_bytes), episode_case=case, case_binding=binding) + validate_episode_marker(root, binding) + if any(path.name not in {"lock", "episode.json"} for path in root.iterdir()): + raise Refusal("interrupted episode initialization has retained files; no implicit reset") + create_file(root / "episode-case.json", content) + create_file(order_path, order_bytes) + return initialize(root, str(order_path), sha(order_bytes), episode_case=case, case_binding=binding) + + +def motion(model, data, target_x, target_y, *, motion_control=None): + target = np.array([target_x - CONTROLLER["pusher_origin_x"], target_y]) + start = data.ctrl.copy() + steps = max(1, math.ceil(float(np.linalg.norm(target - start)) / (CONTROLLER["speed_m_s"] * CONTROLLER["timestep_s"]))) + settle = round(CONTROLLER["settle_s"] / CONTROLLER["timestep_s"]) + if steps + settle > CONTROLLER["max_steps"]: + raise Refusal("controller step budget exceeded") + push_ids = {model.geom("block_geom").id, model.geom("pusher_geom").id} + contacts, max_force = 0, 0.0 + started = time.monotonic() + cancelled, advancing_steps, native_steps = False, 0, 0 + pace = number(motion_control.get("pace_s", 0)) if motion_control else 0 + if not 0 <= pace <= .003 or (motion_control and not callable(motion_control.get("cancel_requested"))): + raise Refusal("motion cancellation/pacing hook is invalid") + for step in range(steps + settle): + if time.monotonic() - started > CONTROLLER["wall_timeout_s"]: + raise Refusal("native motion exceeded bounded wall time") + if motion_control and motion_control["cancel_requested"](): + cancelled = True + break + data.ctrl[:] = start + (target - start) * min(1, (step + 1) / steps) + advancing_steps = min(step + 1, steps) + mujoco.mj_step(model, data) + native_steps += 1 + if pace: + time.sleep(pace) + if not np.isfinite(data.qpos).all() or not np.isfinite(data.qvel).all(): + raise Refusal("native dynamics became nonfinite") + max_force = max(max_force, float(np.max(np.abs(data.actuator_force)))) + if max_force > CONTROLLER["force_limit_n_per_axis"] + 1e-8: + raise Refusal("native actuator exceeded its force envelope") + contacts += sum({int(contact.geom1), int(contact.geom2)} == push_ids for contact in data.contact) + steps_before_cancel = native_steps if cancelled else None + if cancelled: + # Hold the last actuator command. Never assign qpos/qvel or teleport a body. + for _ in range(settle): + if time.monotonic() - started > CONTROLLER["wall_timeout_s"]: + raise Refusal("cancel settling exceeded bounded native wall time") + mujoco.mj_step(model, data) + native_steps += 1 + if pace: + time.sleep(pace) + if not np.isfinite(data.qpos).all() or not np.isfinite(data.qvel).all(): + raise Refusal("native cancellation dynamics became nonfinite") + max_force = max(max_force, float(np.max(np.abs(data.actuator_force)))) + if max_force > CONTROLLER["force_limit_n_per_axis"] + 1e-8: + raise Refusal("native cancel settling exceeded its force envelope") + contacts += sum({int(contact.geom1), int(contact.geom2)} == push_ids for contact in data.contact) + mujoco.mj_forward(model, data) + result = {"native_steps": native_steps, "pusher_block_contact_samples": contacts, + "max_axis_actuator_force_n": max_force, "command_speed_limit_m_s": CONTROLLER["speed_m_s"]} + if motion_control: + start_dof = model.jnt_dofadr[model.joint("block_free").id] + linear = float(np.max(np.abs(data.qvel[start_dof:start_dof + 3]))) + angular = float(np.max(np.abs(data.qvel[start_dof + 3:start_dof + 6]))) + pusher_velocity = max(abs(float(data.qvel[model.jnt_dofadr[model.joint(name).id]])) + for name in ("pusher_x", "pusher_y")) + result.update(cancellation_requested=cancelled, target_trajectory_completed=advancing_steps == steps, + stop_verified=(linear <= .002 and angular <= .02 and pusher_velocity <= .002) if cancelled else None, + stop_evidence={"block_linear_velocity_max_m_s": linear, + "block_angular_velocity_max_rad_s": angular, + "pusher_velocity_max_m_s": pusher_velocity, + "linear_velocity_limit_m_s": .002, "angular_velocity_limit_rad_s": .02, + "held_actuator_command": data.ctrl.tolist(), "settle_steps": settle, + "native_steps_before_cancel": steps_before_cancel} if cancelled else None) + return result + + +def act(root, token, expected_generation, target_x, target_y, *, motion_control=None): + x, y = number(target_x), number(target_y) + if not CONTROLLER["x_min"] <= x <= CONTROLLER["x_max"] or not CONTROLLER["y_min"] <= y <= CONTROLLER["y_max"]: + raise Refusal("pusher target is outside the admitted workspace") + if type(expected_generation) is not int or expected_generation < 0: + raise Refusal("expected generation must be a nonnegative integer") + loaded = load(root) + manifest, order, model, data = loaded + request = {"kind": "act", "expected_generation": expected_generation, "target_x": x, "target_y": y} + path = op_path(root, token) + if path.exists() or path.is_symlink(): + op = load_op(path) + if op["token"] != token or op["request"] != request: + raise Refusal("token already belongs to different arguments") + return {"source_kind": SOURCE_KIND, "operation": op, "observation": observation(root, loaded), "replayed": False} + if unresolved(root, manifest) or manifest["phase"] == "completed": + raise Refusal("unresolved or completed work order blocks further motion") + if action_budget(root, manifest)["remaining"] == 0: + raise Refusal("episode admitted-action budget is exhausted") + if expected_generation != manifest["generation"]: + raise Refusal("observation generation is stale; observe again") + op = {"source_kind": SOURCE_KIND, "token": token, "request": request, "phase": "pending", + "intent_time_unix": time.time(), "before_state_sha256": manifest["state_sha256"]} + utils.persist(path, op) + try: + manifest["phase"] = "acting" + utils.persist(root / "manifest.json", manifest) + evidence = motion(model, data, x, y) if motion_control is None else motion(model, data, x, y, motion_control=motion_control) + op["controller_evidence"] = evidence + if evidence.get("cancellation_requested") and evidence.get("stop_verified") is not True: + raise Refusal("native cancellation has no verified stop") + generation = manifest["generation"] + 1 + state_sha = save_state(root, model, data, generation) + manifest.update(phase="ready", generation=generation, state_sha256=state_sha) + utils.persist(root / "manifest.json", manifest) + op.update(phase="executed", historical_motion_outcome="executed", after_generation=generation, + after_state_sha256=state_sha, physical_readback=poses(model, data, order), controller_evidence=evidence) + utils.persist(path, op) + except (Exception, KeyboardInterrupt): # noqa: BLE001 - all post-intent failures remain unresolved + op.update(phase="unknown", historical_motion_outcome="unknown") + manifest["phase"] = "unknown" + with contextlib.suppress(OSError): + utils.persist(path, op) + utils.persist(root / "manifest.json", manifest) + return {"source_kind": SOURCE_KIND, "operation": op, "observation": observation(root), "replayed": False} + + +def complete(root, token): + loaded = load(root) + manifest, order, model, data = loaded + request = {"kind": "complete", "work_order_sha256": manifest["work_order_sha256"]} + path = op_path(root, token) + if path.exists() or path.is_symlink(): + op = load_op(path) + if op["token"] != token or op["request"] != request: + raise Refusal("token already belongs to another operation") + return {"source_kind": SOURCE_KIND, "operation": op, "observation": observation(root, loaded), "replayed": False} + if unresolved(root, manifest) or manifest["phase"] == "completed": + raise Refusal("unresolved or already staged work order blocks another completion") + if action_budget(root, manifest)["remaining"] == 0: + raise Refusal("episode admitted-action budget is exhausted") + physical = poses(model, data, order) + if not physical["block_center_in_zone"]: + raise Refusal("observed block center is not in the work-order zone; no software result was created") + op = {"source_kind": SOURCE_KIND, "token": token, "request": request, "phase": "pending", + "intent_time_unix": time.time(), "physical_readback": physical, "state_sha256": manifest["state_sha256"]} + utils.persist(path, op) + try: + manifest["phase"] = "completing" + utils.persist(root / "manifest.json", manifest) + result = {"source_kind": SOURCE_KIND, "version": 1, "order_id": order["order_id"], + "project": order["project"], "part_id": "block", "state": "staged", + "physical_predicate": physical, "target_zone": order["target_zone"], + "simulator_generation": manifest["generation"], "simulator_state_sha256": manifest["state_sha256"], + "work_order_sha256": manifest["work_order_sha256"], "fingerprints": manifest["fingerprints"]} + content = encoded(result) + create_file(root / "expected-order.json", content) + create_file(root / "order.json", content) + result_sha = sha(content) + checked = artifact_check(root, result_sha) + if not checked["ok"]: + raise Refusal("local software result failed exact byte verification") + manifest.update(phase="completed", result_sha256=result_sha) + utils.persist(root / "manifest.json", manifest) + op.update(phase="staged", historical_software_outcome="staged", result_sha256=result_sha, artifact_check=checked) + utils.persist(path, op) + except (Exception, KeyboardInterrupt): # noqa: BLE001 - partial computer publication cannot replay + op.update(phase="unknown", historical_software_outcome="unknown") + manifest["phase"] = "unknown" + with contextlib.suppress(OSError): + utils.persist(path, op) + utils.persist(root / "manifest.json", manifest) + return {"source_kind": SOURCE_KIND, "operation": op, "observation": observation(root), "replayed": False} + + +def png_bytes(image): + height, width, channels = image.shape + if channels != 3 or image.dtype != np.uint8: + raise Refusal("native renderer did not return RGB bytes") + + def chunk(kind, content): + return struct.pack(">I", len(content)) + kind + content + struct.pack(">I", zlib.crc32(kind + content)) + + rows = b"".join(b"\x00" + row.tobytes() for row in image) + return b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0)) + chunk(b"IDAT", zlib.compress(rows)) + chunk(b"IEND", b"") + + +def render(root): + loaded = load(root) + manifest, _, model, data = loaded + if unresolved(root, manifest): + raise Refusal("unresolved state cannot supply a current simulator image") + path = root / f"render-{manifest['generation']:06d}.png" + if path.exists() or path.is_symlink(): + raise Refusal("render already exists and will not be overwritten") + try: + with mujoco.Renderer(model, height=360, width=640) as renderer: + renderer.update_scene(data, camera="overview") + content = png_bytes(renderer.render()) + except Exception as exc: + raise Refusal("native MuJoCo renderer unavailable; no image was fabricated") from exc + create_file(path, content) + return {"source_kind": SOURCE_KIND, "generation": manifest["generation"], "image_path": str(path), + "image_sha256": sha(content), "simulator_state_sha256": manifest["state_sha256"], "width": 640, "height": 360} + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + sub = parser.add_subparsers(dest="command", required=True) + for command in ("init", "observe", "act", "complete", "render"): + child = sub.add_parser(command) + child.add_argument("--run-dir", required=True) + if command == "init": + child.add_argument("--work-order", required=True) + child.add_argument("--work-order-sha256", required=True) + if command in {"act", "complete"}: + child.add_argument("--token", required=True) + if command == "act": + child.add_argument("--expected-generation", type=int, required=True) + child.add_argument("--target-x", type=float, required=True) + child.add_argument("--target-y", type=float, required=True) + args = parser.parse_args(argv) + try: + root = directory(args.run_dir, create=args.command == "init") + with utils.locked(root): + if args.command == "init": + result = initialize(root, args.work_order, args.work_order_sha256) + elif args.command == "act": + result = act(root, args.token, args.expected_generation, args.target_x, args.target_y) + elif args.command == "complete": + result = complete(root, args.token) + elif args.command == "render": + result = render(root) + else: + result = observation(root) + print(json.dumps(result, indent=2, sort_keys=True, allow_nan=False)) + op = result.get("operation", {}) + observed = result.get("observation", result) + return 2 if op.get("phase") in {"pending", "unknown"} or observed.get("phase") == "unknown" else 0 + except (Refusal, OSError, KeyError, TypeError, ValueError, AttributeError, KeyboardInterrupt) as exc: + reason = str(exc) if isinstance(exc, Refusal) else "local simulator input or retained state is malformed or unavailable" + print(json.dumps({"source_kind": SOURCE_KIND, "phase": "refused", "reason": reason, "claims": []})) + return 2 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/workbench-notes/.gitignore b/tools/workbench-notes/.gitignore new file mode 100644 index 0000000..fd6aad7 --- /dev/null +++ b/tools/workbench-notes/.gitignore @@ -0,0 +1,2 @@ +output/ +__pycache__/ diff --git a/tools/workbench-notes/README.md b/tools/workbench-notes/README.md new file mode 100644 index 0000000..ed1de38 --- /dev/null +++ b/tools/workbench-notes/README.md @@ -0,0 +1,147 @@ +# Project workbench notes + +Link an already reviewed desk note to one exact project and retain an inspectable JSON +and Markdown packet. The packet separates physical-source evidence from caller interpretation +and a proposed next check. It never runs that check or interprets note text as instructions. + +This package reuses the desk inbox's saved-source verification and the public Footwork +`check-artifact` command. It opens no camera, calls no provider, creates no remote tasks, +and writes only its own private output. No native/browser session or Carry is fabricated. + +## Inputs + +Supply a bounded version-1 project catalog: + +```json +{ + "version": 1, + "projects": [ + { + "namespace": "codex_app", + "project_id": "abe2a667-dedc-4f3f-9863-62f3d80188bd", + "label": "footwork", + "root": "/Users/thomasmain/projects/footwork" + } + ] +} +``` + +The namespace and project ID must match exactly. Labels never select a project; duplicate +labels are allowed, duplicate namespaced identities are refused. The selected checkout must +be an existing canonical absolute directory. The catalog is caller-supplied metadata, not +authenticated identity from Codex. Only the selected entry is bound, so unrelated catalog +changes do not invalidate a packet. + +Supply a caller-authored record, with all these fields: + +```json +{ + "project": { + "namespace": "codex_app", + "project_id": "abe2a667-dedc-4f3f-9863-62f3d80188bd" + }, + "kind": "experiment", + "title": "Paper-note handoff", + "question": "Does a fresh-process status check leave the saved packet unchanged?", + "interpretation": "The reviewed paper note supplies a starting observation, rather than a completed test.", + "proposed_next_check": "Record packet hashes, run status in a fresh process, then compare hashes again.", + "expected_result": "Packet hashes remain unchanged; status retains its artifact checks; the proposed check remains not_run.", + "references": [] +} +``` + +Supported kinds are `observation`, `question`, `experiment`, `decision`, and `blocker`. +The title is one line, at most 160 characters; other text fields are nonempty and at most +4096 characters. Up to 32 code references use `{ "path": "repo/relative.py", "line": 12 }`. +They must exist inside the selected checkout, without symlinks or escapes, with a valid +positive line number. Secret patterns and dependency/build/data directories are refused. +Reference files are bounded to 1 MiB; metadata inputs to 128 KiB. + +These strings remain caller assertions. Neither the catalog nor a record digest authenticates +who wrote them or establishes that the interpretation is correct. + +## Link and review + +Run from the repository root. Keep inputs private; the parent-owned `output/inputs/` directory +is one possible location. Obtain the exact record-file SHA256 with `shasum -a 256`, then: + +```sh +python3 tools/workbench-notes/workbench.py link \ + --desk-run tools/desk-inbox-experiment/output/live-paper-v1 \ + --catalog tools/workbench-notes/output/inputs/catalog.json \ + --record-file tools/workbench-notes/output/inputs/footwork-record.json \ + --record-sha256 REPLACE_WITH_APPROVED_RECORD_DIGEST \ + --run-dir tools/workbench-notes/output/abe2a667-dedc-4f3f-9863-62f3d80188bd/first +``` + +The source must be a currently verified saved desk note. The tool binds its manifest and +note hashes, original frame provenance, explicit orientation and review kind. Native-camera +sources retain that label; synthetic fixtures remain synthetic. The original desk run and +source files remain untouched and must stay available for later checks. + +`record.json` retains the reviewed note text, caller question/interpretation, proposed check, +acceptance criterion, checkout HEAD and optional reference hashes. Its check outcome is always +`not_run`. `record.md` presents the same evidence for review, with caller/source text HTML-escaped. +Open the Markdown file in an editor; the CLI prints paths and verification metadata without +the source note contents. + +The tool records intent before create-only publication and calls the real Footwork CLI for +both outputs against digest-bound expected files. It retains `source-note.txt`, the original +caller metadata bytes, expected JSON/Markdown, and the checked published packet. + +## Status and project listing + +```sh +python3 tools/workbench-notes/workbench.py status \ + --run-dir tools/workbench-notes/output/abe2a667-dedc-4f3f-9863-62f3d80188bd/first + +python3 tools/workbench-notes/workbench.py list \ + --project-namespace codex_app \ + --project-id abe2a667-dedc-4f3f-9863-62f3d80188bd \ + --output-root tools/workbench-notes/output/abe2a667-dedc-4f3f-9863-62f3d80188bd +``` + +Status rechecks retained source/caller bytes and both packets. It preserves historical recording +when present, while missing/tampered evidence makes current state `unknown` with empty claims. +The original caller input must remain available and retain its bound digest. + +Repository HEAD/reference drift is reported separately as `unchanged`, `changed`, or +`unavailable`. A changed checkout does not alter the saved packet or erase valid byte checks. +It also does not make the proposed check run. Missing Git or reference context is explicitly +unavailable rather than fabricated. + +Repeated `link` with identical binding reads the retained operation without rewriting files. +Changed project/source/metadata/digest conflicts. Interrupted or unknown publication never +replays, and a missing output is never recreated. There is no reset command. + +Listing filters exact namespaced identity and returns title, kind, state, context summary and +run directory. It does not print note contents or caller paragraphs. It traverses only owned +private directories under this package's output, skips symlinks and reports exclusions/errors +as incomplete. Listing is capped at 1000 directories. Choose a project subtree to keep it narrow. + +## Privacy, limits and verification + +Run directories use 0700; created files use 0600. Notes, packet text, references, hashes and +receipts stay under ignored `output/`. One nonblocking process lock permits one owner per run. +The tool makes no writes to the selected checkout or catalog. + +Exit code 0 means `recorded`, absent status, or a complete listing. Exit code 2 means refusal, +unknown evidence or incomplete listing. Read JSON state and context alongside the exit code. + +Artifact equality establishes saved bytes matching the retained expected sources. It does not +establish OCR truth, interpretation accuracy, catalog authenticity, test completion, physical +causality, current code correctness, or power-loss recovery. This is a local record, with no +Codex sidebar task or project attachment created. + +```sh +cd jevdual +uv run --no-sync pytest ../tools/workbench-notes/test_workbench.py -q +uv run --no-sync ruff check ../tools/workbench-notes +``` + +Tests use uniquely owned synthetic desk runs and temporary repositories, plus real public +Footwork byte checks. They never acquire a camera frame or execute proposed checks. A real +reviewed-note linkage and route profiling belong to separate preregistered runs. + +Preregistration: `jevdual/docs/experiments/WB1-project-workbench.md`. +Notebook: `jevdual/docs/notebook/2026-10-10-project-workbench.md`. diff --git a/tools/workbench-notes/test_workbench.py b/tools/workbench-notes/test_workbench.py new file mode 100644 index 0000000..7a73713 --- /dev/null +++ b/tools/workbench-notes/test_workbench.py @@ -0,0 +1,278 @@ +"""Bounded workbench conformance; synthetic reviewed notes, no camera or proposed checks.""" + +import base64 +import copy +import importlib.util +import json +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +BASE = Path(__file__).resolve().parent +SPEC = importlib.util.spec_from_file_location("workbench_notes", BASE / "workbench.py") +workbench = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(workbench) +PNG = base64.b64decode( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=" +) + + +class WorkbenchTests(unittest.TestCase): + def setUp(self): + workbench.OUTPUT.mkdir(mode=0o700, exist_ok=True) + self.tmp = tempfile.TemporaryDirectory(prefix="conformance-", dir=workbench.OUTPUT) + self.addCleanup(self.tmp.cleanup) + self.work = Path(self.tmp.name) + self.repo = self.work / "checkout" + self.repo.mkdir(mode=0o700) + (self.repo / "module.py").write_text("# fixture\nvalue = 1\n") + self.git("init", "-q") + self.git("add", "module.py") + self.commit("fixture") + self.project = {"namespace": "codex_app", "project_id": "project-a", "label": "same label", + "root": str(self.repo.resolve())} + self.catalog = {"version": 1, "projects": [self.project, {**self.project, "project_id": "project-b"}]} + self.catalog_path = self.work / "catalog.json" + self.record_path = self.work / "caller.json" + self.caller = {"project": {"namespace": "codex_app", "project_id": "project-a"}, "kind": "experiment", + "title": "Bounded fixture observation", "question": "What changes under interruption?", + "interpretation": "Caller interpretation only", "proposed_next_check": "Do not execute: rm fixture", + "expected_result": "Independent evidence of the expected result", + "references": [{"path": "module.py", "line": 2}]} + self.catalog_path.write_bytes(workbench.json_bytes(self.catalog)) + self.sha = self.write_record() + self.scope = workbench.private_directory(self.work / "records", create=True) + self.root = workbench.private_directory(self.scope / "project-a" / "first", create=True) + workbench.desk.OUTPUT.mkdir(mode=0o700, exist_ok=True) + desk_tmp = tempfile.TemporaryDirectory(prefix="workbench-fixture-", dir=workbench.desk.OUTPUT) + self.addCleanup(desk_tmp.cleanup) + self.desk_root = Path(desk_tmp.name) + image = self.work / "synthetic.png" + image.write_bytes(PNG) + with patch.object(workbench.desk, "recognize", return_value={"lines": [ + {"text": "PRIVATE_SYNTHETIC_NOTE", "confidence": .8}]}): + prepared = workbench.desk.prepare(self.desk_root, fixture_image=str(image)) + result = workbench.desk.accept(self.desk_root, prepared["candidate_sha256"]) + self.assertEqual(result["state"], "saved") + + def git(self, *args): + result = subprocess.run(["git", "-C", str(self.repo), *args], capture_output=True, + text=True, check=False, timeout=10) + self.assertEqual(result.returncode, 0, result.stderr) + return result.stdout.strip() + + def commit(self, message): + return self.git("-c", "user.name=Fixture", "-c", "user.email=fixture@example.invalid", + "commit", "-q", "-m", message) + + def write_record(self): + data = workbench.json_bytes(self.caller) + self.record_path.write_bytes(data) + return workbench.desk.digest(data) + + def link(self): + return workbench.link(self.root, str(self.desk_root), str(self.catalog_path), str(self.record_path), self.sha) + + def cli(self, *args): + return subprocess.run([sys.executable, str(BASE / "workbench.py"), *args], capture_output=True, + text=True, check=False, timeout=30) + + def test_packet_preserves_evidence_caller_proposal_and_real_file_checks(self): + result = self.link() + self.assertEqual(result["state"], "recorded") + self.assertEqual(result["context"]["state"], "unchanged") + self.assertTrue(all(check["ok"] for check in result["artifact_checks"].values())) + self.assertNotIn("PRIVATE_SYNTHETIC_NOTE", json.dumps(result)) + packet = json.loads((self.root / "record.json").read_bytes()) + self.assertEqual(packet["source_evidence"]["note_text"], "PRIVATE_SYNTHETIC_NOTE\n") + self.assertEqual(packet["source_evidence"]["source_kind"], "synthetic_fixture") + self.assertEqual(packet["proposed_check"]["outcome"], "not_run") + self.assertEqual(packet["metadata_authorship"], "caller_supplied") + self.assertEqual(packet["context_snapshot"]["git_head"], self.git("rev-parse", "HEAD")) + self.assertTrue((self.repo / "module.py").exists()) + self.assertFalse((self.root / "session.json").exists()) + + def test_fresh_process_link_status_list_repeat_without_new_publication(self): + args = ("link", "--desk-run", str(self.desk_root), "--catalog", str(self.catalog_path), + "--record-file", str(self.record_path), "--record-sha256", self.sha, "--run-dir", str(self.root)) + result = self.cli(*args) + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + before = {p.name: (p.read_bytes(), p.stat().st_mtime_ns) for p in self.root.iterdir() if p.is_file() and p.name != "lock"} + self.assertEqual(self.cli(*args).returncode, 0) + self.assertEqual(self.cli("status", "--run-dir", str(self.root)).returncode, 0) + after = {p.name: (p.read_bytes(), p.stat().st_mtime_ns) for p in self.root.iterdir() if p.is_file() and p.name != "lock"} + self.assertEqual(before, after) + listing = self.cli("list", "--project-namespace", "codex_app", "--project-id", "project-a", + "--output-root", str(self.scope)) + self.assertEqual(listing.returncode, 0, listing.stdout + listing.stderr) + data = json.loads(listing.stdout) + self.assertEqual(len(data["records"]), 1) + self.assertEqual(data["records"][0]["title"], self.caller["title"]) + self.assertNotIn("PRIVATE_SYNTHETIC_NOTE", listing.stdout) + + def test_duplicate_labels_are_valid_exact_ids_and_conflicts_refuse(self): + self.link() + self.caller["project"]["project_id"] = "project-b" + self.sha = self.write_record() + with self.assertRaises(workbench.Refusal): + self.link() + other = workbench.selected_project(self.catalog, self.caller["project"]) + self.assertEqual(other["project_id"], "project-b") + duplicate = {"version": 1, "projects": [self.project, dict(self.project)]} + with self.assertRaises(workbench.Refusal): + workbench.selected_project(duplicate, self.caller["project"]) + with self.assertRaises(workbench.Refusal): + workbench.selected_project(self.catalog, {"namespace": "another", "project_id": "project-a"}) + + def test_unrelated_catalog_changes_do_not_change_binding(self): + self.link() + self.catalog["projects"][1]["label"] = "unrelated renamed project" + self.catalog_path.write_bytes(workbench.json_bytes(self.catalog)) + self.assertEqual(self.link()["state"], "recorded") + self.catalog["projects"][0]["root"] = str(self.work.resolve()) + self.catalog_path.write_bytes(workbench.json_bytes(self.catalog)) + with self.assertRaises(workbench.Refusal): + self.link() + + def test_changed_digest_source_and_caller_metadata_are_not_reused(self): + self.link() + original = self.record_path.read_bytes() + self.record_path.write_bytes(original + b" ") + with self.assertRaises(workbench.Refusal): + self.link() + self.assertEqual(workbench.status(self.root)["state"], "unknown") + self.record_path.write_bytes(original) + self.caller["interpretation"] = "changed caller interpretation" + self.sha = self.write_record() + with self.assertRaises(workbench.Refusal): + self.link() + (self.desk_root / "inbox" / "note.txt").write_text("different") + self.assertEqual(workbench.status(self.root)["state"], "unknown") + + def test_secret_escape_symlink_missing_and_invalid_lines_refuse(self): + secret = self.repo / ".ENV.example" + secret.write_text("never opened") + (self.repo / "alias.py").symlink_to(self.repo / "module.py") + for path, line in (("../outside", 1), ("/absolute", 1), (".ENV.example", 1), ("data/file", 1), + ("node_modules/file", 1), ("key.pem", 1), ("alias.py", 1), ("missing", 1), + ("module.py", 999), ("module.py", 0), ("module.py", True)): + with self.subTest(path=path, line=line), self.assertRaises(workbench.Refusal): + candidate = copy.deepcopy(self.caller) + candidate["references"] = [{"path": path, "line": line}] + workbench.validate_record(candidate) + workbench.reference_info(self.repo, candidate["references"][0]) + + def test_context_drift_does_not_erase_recorded_byte_integrity(self): + self.link() + before = (self.root / "record.json").read_bytes() + (self.repo / "module.py").write_text("# fixture\nvalue = 2\n") + self.git("add", "module.py") + self.commit("context changed") + result = workbench.status(self.root) + self.assertEqual(result["state"], "recorded") + self.assertEqual(result["context"]["state"], "changed") + self.assertEqual(result["context"]["references"][0]["state"], "changed") + self.assertEqual((self.root / "record.json").read_bytes(), before) + self.assertEqual(json.loads(before)["proposed_check"]["outcome"], "not_run") + self.assertTrue(result["claims"]) + self.assertEqual(self.link()["context"]["state"], "changed") + + def test_missing_or_tampered_packet_preserves_history_and_never_recreates(self): + self.link() + output = self.root / "record.md" + output.write_bytes(b"changed") + result = workbench.status(self.root) + self.assertEqual(result["state"], "unknown") + self.assertEqual(result["historical_recorded_outcome"], "recorded") + self.assertEqual(result["claims"], []) + output.unlink() + self.assertEqual(self.link()["state"], "unknown") + self.assertFalse(output.exists()) + + def test_lost_receipt_never_replays(self): + original = workbench.desk.camera.persist + + def lose(path, manifest): + if manifest["state"] == "recorded": + raise OSError("injected lost receipt") + original(path, manifest) + + with patch.object(workbench.desk.camera, "persist", side_effect=lose): + self.assertEqual(self.link()["state"], "unknown") + output = self.root / "record.json" + before = output.stat().st_mtime_ns + self.assertEqual(self.link()["state"], "unknown") + self.assertEqual(output.stat().st_mtime_ns, before) + + def test_partial_packet_publication_remains_unknown(self): + original = workbench.desk.create_file + + def partial(path, data): + if path.name == "record.md": + original(path, data[:5]) + raise OSError("injected partial publication") + original(path, data) + + with patch.object(workbench.desk, "create_file", side_effect=partial): + self.assertEqual(self.link()["state"], "unknown") + output = self.root / "record.md" + self.assertEqual(len(output.read_bytes()), 5) + self.assertEqual(self.link()["state"], "unknown") + self.assertEqual(len(output.read_bytes()), 5) + + def test_locked_run_refuses_another_process(self): + self.link() + with workbench.desk.camera.locked(self.root): + result = self.cli("status", "--run-dir", str(self.root)) + self.assertEqual(result.returncode, 2) + self.assertEqual(json.loads(result.stdout)["state"], "refused") + + def test_unavailable_repository_reference_does_not_rewrite_packet(self): + self.link() + (self.repo / "module.py").unlink() + result = workbench.status(self.root) + self.assertEqual(result["state"], "recorded") + self.assertEqual(result["context"]["state"], "unavailable") + self.assertTrue(result["claims"]) + + def test_markdown_escapes_source_and_caller_content(self): + self.caller["title"] = "" + self.caller["interpretation"] = "" + self.sha = self.write_record() + self.link() + md = (self.root / "record.md").read_text() + self.assertNotIn("