Event schema reference

EventType — all 26 event types

export type EventType =
  // Claim lifecycle
  | "sentence_first_seen"
  | "sentence_removed"
  | "sentence_modified"
  | "sentence_reintroduced"

  // Citation changes
  | "citation_added"
  | "citation_removed"
  | "citation_replaced"

  // Template changes
  | "template_added"
  | "template_removed"
  | "template_parameter_changed"

  // Section & page structure
  | "section_reorganized"
  | "lead_promotion"
  | "lead_demotion"
  | "page_moved"

  // Links & categories
  | "wikilink_added"
  | "wikilink_removed"
  | "category_added"
  | "category_removed"

  // Access control
  | "protection_changed"

  // Content conflict
  | "revert_detected"
  | "edit_cluster_detected"

  // Talk page correlation
  | "talk_page_correlated"
  | "talk_thread_opened"
  | "talk_thread_archived"
  | "talk_reply_added"
  | "talk_activity_spike";

Core interface: EvidenceEvent

All events share a common envelope with before/after snapshots and deterministic facts. There are no discriminated subtypes — all fields are optional at the envelope level:

export interface EvidenceEvent {
  eventId?: string;                              // deterministic content hash (see below)
  eventType: EventType;                          // discriminator
  claimId?: string;                              // claim identity hash, when applicable
  fromRevisionId: number;                        // parent revision
  toRevisionId: number;                          // source revision
  section: string;                               // section title where change occurred
  before: string;                                // text / state before the change
  after: string;                                 // text / state after the change
  deterministicFacts: DeterministicFact[];       // facts backing this event
  modelInterpretation?: ModelInterpretation;     // set by downstream consumers
  layer: EvidenceLayer;                          // provenance layer
  timestamp: string;                             // ISO 8601
}

Supporting types

export type EvidenceLayer =
  | "observed"              // directly observed from the diff
  | "policy_coded"          // coded against a policy rule
  | "model_interpretation"  // set by downstream consumers
  | "speculative"           // inferred but without a hard rule
  | "unknown";              // layer not yet classified

export interface DeterministicFact {
  fact: string;
  detail?: string;
  provenance?: FactProvenance;
}

export interface FactProvenance {
  analyzer: string;        // analyzer that produced this fact
  version: string;         // analyzer version
  inputHashes: string[];   // hashes of input data used
  parameters?: Record<string, string | number | boolean>;  // parameters used by the analyzer
}

export interface ModelInterpretation {
  semanticChange: string;
  confidence: number;
  policyDimension?: PolicyDimension;
  discussionType?:
    | "notability_challenge"
    | "sourcing_dispute"
    | "neutrality_concern"
    | "content_deletion"
    | "content_addition"
    | "naming_dispute"
    | "procedural"
    | "other";
}

export type PolicyDimension =
  | "verifiability"
  | "npov"
  | "blp"
  | "due_weight"
  | "protection"
  | "edit_warring"
  | "notability"
  | "copyright"
  | "civility";

ModelInterpretation and modelInterpretation on EvidenceEvent are never set by Refract's deterministic pipeline — they exist for downstream consumers to attach semantic analysis without modifying the deterministic event.

AnalyzerConfig is a supporting type exported from @refract-org/evidence-graph that defines configurable parameters for analyzers (similarity thresholds, time windows, revert patterns). See the CLI reference for the per-flag equivalents.

Deterministic identity

eventId is derived deterministically from the event content so the same observation always produces the same ID:

modelInterpretation is excluded from the hash so downstream consumers can add it without changing the event's deterministic identity.

import { createHash } from "node:crypto";

export function createEventIdentity(
  event: Omit<EvidenceEvent, "eventId" | "modelInterpretation">,
): string {
  const factsStr = event.deterministicFacts
    .map((f) => `${f.fact}:${f.detail ?? ""}`)
    .join("|");
  const identityKey =
    `${event.eventType}|${event.fromRevisionId}|${event.toRevisionId}|` +
    `${event.section}|${event.before}|${event.after}|${event.timestamp}|${factsStr}`;
  return createHash("sha256").update(identityKey).digest("hex").slice(0, 16);
}

The hash is truncated to 16 hex characters (64 bits of collision resistance).

Version compatibility

When consuming Refract events across different package versions, the schemaVersion field on each event tells you which schema produced it.

refract CLI @refract-org/evidence-graph EVENT_SCHEMA_VERSION Key changes
0.5.x 0.4.x "0.4.0" sentence_modified event type, FactProvenance.parameters, AnalyzerConfig, EVENT_SCHEMA_VERSION, CLAIM_IDENTITY_VERSION
0.4.x 0.3.x "0.3.0" ClaimLedger, ObservationReport, --report flag, cron merging
0.3.x 0.2.x "0.2.0" 25 event types, initial public release

Negotiation rules: