Research: Retire Mission Identity Drift Window

Date: 2026-04-13 Mission: 01KP2JNZ7FRXE6PZKJMH790HA5

Call-Site Audit

emit_mission_created

  • Emitter method: src/specify_cli/sync/emitter.py:468mission_id: str | None = None
  • Wrapper: src/specify_cli/sync/events.py:248mission_id: str | None = None (forwards correctly)
  • Caller: src/specify_cli/core/mission_creation.py:345 — passes mission_id=meta.get("mission_id")
  • Assessment: Working correctly. meta.get("mission_id") is always non-None for new missions since mission creation mints a ULID. Safe to make mandatory.

emit_mission_closed

  • Emitter method: src/specify_cli/sync/emitter.py:514mission_id: str | None = None
  • Wrapper: src/specify_cli/sync/events.py:275does NOT accept or forward mission_id (gap)
  • External callers: None found. Wrapper is exported via sync/__init__.py but not yet called by any production code.
  • Assessment: Wrapper needs mission_id: str added. No callers to update beyond the wrapper itself.

emit_mission_origin_bound

  • Emitter method: src/specify_cli/sync/emitter.py:628mission_id: str | None = None
  • No wrapper function — called directly from tracker/origin.py:265
  • Caller: src/specify_cli/tracker/origin.py:265does NOT pass mission_id (gap)
  • Assessment: Caller must be updated to load mission_id from meta.json and pass it.

legacy_aggregate_id Surface

FileLine(s)WhatAction
src/specify_cli/status/models.py181Docstring describing fieldUpdate
src/specify_cli/status/models.py220-223to_dict() emits fieldRemove
src/specify_cli/status/emit.py385-386Comment referencing T025Remove
tests/status/test_event_mission_id.py9, 68, 166, 171, 294, 317-350, 414-444Fixtures and assertionsFlip/remove
tests/contract/test_identity_contract_matrix.py190, 281-282, 406-412Contract surface, backward-compat testUpdate

effective_aggregate_id Fallback Surface

FileLine(s)MethodAction
src/specify_cli/sync/emitter.py497-505emit_mission_createdRemove fallback, use mission_id directly
src/specify_cli/sync/emitter.py540-544emit_mission_closedRemove fallback, use mission_id directly
src/specify_cli/sync/emitter.py656-660emit_mission_origin_boundRemove fallback, use mission_id directly

Documentation Surface

  • docs/: No references to legacy_aggregate_id or drift window found.
  • CLAUDE.md: No references to legacy_aggregate_id or drift window found (mentions "drift" only in context of status-model drift detection, which is a different concept).
  • Source docstrings in models.py and emitter.py: Reference drift window — will be updated inline with code changes.

Decisions

DecisionRationaleAlternative rejected
Make mission_id mandatory on emitter methods and wrappersAll active code paths have it; Optional type masks the invariantKeep optional with runtime assertion — adds dead branch and test complexity
Keep `mission_id: str \None on StatusEvent` dataclassLegacy events on disk lack the field; removing would break deserialization (violates C-002)
Fix origin.py caller before making method mandatoryClean compilation; prevents runtime crash in origin-binding flowLeave caller broken — defeats purpose of mandatory type