An official website of the Disclosure Foundation

Introduction

Overview

Modules

ObservationShared PrimitivesExtension SlotsCompanions

Guides

Composing an ObservationValidating at Boundaries

Observation

The core record type — fields, domains, and the extension seam

Observation is the single source of truth for the record lexicon. It's a plain JSON-serializable object validated by ObservationSchema.

Required fields

FieldTypeNotes
idstringStable unique identifier
temporalTemporalDataDate (with certainty), optional time, duration
locationLocationDataName, country, coordinates, site type
statusPublicationStatusdraft · review · published · archived · retracted
createdAt / updatedAtstringISO timestamps

Everything else is optional — a sparse record is a valid record. Completeness scoring measures how much of the lexicon a record fills in; validity never depends on it.

Descriptive domains

FieldDescribes
summary / descriptionShort and long prose accounts
objectCharacteristicsShape, size, color, luminosity, number observed
movementManeuvers, speed, acceleration, departure
witnessesCount, categories, military/independent flags
sensorEvidenceSensor readings — each with an id claims can cite
investigationStatus, investigator, findings
responseImpactInterception response, physical effects
environmentWeather, visibility, terrain
aviationAirspace class, nearby facilities
relationsTyped edges to other observations (RelationEdge)
sourceDataWhere the record came from: source type, credibility
media / featuredMediaAttachments — each with an id claims can cite
eventTypeFree-text descriptor (the word classification is reserved for origins)
import type { Observation } from '@disclosureos/records/observation';
import { createObservation } from '@disclosureos/records/factories';

createObservation fills in id, status, and timestamps, then parses — invalid input throws. See Composing an Observation.

The extension seam

Observation carries an empty interface that other packages augment:

export interface ObservationExtensions {}
export type Observation = z.infer<typeof ObservationSchema> & ObservationExtensions;

Importing @disclosureos/observables adds the observableAssessments slot; importing @disclosureos/origins adds origin. The slots are typed at compile time but are not part of ObservationSchema — which is why enriched records must be validated with parseEnrichedObservation, never ObservationSchema.parse().

Two more escape hatches exist for data outside the standard:

  • extensions — a free-form Record<string, unknown> bag for your own pipeline metadata. Passed through validation untouched.
  • schemaVersion / dataSourceId — clerical metadata for migrations and ingestion tracking.

Records-owned extension slots

Five optional forensic surfaces ship inside records itself (subpath-only schemas, plain optional fields on the record): provenance, identifiers, testimony, physicalEvidence, and documents. See Extension slots.

Validating

import { validateObservation } from '@disclosureos/records';

const issues = validateObservation(record);
// ValidationIssue[] — [{ path: 'location.latitude', message: '...' }]

validateObservation accepts unknown, never mutates, and never strips. For records carrying slots, use the schema contract instead.

Overview

@disclosureos/records — the Observation lexicon and shared primitives

Shared Primitives

Confidence, Attribution, Claim, evidence refs, and validation helpers — the primitives every part reuses

On this page

Required fields
Descriptive domains
The extension seam
Records-owned extension slots
Validating