@real-signal/attention-ethics
v0.1.0
Published
The seven-gate cascade for AI restraint — a pure-function library any LLM, MCP server, or agent can install to decide whether speaking is warranted. Zero dependencies. The engineering primitive behind Real Signal's silence-correctness metric.
Downloads
72
Maintainers
Readme
@real-signal/attention-ethics
The seven-gate cascade as a pure-function library. v0.1.0 — 2026-06-10.
A zero-dependency JavaScript library any AI system, MCP server, or
chatbot can install to decide whether speaking is warranted. The
library does not look at content. It looks at the structure around
the candidate emission — place, time, person, relevance,
effort — plus two upstream gates that ask whether the moment itself
is worth breaking. If any gate refuses, the cascade returns
{ emit: false, gate, reason }. Silence is the default; speaking is
earned.
This is the engineering primitive Real Signal uses inside its own notification pipeline, extracted as a reusable library. Adopters bring their own substrate (their own resonance engine, their own fatigue model, their own place identifiers); the library provides the doctrine.
Doctrine in 200 words
Real Signal is built around a single observation: most consumer AI products optimise for engagement (notification frequency, dwell, daily active use). The inverse — cognitive load going down — is the property most users actually want from the systems sharing their attention.
The Attention Ethics layer is the runtime expression of that inversion. Every candidate emission must answer seven questions before it is permitted to surface:
0a. Resonance — does the substrate itself say to speak? 0b. Moment silence — is this moment worth breaking at all?
- Place — is the emission grounded in a specific scope?
- Time — is there a real temporal anchor?
- Person — is there a specific receiver in a receptive state?
- Relevance — does the candidate clear the compound bar?
- Effort — is the action one the receiver can take calmly?
Any gate that refuses ends the cascade and records the reason. The default is silence. The cascade is pure compute — no I/O, no persistence, no hidden network calls. The library is the structure; adopters bring the substrate.
The longer-form articulation lives in the preprint at real-signal.ai/research/attention-ethics-layer.md.
Install
npm install @real-signal/attention-ethicsThe package has zero runtime dependencies. The only dev dependency is vitest, used for the test suite.
Quickstart
import { runGates } from '@real-signal/attention-ethics'
const candidate = {
place_id: 'cluny', // gate 1: place
timing_signal: 'afternoon_lull', // gate 2: time
recipient: { user_id: 'u-001' }, // gate 3: person
relevance_score: 72, // gate 4: relevance (0-100)
action: 'tap to read more', // gate 5: effort
}
const context = {
// Optional. When you have your own resonance engine.
resonance: { score: 0.8, should_surface: true },
// Optional. When you have composed a moment.
moment: {
should_stay_silent: false,
signal_saturation: 0.2,
attention_density: 0.7,
primary_state: 'calm',
},
// Optional. Per-recipient fatigue from your own model.
fatigue: { score: 0.1, in_quiet_hour: false },
// Optional. Environmental modifiers used by the relevance gate.
attention_density: 0.7,
signal_saturation: 0.2,
}
const result = runGates(candidate, context)
// → { emit: true, trace: [ ... ] }
// or
// → { emit: false, gate: 'place', reason: '...', trace: [ ... ] }The cascade short-circuits on the first refusal. When emit is false,
gate names the refusing gate and reason carries a machine-readable
explanation. trace always contains the per-gate results up to and
including the refusing gate.
Mapping gates to the preprint
| Gate | Preprint section |
|-------------------|------------------------------------------------------------------------|
| resonance | §4.2 Resonance as a precondition |
| moment_silence | §4.3 Moment-level silence |
| place | §4.4 Grounding signals in place |
| time | §4.5 Temporal specificity |
| person | §4.6 Per-person earned interruption |
| relevance | §4.7 Earned interruption as compound thresholds |
| effort | §4.8 Action friction as a relevance modifier |
Each gate has a corresponding JSDoc comment in
src/gates/ linking back to its preprint section. If
the preprint URL fragment moves, the in-file comment is the canonical
reference; the table above is a courtesy.
Voice lock (bonus export)
The library also exposes Real Signal's voice-lock validator as a pure function. The cascade decides whether to speak; the voice-lock checks how the candidate is phrased. Both layers are independent and either can be used without the other.
import { validateTierVoice } from '@real-signal/attention-ethics'
validateTierVoice('cluny holds 18 quiet seats through 4pm', 'atmosphere')
// → { ok: true }
validateTierVoice('amazing deal — hurry!', 'atmosphere')
// → { ok: false, reason: 'banned-all-tiers pattern: \\bamazing\\b' }The validator catches common bypass attempts: bold-math letters
(𝓪𝓶𝓪𝔃𝓲𝓷𝓰), spaced-out words (a m a z i n g), zero-width
characters between letters, and full-width punctuation. Adversarial
inputs are normalised with NFKC before scanning.
Calibrating to your domain
Every numeric threshold the cascade reads is configurable:
runGates(candidate, context, {
resonance: { threshold: 0.6, required: false },
place: { wildcardValues: ['all', 'global', 'unknown'] },
time: { validSignals: ['lunch', 'evening'], minFreeTextLength: 6 },
person: { fatigueRefuseAt: 0.9, refuseInQuietHour: true },
relevance: { baseThreshold: 50 },
effort: { maxLength: 280 },
order: ['place', 'time', 'relevance'], // run a subset
})The defaults are the values Real Signal uses internally for one-pocket operation. They may not transfer to your domain — calibration is the adopter's responsibility. The library cannot make that call for you; it can only make the structure available.
Design choices
- Zero dependencies. The library imports nothing at runtime. Adopters do not pull in Supabase, an HTTP client, or any Real Signal-specific code.
- Pure functions. No I/O. No persistence. No hidden telemetry. Same input always yields the same output.
- Fail closed. Bad input does not throw — it produces
emit: falsewith a reason that names the failure. Silence is the safe default. - Short-circuit. The first refusing gate ends the cascade. Later gates do not run; latency stays predictable.
- Substrate-agnostic. Your fatigue model, your resonance engine, your place identifiers. The library reads the shape; you produce the values.
Attribution
If your AI system uses this library to govern its output, attribute Real Signal in your documentation (one line linking to real-signal.ai is enough) — the library is the engineering primitive; the doctrine that named it lives there.
This requirement is reasonable to ask in good faith and is not legally binding under the MIT license below. The cascade itself has no telemetry and does not phone home; the request is purely about citation hygiene as the category forms.
License
MIT — see LICENSE.
The MIT terms are clean: fork it, modify it, embed it in commercial products. The attribution paragraph above lives here in the README rather than in the LICENSE file so automated license detection (GitHub, npm, license scanners) reads this package as plain MIT.
Status
- v0.1.0 — initial release. The seven gates + voice-lock, 47 tests, examples, zero dependencies. Ready for adoption.
- Production use — the same cascade structure runs Real Signal's notification pipeline today. This library is the extracted form.
- Publication status — scaffold-ready for
npm publish, pending the founder's authenticated publish from their machine. The npm scope@real-signalis reserved for Real Signal Research.
Changelog
See CHANGELOG.md.
Source
Canonical repository (pending extraction): github.com/real-signal/attention-ethics.
Until extraction, the development history lives inside the Real Signal
monorepo at external/attention-ethics/.
