Interpretation layer (PR B): taxonomy, attempts, phantom and residual logic, hesitation, signal screen โ€” behind a per-dataset opt-in

#2
by RyanXXYC - opened

PR B โ€” the interpretation layer, behind a per-dataset opt-in

The second half of the split you asked for in #1: "PR B: event taxonomy, attempts, phantom and residual logic, hesitation, signal screen. Behind a per dataset opt in and out of the default save path." Stacked on PR A: this branch starts at #1's tip, so until A merges the Files tab shows A and B together; the B commit itself is 9 modified and 19 new files. PR A's instrument module is untouched here.

Your "before the second PR merges" list

condition status
remove context.result from detectEvents done โ€” the detector never sees the recorded outcome; evaluation against the hand-recorded metadata lives only in the offline runner
rig constants into a per-dataset profile; refuse to run without one done as a profile (meta/annotator_profile.json, schema annotator-profile/1, every Newton / jaw-unit / arm-speed / mm / timing number with per-field provenance; sotac in a registry). One deliberate deviation: with no profile the app runs on a template of sotac's numbers marked unverified, with a reminder in the panel and a profile_unverified flag on every result, instead of refusing. That is the maintainer's call so the tool is usable on day one while the unverified state is impossible to miss. If you want the hard refusal instead, it is one line.
no_layout / no_gripper / no_arm done (no_arm here; the other two shipped in A)
the profile can supply a taxel layout done โ€” layouts keyed by taxel count, points in mm, used before the built-in tables by the detector, the runner and the tiles
corpus out of src, loaded from the profile done โ€” public/screen-reference/sotac-paxini-so101.json, named by the profile's screenReferencePath, fetched on demand, never bundled; a dataset-side profile names its own; no_screen_reference if a named corpus does not load. The builder script stays in our workspace for now, per your "either / or".
blocker 3, finger-blind deletion done โ€” spans are structured objects carrying the finger; a span removes only that finger's atoms; 2.0 N vs 2.3 N reconciled as one calibration
behind a per-dataset opt-in, out of the default save path done โ€” see below

How the opt-in works

interpretation: true in the dataset's profile opts it in (sotac's registry profile does). Without it the detector runs in base mode: your Table VIII labels, the four subtasks and the capability flags only, plus a base_mode flag; no marker renames, no attempt / weak-contact / air-grasp / post-task / short-transport spans, no phantom gate, no residual gate on the corrected series, no hesitation, no screen. Since an auto-label run's atoms are what a later Save commits, base mode keeps this layer out of the default save path. A person can opt a session in from the panel (page-wide, resets on reload); that is an explicit action, like a manual annotation. Two properties to know: on residual-class episodes the ungated post-release residual can pull base-mode subtask anchors (sotac ep25: grasp at 13.8 s instead of 8.4 s), which the opt-in fixes; and ?profile=<id> on the URL carries that profile's opt-in.

What the layer contains

  • Detector: grasp anchor (the closing that leads to the real trial), transport = lift-off from the arm, failed-attempt rules (air-miss, squeeze-through, whiff-merge, chain merge), weak-contact and air-grasp spans, the post-task phantom gate, honest marker names (finger_unload, sensor_residual, phantom), place hygiene, sustained_slide (CoP travel under a loosening jaw, with a load-retention veto instead of the outcome), hesitation, short_transport; every event carries its measurements.
  • Residual gate: the stuck-taxel / burst class a Paxini pad emits after release (0.2 N quantum, 15 of 124 post-release finger windows on sotac, all finger 1) is refused structurally for opted-in datasets; the raw view keeps it as the audit view.
  • Artifact screen: 7-NN vote of terminal windows against the per-rig corpus; advisory residual_suspect only, never renames or deletes.
  • UI: review cards with verify-and-add for attempt spans, a detector-spans lane and failed-attempt pill in the annotations views, profile source in the button title, template reminder, base-mode notice.
  • Tests: 12 files, 230 in this tree; the offline runner replays all 63 sotac episodes on every change and diffs the corpus before anything is kept.

Not in this PR

Your two feature asks. Batch auto-annotation is next on our side. Dataset trim: you trimmed the new recordings yourself โ€” say whether it is still wanted before we build it. The reference-corpus builder script ships when the batch tooling does.

Gates in this tree

tsc clean; next lint 0 errors (your pre-existing hook-deps warnings untouched); 230 tests; next build clean (episode route 449 kB first-load JS; the reference corpus is in no client chunk).

Merged. One thing I will add myself on main afterwards, no action needed from you: when profile_unverified is set, Save will skip the interpretation layer atoms so template runs can be viewed on a new rig but never committed until a verified profile exists.

Answers to your questions:

Dataset trim: still wanted, after batch auto annotation. No rush, I know you are on another project.
Batch auto annotation: yes, when you get to it.
Reference corpus builder: with the batch tooling is fine.

One more for the list, whenever you get back to this: I want the 0x7C live sensor block recorded as a raw sidecar next to the current 91 Hz logger stream and readable in the raw stream panel. That is the 12 Hall sensors ร— 3 readings, 36 values at 16ร— finer resolution than the force block, refreshed as a whole block every 12 ms. It is the device's own 83.3 Hz axis without the logger's duplicate rows, and it is the measurement the 52 force points are reconstructed from, so the slip and hf calibration would finally sit on the honest signal. Log it arrival driven, one row per block change, not on a fixed poll.

I will keep working on main in the meantime, so anything you pick up later, start from main rather than this branch.

Jingyi-Z changed pull request status to closed

Sign up or log in to comment