Skip to content

Make SignalKind own one canonical stable domain identity #481

Description

@forkwright

Finding

stoicheion::SignalKind is the canonical seven-domain signal type, but Semaino independently defines its domain identity three times:

  1. alert.rs maps each variant to a numeric fingerprint discriminant.
  2. convergence.rs repeats that numeric mapping and separately fixes storage at eight slots.
  3. aggregator.rs defines a parallel KindKey enum for baseline ownership.

The first two copies assign known domains 0..=6 and every future variant 255. The aggregator instead folds Osint and every future variant into KindKey::Osint. Because SignalKind is #[non_exhaustive], adding a new domain does not force any of these downstream mappings to change.

Verified against main c8e671845adca3e5b57a205d08aca31eadbc6000.

Evidence

  • crates/stoicheion/src/signal.rs:237-255 defines the authoritative SignalKind variants: RF, mesh, network, proximity, GPS, environmental, and OSINT.
  • crates/semaino/src/alert.rs:314-326 defines private kind_discriminant: known variants map to 0..=6; the wildcard maps to 255. That value participates in AlertFingerprint, so it controls alert suppression identity.
  • crates/semaino/src/convergence.rs:92-108 declares DOMAIN_SLOTS = 8, reserving indices 0..=6 plus one shared future-variant slot. convergence.rs:295-310 repeats the alert mapping independently.
  • crates/semaino/src/aggregator.rs:52-82 defines a separate seven-variant KindKey. Its wildcard maps both current Osint and every future SignalKind variant to Osint.
  • No current open issue owns the domain-identity fork. Bound and scope the convergence grid: per-cell hits and per-signal detection grow unbounded under signal flood #223 bounded convergence storage; security(signals): enforce typed sensitivity and minimized evidence at the canonical pipeline #406 concerns signal sensitivity, not domain enumeration ownership.

Why this matters

These identities govern three different semantics: which signals share a temporal baseline, which domains count as distinct convergence evidence, and which alerts suppress one another. A newly added domain can therefore:

  • contaminate the OSINT baseline;
  • collide with every other future domain in convergence;
  • collide with every other future domain in alert suppression; and
  • acquire different identities in different subsystems without a compiler failure.

This is not a trust-boundary mirror. All three consumers are interpreting the same in-process SignalKind fact.

Desired correction

Make the defining crate own one domain identity. A suitable shape is a SignalDomain enum plus SignalKind::domain() implemented inside stoicheion, where adding a SignalKind variant makes an exhaustive match fail to compile until its domain is assigned. If a stable numeric code is required for fingerprints or storage, make that a method of SignalDomain rather than another matcher.

Have Semaino baseline keys, convergence slots, and alert fingerprints consume that type/code. Derive bounded storage cardinality from the canonical domain set or use a keyed structure that does not require a second maintained slot count.

Done when:

  • one implementation maps SignalKind to domain identity;
  • adding a new SignalKind cannot compile until its domain identity is decided;
  • future domains cannot silently alias OSINT or a shared sentinel;
  • alert, convergence, and baseline tests use one shared domain table; and
  • no private seven-way SignalKind domain matcher remains in Semaino.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions