When a community corpus accepts a feature, it implicitly vouches that the feature means the same thing across sessions, devices, and protocols. We audited every feature in the SDK — the 187-dimension framework and the 85-dimension physical-primitive vector — against three gates: invariance census, protocol confounds, and compute-robustness on real windows. 98 of 272 features (36%) pass all three. This essay is that whitelist and the reasoning behind it.
The Three Gates
- Invariance census — what the feature cancels (gain, scale) versus what it carries (device constants, calibration need). From the framework's labels: 93 DEV_BOUND, 46 INV_GAIN, 6 INV_SCALE, 36 NONE, 6 CALIBRATED, inside a 187-dim vector.
- Protocol confound — does the feature's meaning depend on flow rate, dead volume, purge protocol, or baseline→exposure→recovery discipline? Everything kinetic does.
- Compute robustness — is the feature actually defined on real windows (nan_rate = 0)? We checked on 24 live SmellNet windows.
The Counts
| Vector | Transferable | Device-bound | Needs calibration | Protocol-confounded | Total |
|---|---|---|---|---|---|
| Framework (187-dim) | 34 | 39 | 42 | 72 | 187 |
| Primitives (85-dim) | 67 | 6 | 0 | 12 | 85 |
| Total | 101 | 45 | 42 | 84 | 272 |
Intersect with "fully-defined on real windows" and the deployable whitelist is 98 features:
| Survivor category | Framework | Primitives | Why it survives |
|---|---|---|---|
Dose & amplitude (_da_ ratio family) | 22 | 12 | Ratios cancel gain; only sensitivity constant survives |
| Direction & selectivity (incl. log-ratio, covariance) | 21 | 55 | Fingerprint-like; robust to absolute offsets |
| Saturation / nonlinearity | 6 | 0 | Shape feature, gauge-cancelling |
| Survivors total | 49 | 67 | — |
What Must Never Be Shared Raw
- The 84 protocol-confounded features — every rise/decay/recovery/kinetic feature (54 framework + 12 primitives) plus dynamics features. Their value depends on the exact flow profile of the rig that recorded them. Sharing them raw is sharing a flow-rate, not a smell.
- The 45 device-bound features — health/hardware probes that mean "this specific unit," not the environment.
- The 42 calibration-bound features — absolute resistance, voltage, circuit/thermal/ADC probes, calibrated ppm. Meaningful only with per-device calibration metadata alongside.
What This Means for the Data Commons
The commons schema should do two things automatically:
- Allow the 98 — the
_da_amplitude/dose family, selectivity ratios, saturation index, and thephys_normalized/direction/covariance primitives. These are the honest, comparable subspace of the SDK. - Reject or require-tags on the rest — kinetic features may be uploaded, but only with protocol metadata (flow, dead volume, purge) attached, and never into the interoperable comparison pool.
This is the difference between a data dump and a data commons: not more columns, but honest columns. A corpus that accepts all 272 features raw would be training models on device identity and flow rates while believing it is learning chemistry. The 98-feature core fixes that at the schema boundary, where it is cheapest.
Sources & Further Reading
benchmarks/bench_features_audit.py+reports/bench_features_audit.json— the full audit, category rollups, and compute-robustness census.features/existing_mapping.py— the invariance labels (INV_GAIN/INV_SCALE/DEV_BOUND/NONE/CALIBRATED).features/physical_features.py— the 85-dimphys_primitive vector.- The 187-dimensions essay — what every framework dimension is and where it lives.
- The anomaly-first essay — why deploying this core now is the strategy, with interoperability as the compounding step.
