Skip to content

fix(kernel): Opentrons evidence uses the closed vocabulary and reports the robot's outcome - #426

Draft
LamaSu wants to merge 1 commit into
masterfrom
fix/kernel-opentrons-evidence-vocabulary
Draft

LamaSu wants to merge 1 commit into
masterfrom
fix/kernel-opentrons-evidence-vocabulary

Conversation

@LamaSu

@LamaSu LamaSu commented Sep 24, 2026

Copy link
Copy Markdown
Owner

What

The TypeScript Opentrons adapter (packages/kernel/src/opentrons/adapter.ts) now emits evidence only from the closed vocabulary (EVIDENCE_EVENT_TYPES), and it reports the robot's own outcome.

When Before Now
Protocol upload protocol_uploaded method_loaded {opentronsProtocolId, name, protocolHash}. The hash is sha256 of the protocol source, i.e. what the robot was told to run
First play of a run run_action execution_started {opentronsRunId}
Pause, resume run_action execution_progress {opentronsRunId, action}
Stop run_action execution_failed {opentronsRunId, reason}
The robot reports the run succeeded (nothing in real mode) execution_completed {status, commandsSucceeded, commandsTotal}
The robot reports failed or stopped (nothing; the runner waited 120 s for its timeout) execution_failed {status, errors}; the runner sees idle and fails at once
Mock mode run_started / run_progress / run_completed execution_*, still marked mock: true and source.simulated

Why

In real mode, no Opentrons bundle could prove a run finished: it held protocol_uploaded and run_action and nothing else. adk limited the opentrons manifest to tier 0 until this mapping lands (#418, bus #3309), and evidence took the mapping.

Details

  • Reserved-field rule (#3219). Robot-local ids are opentronsRunId and opentronsProtocolId. payload.jobId and the unit fields are reserved for the PCC binding, which the kernel's EvidenceEmitter stamps on every event (feat(evidence): LO-EV-9 bind device evidence to the accepted job and kernel before settlement #341). The tests assert that no reserved key appears in any payload.
  • The robot's outcome is read when the runner polls getProgress(). A terminal status releases the run. Before this change, a finished run stayed current, so the next job's start replayed it.
  • emitEvidence now takes an EvidenceEventType, not string cast to any, so the compiler enforces the vocabulary.

Tests (DGX Spark)

  • kernel 867/867 on master, including 4 new tests in opentrons-evidence-vocabulary.test.ts, run against a fake Opentrons HTTP API. adapter-honesty.test.ts is updated to the new types. tsc is clean.
  • Test-merged into feat(evidence): LO-EV-9 bind device evidence to the accepted job and kernel before settlement #341 (feat/evidence-subject-binding @799cea1d): kernel 873/873.
  • Mutation check. Each of these mutants is killed:
    • the outcome is not read;
    • the run is not released on success, or on failure;
    • the first play is reported as progress;
    • a stop is reported as a completion;
    • runId is used in place of opentronsRunId.

Draft. Merging is the operator's call.

🤖 Generated with Claude Code

https://claude.ai/code/session_0117ows6894R3n6YQXBCRahS

…s the robot's outcome

The TypeScript Opentrons adapter emitted types outside
EVIDENCE_EVENT_TYPES (protocol_uploaded, run_action, run_started,
run_progress, run_completed). In real mode it never emitted a completion
at all, so no Opentrons bundle could prove a run finished. adk limited the
opentrons manifest to tier 0 until the adapter is mapped (#418).

Mapping (real and mock mode):
- protocol upload: method_loaded {opentronsProtocolId, name,
  protocolHash = sha256 of the protocol source}
- first play: execution_started {opentronsRunId}
- pause, resume: execution_progress {opentronsRunId, action}
- stop: execution_failed {opentronsRunId, reason}
- the robot reports the run succeeded: execution_completed {status,
  commandsSucceeded, commandsTotal}, read when the runner polls progress
- the robot reports it failed or stopped: execution_failed {status, errors}

Robot-local ids are opentronsRunId and opentronsProtocolId. payload.jobId
and the unit fields are reserved for the PCC binding, which the kernel's
EvidenceEmitter stamps.

A terminal status now releases the run. Before, a finished run stayed
current, so the next job's start replayed it, and a failed run left the
runner polling "busy" until its 120 s timeout. It now sees idle and fails
at once.

kernel 867/867 (4 new tests against a fake Opentrons HTTP API), tsc
clean. Mutants (no outcome read, no release on success or on failure, the
first play reported as progress, stop reported as a completion, runId in
place of opentronsRunId) are each killed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117ows6894R3n6YQXBCRahS
LamaSu added a commit that referenced this pull request Sep 29, 2026
…ompleted commands are timed by the run's end

Sensors' review of #417 (bus #3461, returns/pcc-sensors-review-417.md):

- F1: every event now has an id, <opentronsRunId>:<commandId>, and a
  source.deviceType of "instrument", the value the kernel's Opentrons adapter
  reports (packages/kernel/src/opentrons/adapter.ts, on master and on #426).
  EvidenceEventSchema.safeParse passes on every event; on fbdab3d it failed
  with "id: Required" and "source.deviceType: Required".
- F2, option (a): an entry is timed by the command's completedAt. A command
  that never completed is timed by run_ended_at, the run's own completedAt.
  run_ended_at is required in that case and may not be earlier than any
  completion, so capturedAt never runs backwards for that entry. createdAt
  stays in rawContent. Times are parsed as ISO-8601 with a zone, and a
  malformed time fails closed.
- The TS goldens are recomputed with packages/spec's hashEvent and hashBundle.
  deviceType is part of the hashed source; id is not hashed, as in TS.

Verified: 34 command-trace tests and 23 log-capture parity tests pass. With
the TS spec (run through tsx), every event passes EvidenceEventSchema, Python
and TS agree on hashEvent and hashBundle, and verifyEvidenceSubjectBinding
(#341) returns ok. Against fbdab3d, 8 of the tests fail, each on the
property it names.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant