Skip to content

Self-signal registry: a shared type for declaring recurring self-generated signals, so measurements can subtract their own infrastructure #16

Description

@iceteaSA

Problem — three incidents, one mechanism

Any measurement over a shared surface risks reading self-generated signal as external behavior. Three corroborating incidents on this box, all within days:

  1. A boundary-aligned Claude keepalive was read as API window mechanics in a ledger analysis. The confound survived a day because the keepalive is event-anchored (fires at window-expiry+ε), so it reproduced the surface's own boundary grid exactly. Diagnosed only after a maintainer-side retraction forced a re-measurement (insula#5 thread, 2026-08-17).
  2. A mechanical board-sweep nearly consumed its own filings as deltas — self-caused changes reading as external novelty; sequencing had to be hand-ordered.
  3. insula#1 GAP-1's two-reader OAuth token collision: rotation-on-use is a self-signal on the credential surface that a second reader misread.

The current defense is doctrine ("enumerate the background processes shaping your own series before reading mechanics off it"). Doctrine doesn't scale past the people who remember the incidents.

Proposal

A small shared type in commons + a per-box machine-readable JSONC file. Durable signals only (keepalives, pollers, crons, sweeps, watchdogs); ephemerals (per-session watchers) explicitly out of scope at v1.

{
  "signal_id": "claude-prime-keepalive",
  "owner": "homelab",
  "kind": "keepalive",                       // keepalive|poller|cron|sweep|watchdog|heartbeat
  "surface": { "domain": "provider-usage", "provider": "claude", "account": "..." },
  "cadence": { "kind": "event-anchored", "anchor": "window-expiry" },
  //           ^ discriminated: {kind: "wall-clock", spec: "cron..."} | {kind: "event-anchored", anchor: ...}
  "since_ms": 0, "last_confirmed_ms": 0
}

Two shape decisions learned from the incidents, not aesthetics:

  • cadence is a discriminated type. The founding confound was event-anchored; free-form cadence text lets a consult catch crons and miss exactly the signal that caused the incident. With kind=event-anchored, anchor=window-expiry, the misread analysis would have matched the registration mechanically.
  • surface is a controlled tuple (domain, provider?, account?), not free text — required so measurement tools can auto-match registrations to what they're measuring.

Contract, two duties: anything generating recurring traffic against a measurable surface registers (one row); any measurement over a shared surface consults — subtracts or annotates matching registrations, and its report carries a greppable self-signals considered: [ids | none-registered] line (tool-enforced where the tool is ours; convention elsewhere). The registry is descriptive, not authoritative: absence of a registration is itself a finding — it shrinks the search space, it cannot close it.

Inventory reality check: enumeration on this box found 10-13 durable rows (keepalive, ledger-poll timers, dispatch-time quota refresh — a self-signal on our own measurement, board-sweeps, nightly consolidation passes, backup crons, gateway idle-shed respawns, OAuth rotation-on-use), so this is not a 2-row hypothetical.

Why commons and a file, not a daemon op

Considered and rejected placing it on the subc daemon (ck signals): the daemon is itself a signal source (health-probe cadence + self-watchdog are textbook instrumentation-pollutes-measurement rows) and a registry hosted inside one of its sources can't be neutral about it; most registrants aren't subc modules and shouldn't need a daemon connection to declare a cron; and wire surface is expensive to ever remove while a file is revocable in an afternoon. Revisit trigger, stated so this is a decision rather than a deferral: move it on-daemon only when a second live consumer exists AND the daemon needs to consult the registry inside its own decision path.

Disposition ask

Is a cortexkit-self-signals type crate (schema + serde + the consult-line convention) acceptable in commons? First consumer is filed separately on insula (ledger analytics annotation). If you'd rather this stay a downstream convention without a commons type, that's a legitimate answer — the contract matters more than the transport, and we'd keep the file format stable ourselves.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions