Benchtop Tool — capture and submit a lab witness against an energy summit
GFTCL-LION-ENERGY-LEDGER-001 / benchtop tool. The operator-facing or lab-facing workflow that lets an experimenter run an energy-summit protocol locally, lock the pre-registration at the moment the run starts, and submit a witness package to the cell. Primary surface: headless CLI (M8BenchtopWitness) for lab-side / scripted use. Same benchtop_sessions substrate whether the witness is captured from terminal or HTTP membrane.
The integrity property the tool exists to enforce
A lab cannot retro-fit its pre-registration to its result. The substrate trigger trig_benchtop_preregistration_immutable refuses any UPDATE that touches preregistration_sha256, preregistration_at_iso, started_at_iso, or session_hash after a session is started. The pre-registration is locked at the call instant of startSession(…), and from there it is the ground truth the operator's verdict is graded against.
This is the same discipline the rest of the cell runs under (immutable witness columns on alert_queue / energy_ledger_receipts / summit_states), now applied to the lab-side capture surface. The benchtop tool structurally cannot let a lab cheat the pre-registration; the SQL layer refuses.
Operator surface
| Surface | When to use | How |
|---|---|---|
M8BenchtopWitness CLI |
Lab-side; headless; scriptable; CI/CD integration | swift run M8BenchtopWitness <cmd> ... |
| HTTP membrane | Remote lab submission | POST /energy/summits/{slug}/witness (see Energy-Ledger-Breaker-Replication) |
All paths read and write the same benchtop_sessions table via BenchtopSessionStore. There is no second source of truth.
CLI workflow
# 1) List open summits
swift run M8BenchtopWitness list
# 2) Inspect a summit's pre-registered fields
swift run M8BenchtopWitness show --summit benchtop_divergence
# 3) Start a session (PRE-REGISTRATION LOCKED AT THIS INSTANT)
swift run M8BenchtopWitness start \
--summit benchtop_divergence \
--lab-identity "MIT-Plasma-Lab-2026" \
--apparatus-id "rev-A-serial-0042" \
--notes "blinded by independent witness Dr. X"
# → prints session_id (e.g., bts-B1E4AE91-...)
# 4) Record measured values (JSON file matching the predicted-values names)
swift run M8BenchtopWitness measure \
--session-id bts-... \
--measured ./measured.json
# 5) Mark ready to submit
swift run M8BenchtopWitness ready --session-id bts-...
# 6a) EITHER export the witness JSON for offline submission
swift run M8BenchtopWitness export --session-id bts-... > witness.json
# 6b) OR submit directly to the local endpoint
swift run M8BenchtopWitness submit \
--session-id bts-... \
--endpoint http://127.0.0.1:8423 \
--membrane-token "$ENERGY_LAB_TOKEN"
# 7) Inspect a session at any time
swift run M8BenchtopWitness show --session-id bts-...
A measured.json file looks like:
[
{"name": "thermal_suppression_pct", "measured": 12.4, "instrument": "FLIR-T1020"},
{"name": "clock_offset_slope", "measured": 0.47, "instrument": "Microsemi-5071A"},
{"name": "controls_match_classical", "measured": true, "instrument": "calorimetry-baseline"}
]
The name field must match an entry in the summit's predicted_values_json (visible via show --summit <slug>). Free-form fields beyond the predicted set are allowed and preserved in the submission.
HTTP membrane workflow
After witness produces a ready package, submit to the cell:
curl -X POST "https://<cell-host>/energy/summits/benchtop_divergence/witness" \
-H "Authorization: Bearer <membrane-token>" \
-H "Content-Type: application/json" \
-d @witness.json
On 202 Accepted, the session flips to submitted (frozen by trigger). Pre-registered prediction, success criterion, and null-result clause are visible via show --summit <slug> before start.
Session lifecycle
startSession() updateMeasured()
│ │
▼ ▼
┌───────────────┐ ────────► ┌────────────────┐
│ in_progress │ ──────────►│ ready_to_submit │
└───────┬───────┘ └────────┬────────┘
│ │
│ POST /energy/...witness │
│ 202 accepted │
▼ ▼
┌───────────────┐ ┌────────────────┐
│ abandoned │ │ submitted │ ← terminal, FROZEN by trigger
└───────────────┘ └────────────────┘
│
│ on operator seal (sealed_confirm | sealed_refute)
▼
┌────────────────┐
│ summit status │
│ transitions │
└────────────────┘
A submitted session row is frozen by trig_benchtop_terminal_frozen — no further UPDATEs accepted. The corresponding lab_submissions.submission_id links the two records; the operator's subsequent seal of the submission drives the summit_states.status transition (inReplication → closedConfirmed | closedRefuted).
Substrate schema (V120)
CREATE TABLE benchtop_sessions (
session_id TEXT PRIMARY KEY,
summit_id TEXT NOT NULL REFERENCES summit_states(summit_id),
lab_identity TEXT NOT NULL,
apparatus_id TEXT NOT NULL DEFAULT '',
apparatus_calibration_json TEXT NOT NULL DEFAULT '{}',
measured_values_json TEXT NOT NULL DEFAULT '[]',
preregistration_sha256 TEXT NOT NULL, -- IMMUTABLE
preregistration_at_iso TEXT NOT NULL, -- IMMUTABLE
status TEXT NOT NULL DEFAULT 'in_progress',
started_at_iso TEXT NOT NULL, -- IMMUTABLE
last_updated_at_iso TEXT NOT NULL,
submitted_submission_id TEXT,
session_hash TEXT NOT NULL, -- IMMUTABLE
notes TEXT NOT NULL DEFAULT '',
timestamp_iso TEXT NOT NULL,
CHECK (status IN ('in_progress','ready_to_submit','submitted','abandoned'))
);
Two triggers enforce the discipline:
-- Pre-registration anchor + start time + session hash are immutable
CREATE TRIGGER trig_benchtop_preregistration_immutable BEFORE UPDATE ON benchtop_sessions
... RAISE(ABORT, 'benchtop_sessions pre-registration anchor is immutable');
-- A submitted session is fully frozen
CREATE TRIGGER trig_benchtop_terminal_frozen BEFORE UPDATE ON benchtop_sessions
WHEN OLD.status = 'submitted' BEGIN
RAISE(ABORT, 'benchtop_sessions terminal state submitted — row is frozen');
END;
Verification (this commit)
The CLI has been exercised end-to-end against the live substrate:
1. ✓ list enumerates the three energy summits.
2. ✓ show --summit benchtop_divergence prints the pre-registered predicted-values schema.
3. ✓ start locks the pre-registration anchor and prints the session_id + sha + timestamp.
4. ✓ measure persists measured values from a JSON file.
5. ✓ ready transitions the session to ready_to_submit.
6. ✓ export prints the witness submission JSON to stdout (verified to include lab_identity, apparatus_id, measured_values, preregistration_sha256, preregistration_at_iso, session_hash, blinding_attestation).
7. ✓ Retro-fit attempt REFUSED: UPDATE benchtop_sessions SET preregistration_sha256='tampered' produces Error: benchtop_sessions pre-registration anchor is immutable (19) — the trigger fires.
The SwiftUI sheet compiles clean and is wired to EnergyDomainPanel's summit cards via .sheet(item: $benchtopSummit).
Constitutional posture
- One substrate, two surfaces — CLI and SwiftUI both read/write
benchtop_sessionsthroughBenchtopSessionStore. They cannot disagree because there is no second source of truth. - Pre-registration immutable by SQL trigger — the lab cannot retro-fit the prediction to the result. Refused at the database layer, not at the application layer.
- No external dependency — the CLI uses
URLSessionto POST locally; the sheet uses the same. No HTTP framework, no auth service. - Membrane-token-gated submission — direct POST requires the lab's
ENERGY_LAB_TOKENvalue supplied viaX-Gaia-Membraneheader. Without it, the substrate rejects the submission with 401. - A null result is the protocol working — the sheet displays the null-result clause in the same visual weight as the success criterion. The
lab_submissions.seal_statedistinguishessealed_confirmfromsealed_refute; the summit transitions toclosedConfirmedorclosedRefutedaccordingly, with equal weight on both publish paths.
What this tool is NOT
- Not a calibration utility. Apparatus calibration is the lab's responsibility; the tool records the calibration manifest the lab supplies but does not certify it.
- Not a grader. The operator (or a future automated grader) compares measured values against the success criterion + null-result clause to decide
sealed_confirmorsealed_refute. The CLI / sheet records the witness; the seal is a separate operator action onlab_submissions. - Not an automation harness. The tool captures what the lab measured; running the apparatus, recording instrumentation, and aggregating raw data are out of scope — that's the lab's instrumentation stack.
Next moves (named)
- Schema validation against
predicted_values_json— currently themeasuredJSON is checked only for parseability. A v2 pass would enforce that every entry'snamematches a predicted-values entry and that themeasuredvalue's type matches the predicted unit / shape. - Per-lab membrane token registry with revocation + audit — the v1 model uses a single
ENERGY_LAB_TOKENenv var. v2 landslab_credentialstable with per-lab tokens, expiry, and revocation. - Operator seal workflow — a
LabSubmissionSealSheetco-resident on the summit drill-in that lets the operator step through pending submissions and sealconfirmorrefuteagainst the criterion. The substrate path is already in place viaEnergySummitReader.transitionSummit(_:to:causedByRef:).
© 2026 Richard Gillespie. All rights reserved. USPTO patent applications 19/460,960 and 19/096,071.
c295e6eac8e917a5e87624824b06372a28fad6033b77d3113f6c8583d82fa116.
This page serves with a substrate-honest pending-signature notice until the operator's Franklin signer cosigns it.