dev.idiolect.retrospection

A signed annotation of a prior encounter with a delayed finding. Retrospections address silent-error latency: merges, migrations, and bitemporal reconciliations often surface failures only after long delay.

Source: lexicons/dev/idiolect/retrospection.json · Rust: idiolect_records::Retrospection · TS: @idiolect-dev/schema/retrospection · Fixture: idiolect_records::examples::retrospection

Shape

FieldTypeRequiredNotes
encounterencounterRefyesThe encounter being retrospected.
findingobjectyes{ kind, kindVocab?, detail, evidence? }.
latencyinteger (seconds)nodetectedAt - encounter.occurredAt. Precomputed for aggregation.
detectingPartydidyesDID of the party that detected the issue.
confidencenumber ∈ [0, 1]noOptional confidence score.
disputedAttributionbooleannoThe detecting party's hint that the causal claim may be contested.
basisbasisnoStructured grounding.
detectedAtdatetimeyesWhen the issue was detected.
occurredAtdatetimeyesWhen this retrospection record was published.

finding

SubfieldTypeRequiredNotes
kindopen enumyesmerge-divergence / data-loss / reconciliation-mismatch / other.
kindVocabvocabRefnoVocab the kind slug resolves against.
detailstring (≤8000 graphemes)yesNarrative detail.
evidenceunion of evidenceDivergence / evidenceLoss / evidenceMismatchnoStructured witness for the finding.

Why a separate record kind

Encounters are at-record-time. Corrections are short-loop. A retrospection covers the case where the finding surfaces after the original encounter has been folded into observations and moved out of the working set:

  • A merge that looked correct at write time turns out to have silently dropped a field three months later.
  • A migration that round-tripped on the test corpus loses information on a long-tail input that nobody sampled.
  • A bitemporal reconciliation surfaces a divergence between two paths that should have converged.

The encounter / correction / observation triple cannot capture these without distorting their semantics. A retrospection record is the right shape: it points at the original encounter, names a delayed finding, and carries the evidence.

The four finding kinds

SlugEvidence shapeWhat it captures
merge-divergenceevidenceDivergence (paths A and B + witness input)Two translation paths that should have converged but produced different outputs.
data-lossevidenceLoss (source field path + target schema + witness input)A source-schema field unrepresented in the target after translation.
reconciliation-mismatchevidenceMismatch (left and right records + expected equality projection)Two records that should reconcile under the lens but do not.
other(optional)Catch-all; evidence may be omitted, detail carries the whole finding.

The structured evidence variants let downstream consumers match on the failure mode without parsing the narrative detail. An aggregator that wants to surface "lenses with high data-loss counts" filters by finding.kind = data-loss and folds.

Field details

latency

Precomputed for aggregation convenience. The value is detectedAt - encounter.occurredAt in seconds. Folds that bucket findings by latency (e.g. "what's the median time-to-detect for merge-divergence findings?") read this field directly. Authors may omit it for kind: other findings where latency is not meaningful.

confidence

Optional, in [0, 1]. A finding the detecting party is sure of omits it. A finding hedged on uncertain evidence sets a value below 1. Aggregators may weight findings by confidence; consumers treating findings as ground truth filter for high confidence.

disputedAttribution

A hint the detecting party expects the causal attribution to be contested. The substrate does not enforce contestation; a contesting party publishes its own retrospection with disagreement, or a dev.idiolect.belief over the finding. The flag exists so consumers can flag the finding as "interpretation pending" rather than treating it as settled.

detectingParty versus the repo signer

detectingParty is the party who actually detected the issue. Most retrospections are first-party: the repo owner is the detecting party. Some are third-party: a researcher republishing a finding from a trusted source. holder is not a field here (unlike encounter / belief / correction); detectingParty plus the repo signer carry the relevant attribution.

Example

{
  "$type": "dev.idiolect.retrospection",
  "encounter": {
    "uri": "at://did:plc:user/dev.idiolect.encounter/3l5"
  },
  "finding": {
    "kind": "data-loss",
    "detail": "Lens dropped the `provenance` array on records with > 100 entries; not detected at write time because all sampled inputs had ≤ 50 entries.",
    "evidence": {
      "$type": "dev.idiolect.defs#evidenceLoss",
      "sourceField": "provenance",
      "targetSchema": {
        "uri": "at://did:plc:schema-author/dev.panproto.schema.schema/v2"
      }
    }
  },
  "latency": 7776000,
  "detectingParty": "did:plc:detector",
  "confidence": 0.95,
  "detectedAt": "2026-07-19T00:00:00.000Z",
  "occurredAt": "2026-07-19T00:01:00.000Z"
}

Concept references