id: https://sniff.world/schema/sighting
name: sniff_sighting
title: 'Sniff Sighting: one observed mention of an entity in one document'
version: 0.2.0
license: https://creativecommons.org/publicdomain/zero/1.0/

description: >-
  A sighting is one observation that a document mentions an entity, recorded
  before anyone decides what it means. Literature never enters a coverage frame
  directly: it enters this ledger, and only an adjudicated sighting may support
  a frame cell. The separation is the point. Recall comes from a precomputed
  index, precision comes from adjudication, and a sighting records which stage
  it has reached.


  Sentence text is deliberately LOCAL-ONLY: it exists for adjudication and is
  never emitted into a published artifact. Published sightings carry the
  document id and character offsets instead, so any claim stays checkable
  against the source without redistributing copyrighted text.

prefixes:
  linkml: https://w3id.org/linkml/
  biolink: https://w3id.org/biolink/vocab/
  sniff: https://sniff.world/schema/
default_prefix: sniff
default_range: string

imports:
  - linkml:types

see_also:
  - https://sniff.world/schema/coverage-frame

classes:

  Sighting:
    tree_root: true
    description: >-
      One entity mention in one document, with the offsets that locate it and
      the adjudication that judged it.
    slots:
      - id
      - subject
      - taxon
      - document
      - passage_offset
      - annotation_offset
      - annotation_length
      - extractor
      - extractor_version
      - retrieved_date
      - archive_hash
      - filter_flags
      - proposal
      - adjudication
      - adjudication_basis
      - adjudication_note
      - sentence_text
    rules:
      - description: >-
          A confirmed or rejected sighting must record WHY, so the share of
          judgments that turned on species ambiguity stays measurable.
        preconditions:
          slot_conditions:
            adjudication:
              any_of:
                - equals_string: confirmed
                - equals_string: rejected
        postconditions:
          slot_conditions:
            adjudication_basis:
              required: true

  Proposal:
    description: >-
      A machine's opinion about a sighting, recorded before any human judged
      it. Held as its own class rather than as fields alongside `adjudication`
      on purpose: a proposal and a verdict are different kinds of thing, and a
      flat `proposed` sitting beside `adjudication` is one mis-read away from a
      compiler typing a cell on a machine's say-so. INV-145 forbids exactly
      that, so the shape is built to make the mistake awkward rather than
      merely detectable. Nothing here may promote a cell; only a merged human
      adjudication may.


      A proposal is what the adjudication ladder measures. Blind review
      withholds it from the reviewer, and blind agreement between this and the
      human verdict is the only agreement figure that means anything.
    attributes:
      status:
        range: AdjudicationStatusEnum
        required: true
        description: The status the proposer would assign. Never a verdict.
      basis:
        range: AdjudicationBasisEnum
        required: true
        description: >-
          Why. Compared against the human's basis as well as their status,
          because a proposer that agrees on every verdict while systematically
          misattributing the reason corrupts the basis tally, and that tally is
          the irreducible-human-judgment fraction this frame exists to measure.
      proposer:
        required: true
        description: Proposer id, for example flags-v1, the deterministic baseline.
      proposer_version:
        required: true
        description: >-
          Bumped on any rule change, so an agreement figure can never be
          compared across two different proposers wearing one name.

slots:

  proposal:
    range: Proposal
    inlined: true
    description: >-
      The machine proposal for this sighting, if one has been made. Absent
      until `propose` runs. Its presence never advances `adjudication`.
  id:
    identifier: true
    description: Stable sighting id, a hash of subject, document, offsets, extractor and version.
  subject:
    range: uriorcurie
    required: true
    description: The entity sighted, as a CURIE.
  taxon:
    range: uriorcurie
    required: true
    description: The taxon the sighting is scoped to, for example NCBITaxon:9615.
  document:
    range: uriorcurie
    required: true
    description: The source document, for example PMID:35277861.
  passage_offset:
    range: integer
    minimum_value: 0
    required: true
    description: Character offset of the passage within the document.
  annotation_offset:
    range: integer
    minimum_value: 0
    required: true
    description: Character offset of the mention within the document. A published receipt.
  annotation_length:
    range: integer
    minimum_value: 0
    required: true
    description: Length of the mention. With the offset, this is the checkable span.
  extractor:
    required: true
    description: The tool that produced the annotation, for example pubtator3.
  extractor_version:
    required: true
    description: Pinned version or release date of the extractor.
  retrieved_date:
    range: date
    required: true
    description: When the response that carried this sighting was retrieved.
  archive_hash:
    required: true
    description: >-
      SHA-256 of the archived response this sighting was derived from. The
      corpus-of-record pointer: re-derivation replays the archive, never the
      live service.
  filter_flags:
    multivalued: true
    description: >-
      Which candidate filters fired on this sighting, for example
      sentence_cooccurrence or dog_gene_id. Annotation only. A filter never
      types a cell.
  adjudication:
    range: AdjudicationStatusEnum
    required: true
    description: How far this sighting has progressed. A pending sighting cannot support a cell.
  adjudication_basis:
    range: AdjudicationBasisEnum
    description: >-
      Why the adjudication went the way it did. Required once adjudicated (see
      the class rule). The share of species_ambiguous plus insufficient_text is
      the irreducible-human-judgment fraction.
  adjudication_note:
    description: One sentence from the adjudicator, citing what was judged.
  sentence_text:
    description: >-
      LOCAL-ONLY. The sentence that was judged, kept for adjudication and never
      emitted into a published artifact. Published sightings carry offsets.

enums:
  AdjudicationStatusEnum:
    description: The three states of a sighting.
    permissible_values:
      pending:
        description: Observed, not yet judged. May not support a frame cell.
      confirmed:
        description: Judged to support a claim about this entity in this taxon.
      rejected:
        description: Judged not to support such a claim.

  AdjudicationBasisEnum:
    description: >-
      Why an adjudication went the way it did. Turns the species-assignment
      problem from a caveat into a measured quantity.
    permissible_values:
      confirmed_canine:
        description: The document studies this entity in this taxon.
      species_ambiguous:
        description: >-
          Entity and taxon both present, but which species the claim attaches
          to is not resolvable from the text.
      wrong_species_context:
        description: >-
          The entity is studied in another species and this taxon is mentioned
          incidentally, as in a comparative genomics survey.
      dog_as_comparator:
        description: The taxon appears as a comparison or control group, not the subject.
      gene_mismatch:
        description: The annotation resolves to a different entity, an alias or symbol collision.
      insufficient_text:
        description: Undecidable without full text.
