Skip to content

Spec: Implicit Gaze Calibration — Click Association, Held-Out Promotion and Topology Safety

Field Value
ID spec-eye-implicit-calibration
Status Proposed
Date 2026-09-22
Modules src/yazses/gaze/implicit.py, store/runtime click observer
Related ADR-v2-010, ADR-v2-149, ADR-014
Issues #397–#399 + topology/HiDPI task

Goal

Finish the runtime half of implicit gaze calibration while guaranteeing that passive learning cannot silently replace a better explicit calibration or survive an incompatible desktop topology.

Existing core

gaze/implicit.py already provides the incremental estimator and refined_if_better held-out comparison.

This spec covers missing runtime acquisition, persistence, topology context and promotion.

Non-goals

  • no raw click text/window content;
  • no screenshots;
  • no frame storage;
  • no cloud learning;
  • no automatic caret targeting;
  • no unconditional online replacement of the baseline.

Sample association

A candidate sample is:

(timestamped gaze features, confidence)
       +
(timestamped user pointer click x/y)
       ->
calibration observation

The click observer is platform-neutral at the estimator boundary.

Association rules: - choose a gaze observation within a documented look-back/association window; - reject when no fresh observation exists; - reject below confidence threshold; - reject implausible residual/outlier per existing core policy; - record only derived numeric observation needed by estimator.

The exact latency offset should be configurable/research-measurable rather than hard-coded forever.

Sample buffers

Separate: - training/update samples/state; - held-out validation samples.

A sample used to fit a candidate is not simultaneously evidence that the candidate improved.

Use bounded state/history consistent with existing incremental design and privacy goals.

Baseline and candidate

  • baseline = explicit calibration currently accepted for this calibration context;
  • candidate = implicit refinement.

Candidate may become active only when refined_if_better/equivalent held-out gate wins by the defined margin/sample requirement.

Persistence

Calibration artifact includes: - map coefficients; - source: explicit / implicitly refined; - version; - timestamp; - ADR-v2-149 context fingerprint.

Write atomically.

A failed candidate leaves the prior artifact exactly valid.

Keep enough metadata to report/rollback the previous accepted map if rollback is a supported command.

Context/topology

Before applying stored calibration: - compare current display/camera context to saved fingerprint; - incompatible context -> mark stale, do not route with it; - topology change during session -> suspend gaze routing until refreshed/revalidated.

Tests cover: - single monitor 1.0 scale; - 2x/HiDPI; - negative-origin second monitor; - changed arrangement/resolution; - changed camera identifier.

User control

Implicit refinement is opt-in.

Status reports: - baseline source; - candidate sample count; - last evaluation result; - stale/valid reason; - no raw personal data.

A user can disable refinement without deleting explicit calibration.

Acceptance criteria

  • Low confidence click pair is rejected.
  • Stale gaze sample is rejected.
  • Training sample does not leak into holdout verdict.
  • Worse candidate cannot replace baseline.
  • Better candidate promotes atomically.
  • Persistence failure leaves baseline valid.
  • Incompatible topology refuses stored map.
  • Same compatible topology reuses map.
  • Report/export contains derived metrics only.

Tests

  • existing tests/test_gaze_implicit.py;
  • fake click observer;
  • fake clock/ring buffer;
  • atomic store failure;
  • topology fixtures;
  • rollback;
  • privacy schema golden test.

Field evaluation

Report: - explicit baseline error; - candidate held-out error; - sample count/time; - drift over session; - dock/undock/camera change outcome.

No promotion to recommended implicit calibration without longitudinal field evidence.