/** * Shared emitter for `assertion_activity` notification rows (ADR-002, * notifications-pane redesign). * * Activity (assertion created / promoted / published) is recorded as a * first-class notification row at WRITE TIME, where the actor is a member of * the context graph by construction — so the row is born membership-scoped * with no read-time join and no per-CG SPARQL fan-out. Three call sites use * this one helper so the row shape stays identical across them: * - `routes/assertion.ts` → `created`, `promoted` (local writes) * - `routes/memory.ts` → `published` (local SWM→VM publish) * - `daemon/lifecycle.ts` → `published` (REMOTE-ONLY, membership-gated) * for cross-node activity gossiped/synced from a collaborator's node. * * What's stored vs derived: * - The ATOMIC row carries the single actor that produced it * (`actorAgentDid`) + the lifecycle `kind` + raw counts. One row per * event. * - `count`, `soleAuthor`, and display names (`contextGraphName`, * `actorAgentName`) are DERIVED on read by the scoped digest-collapse * in `GET /api/notifications` (A4) — they are NOT stored here. This * keeps the persisted row truthful (one event = one row) and lets the * read path group `(contextGraphId, kind, windowBucket)` and decide * self-suppression/sole-author against the *reading* agent. */ import type { DashboardDB } from '@origintrail-official/dkg-node-ui'; import { type AssertionActivityKind } from '@origintrail-official/dkg-node-ui'; /** * Cheap local membership gate for the cross-node (gossip) activity emitter * (CR-2). A REMOTE publish overheard via gossip should only become an * activity row when this node is genuinely involved in the CG — not for * every CG it merely overhears. "Involved" = the local membership cache has * at least one ACTIVE membership row for the CG (this node and/or a local * agent participant). This is an O(rows-for-cg) read against the indexed * `context_graph_memberships` table, not a SPARQL fan-out. * * Defense-in-depth only: `GET /api/notifications` (A4) re-filters every row * against the *caller's* member set, so a too-permissive gate here cannot * leak a row to a non-member reader — it would just store an unread row that * the scoped read drops. We still gate to keep the table from accumulating * activity for unrelated CGs. */ export declare function localNodeInvolvedInContextGraph(dashDb: Pick, contextGraphId: string): boolean; /** * Coerce an actor identity (an EVM address, a bare `did:dkg:agent:…` DID, or * a peer-id-based DID) into the canonical `did:dkg:agent:` form the rest * of the UI resolves agent profiles against. Returns undefined for an * empty/whitespace actor so the caller can omit the field. */ export declare function toActorAgentDid(actor: string | null | undefined): string | undefined; export interface AssertionActivityInput { contextGraphId: string; kind: AssertionActivityKind; /** Actor EVM address or DID. Omitted-resolution → row with null actor. */ actorAgentAddress?: string | null; /** Sub-graph slug if the assertion is sub-graph-scoped. */ subGraphName?: string; /** Triples written/published, when known. */ tripleCount?: number; /** Root entities in a promoted/published bundle, when known. */ entityCount?: number; } /** * Build + insert one atomic `assertion_activity` notification row. Resilient: * a malformed `contextGraphId` is a no-op (returns null) and any DB error is * swallowed by the caller's surrounding try/catch — activity notifications * must never break a write/publish/gossip path. * * Returns the inserted row id, or null when nothing was written. */ export declare function recordAssertionActivity(dashDb: Pick, input: AssertionActivityInput): number | null; //# sourceMappingURL=activity-notification.d.ts.map