@nexart/signals
v0.1.0
Published
Minimal, protocol-agnostic signal capture SDK for optional CER context evidence
Maintainers
Readme
@nexart/signals v0.1.0
Minimal, protocol-agnostic signal capture SDK.
Captures and normalizes structured upstream signals so they can be bound into Certified Execution Records (CERs) as optional context evidence.
- Does not define governance semantics or enforce policy
- Does not interpret the meaning of signals
- Validates structure only and provides normalization helpers
- Fully optional and independent of
@nexart/ai-execution
Install
npm install @nexart/signalsQuick start
import { createSignal, createSignalCollector } from '@nexart/signals';
// ── Standalone signal ──────────────────────────────────────────────────────
const signal = createSignal({
type: 'approval',
source: 'github-actions',
actor: 'ci-bot',
status: 'ok',
payload: { pr: 42, approved_by: 'alice' },
});
// {
// type: 'approval',
// source: 'github-actions',
// step: 0,
// timestamp: '2026-...',
// actor: 'ci-bot',
// status: 'ok',
// payload: { pr: 42, approved_by: 'alice' }
// }
// ── Collected signals ──────────────────────────────────────────────────────
const collector = createSignalCollector({ defaultSource: 'my-pipeline' });
collector.add({ type: 'fetch', source: 'my-pipeline', payload: { url: '...' } });
collector.add({ type: 'transform', source: 'my-pipeline', actor: 'etl-bot' });
collector.add({ type: 'store', source: 'my-pipeline', status: 'ok' });
const collection = collector.export();
// {
// signals: [
// { type: 'fetch', step: 0, ... },
// { type: 'transform', step: 1, ... },
// { type: 'store', step: 2, ... },
// ],
// count: 3,
// exportedAt: '2026-...'
// }Signal shape
Every NexArtSignal has exactly these fields — all always present, no undefined values:
| Field | Type | Default | Description |
|---|---|---|---|
| type | string | required | Signal category — free-form (e.g. "approval", "deploy", "audit") |
| source | string | required | Upstream system or protocol — free-form (e.g. "github-actions", "linear") |
| step | number | 0 / auto | Position in sequence. Auto-assigned in insertion order by the collector |
| timestamp | string | current time | ISO 8601 |
| actor | string | "unknown" | Who produced this signal — free-form |
| status | string | "ok" | Outcome — free-form (e.g. "ok", "error", "pending", "skipped") |
| payload | Record<string, unknown> | {} | Opaque upstream data — NexArt does not interpret this |
API
createSignal(input)
Creates a single normalized signal. type and source are required. All other fields are optional with safe defaults.
const signal = createSignal({
type: 'review',
source: 'linear',
step: 2,
actor: 'alice',
status: 'ok',
payload: { issue: 'NX-123', verdict: 'approved' },
});createSignalCollector(options?)
Creates an ordered signal buffer. Returns a SignalCollector with three members:
collector.add(input)
Adds a signal. Returns the normalized NexArtSignal that was stored.
- If
stepis omitted, it is auto-assigned using the insertion index (0, 1, 2, …) - If
stepis explicitly provided, that value is used and the signal is sorted correctly on export
collector.export()
Returns a SignalCollection:
interface SignalCollection {
signals: NexArtSignal[]; // sorted by step (ascending)
count: number;
exportedAt: string; // ISO 8601, time of export() call
}Non-destructive — calling export() multiple times is safe. The internal buffer is not cleared.
collector.size
Returns the current number of collected signals.
Collector options
interface CollectorOptions {
defaultSource?: string; // applied when signal source is empty/omitted
defaultActor?: string; // applied when signal actor is omitted
}Step ordering
Signals without an explicit step get steps assigned in insertion order:
collector.add({ type: 'a', source: 's' }); // step = 0
collector.add({ type: 'b', source: 's' }); // step = 1
collector.add({ type: 'c', source: 's' }); // step = 2Signals with an explicit step are sorted by that value on export, regardless of insertion order:
collector.add({ type: 'deploy', source: 's', step: 10 });
collector.add({ type: 'build', source: 's', step: 0 });
collector.add({ type: 'test', source: 's', step: 5 });
collector.export().signals.map(s => s.type);
// ['build', 'test', 'deploy']Determinism
Pin timestamp and step for deterministic output that can be hashed:
const signal = createSignal({
type: 'approval',
source: 'ci',
step: 0,
timestamp: '2026-03-17T00:00:00.000Z', // pinned
actor: 'bot',
payload: { pr: 42 },
});
// same input → identical object every timeIntegration with @nexart/ai-execution (v0.10.0+)
NexArtSignal[] is structurally identical to CerContextSignal[] in @nexart/ai-execution — no casting or conversion needed. Pass collection.signals directly to any certify call and it will be sealed into the certificateHash alongside the execution record.
import { createSignalCollector } from '@nexart/signals';
import { certifyDecision, certifyLangChainRun, verifyCer } from '@nexart/ai-execution';
const collector = createSignalCollector({ defaultSource: 'github-actions' });
collector.add({ type: 'approval', actor: 'alice', status: 'ok', payload: { pr: 42 } });
collector.add({ type: 'deploy', actor: 'ci-bot', status: 'ok', payload: { env: 'prod' } });
const { signals } = collector.export();
// ── certifyDecision ──────────────────────────────────────────
const bundle = certifyDecision({
provider: 'openai',
model: 'gpt-4o-mini',
prompt: 'Summarise.',
input: userQuery,
output: llmResponse,
parameters: { temperature: 0, maxTokens: 512, topP: null, seed: null },
signals, // ← sealed into certificateHash; omit = identical hash as before
});
verifyCer(bundle).ok; // true
bundle.context?.signals.length; // 2
// ── certifyLangChainRun ──────────────────────────────────────
const { bundle: lcBundle } = certifyLangChainRun({
provider: 'openai',
model: 'gpt-4o-mini',
input: { messages: [{ role: 'user', content: 'Summarise.' }] },
output: { text: 'Summary...' },
signals, // ← same field, same semantics
});Signals are evidence-only — they do not affect execution behavior or parameters. See @nexart/ai-execution README for the full CerContextSignal shape and tamper-detection guarantees.
Version history
| Version | Description |
|---|---|
| v0.1.0 | Initial release: createSignal, createSignalCollector, NexArtSignal, SignalCollection, CollectorOptions, SignalCollector types |
