Context and Problem Statement
Mission creation currently allocates the next mission number by scanning local
kitty-specs/ and local .worktrees/ for directory names that begin with a
three-digit prefix, then returning max(found) + 1.
That allocator is local-only. It has no shared registry, no reservation step, no merge-time canonicalization, and no protection against parallel authors who branch from the same base commit. In a distributed workflow, two independent clones can both observe the same maximum prefix and both allocate the same next number for different missions.
This is not a theoretical problem.
- PR #555 was created as
077-planning-artifact-and-query-consistencywhilemainindependently landed077-mission-terminology-cleanup. - The resulting merge from
origin/maininto that branch produces a real collision inkitty-specs/and content conflicts in docs, runtime, and test files. mainalready contains multiple duplicate numeric mission prefixes, proving that the sequential prefix is not a globally unique identifier in production data.
At the same time, the codebase has already begun migrating to a different model:
downstream runtime and merge-state systems increasingly treat mission_id as
the canonical machine-facing identity. The remaining gap is that mission
creation still emits only the sequential prefix and human-readable slug as the
effective primary key.
The architecture decision is therefore not to design a new dense counter that works under partition. The decision is to finish the migration to a stable, partition-safe mission identity model.
Decision Drivers
- Spec Kitty must support concurrent mission creation across independent clones.
- Canonical mission identity must be unique without requiring online coordination.
- Existing dense numeric prefixes are already proven non-unique and cannot be relied on as machine-facing identifiers.
- The codebase already has partial
mission_idadoption in runtime and merge flows; the architecture should align with that direction instead of adding a second identity scheme. - Human-readable mission labels remain useful, but readability must not come at the cost of correctness.
Considered Options
- Option A: Keep sequential numeric mission prefixes as the canonical identity
- Option B: Keep sequential prefixes, but allow manual or explicit override
- Option C: Add an online reservation or registry for mission numbers
- Option D: Make
mission_id(ULID) the canonical mission identity and demotemission_numberto display metadata
Decision Outcome
Chosen option: Option D — make mission_id a creation-time ULID and treat it
as the canonical mission identity for all machine-facing behavior.
Decision
- Every new mission must receive a
mission_idat creation time. mission_idis the canonical machine-facing identity for missions.mission_numberis not a canonical identifier. It is descriptive, human-facing metadata only.- Any machine-facing selector, state file, merge primitive, work-package
reference, status artifact, or synchronization payload must resolve by
mission_idrather than by sequential mission number. - Human-facing mission handles such as path names, branch names, and UI labels
must stop assuming that the three-digit prefix is unique. These handles may
include a shortened mission-id-derived alias for readability, but the full
mission_idremains authoritative. - Dense sequential mission numbers may still exist as display aliases, but
they are assigned or finalized only in a context that has a single source of
truth, such as
main. They must not be used as stable pre-merge identity.
Consequences
Positive
- Mission creation becomes partition-safe without requiring online coordination.
- Existing runtime and merge-state code that already prefers
mission_idaligns with the documented architecture. - Duplicate numeric prefixes no longer threaten correctness because they cease to be identity-bearing.
- The architecture cleanly separates stable machine identity from mutable, human-facing display labels.
Negative
- Existing missions require a backfill migration for
mission_id. - Existing duplicate numeric prefixes on
mainmust be cleaned up so legacy selectors and documentation stop encountering ambiguous mission references. - Paths, branch names, selectors, and operator habits that currently rely on numeric mission prefixes require a staged migration and deprecation window.
Neutral
- ULID is chosen for uniqueness and rough chronological ordering. No stronger causal ordering primitive is required for mission identity itself.
- This ADR does not lock in an exact short-handle format for paths or branch names, only the architectural rule that the sequential prefix is no longer canonical identity.
Confirmation
This ADR is considered implemented when all of the following are true:
- New missions always write
mission_idat creation time. - A repository audit can report zero missions missing
mission_id. - Runtime and merge flows no longer depend on numeric mission prefixes being unique.
- Duplicate mission prefixes on
mainno longer create selector ambiguity or identity ambiguity. mission_numberis absent from machine-facing contracts that require stable identity, or is clearly documented there as display-only metadata.
Pros and Cons of the Options
Option A: Keep sequential numeric mission prefixes as the canonical identity
Continue using max(local prefixes) + 1 and rely on NNN-slug for mission
identity.
Pros:
- Preserves the current naming model.
- Keeps mission references short and familiar.
Cons:
- Cannot survive concurrent offline creation.
- Already disproven by duplicate prefixes on
main. - Forces correctness to depend on a dense counter in a distributed system.
Why Rejected: The current model is already failing in real repository data.
Option B: Keep sequential prefixes, but allow manual or explicit override
Permit the caller to supply the number explicitly when collisions are expected.
Pros:
- Small local change to current behavior.
- Gives operators a manual escape hatch.
Cons:
- Pushes distributed coordination onto humans.
- Does not create a canonical unique machine identity.
- Leaves the underlying race intact.
Why Rejected: Manual override is a workaround, not an architecture.
Option C: Add an online reservation or registry for mission numbers
Reserve the next number through main or another shared authority before
creating a mission.
Pros:
- Preserves dense numbers as canonical from birth.
- Prevents duplicate allocation while online.
Cons:
- Requires coordination at mission creation time.
- Fails the partition-tolerant offline workflow requirement.
- Adds operational complexity around retries, lock ownership, and reservations.
Why Rejected: Online reservation solves a different problem than the one Spec Kitty needs to support.
Option D: Make mission_id (ULID) the canonical mission identity and demote mission_number to display metadata
Mint a ULID at mission creation and use it as the stable identity everywhere that correctness depends on uniqueness.
Pros:
- Partition-safe by design.
- Matches the direction already present in runtime and merge-state code.
- Separates stable identity from human-facing display labels.
- Enables a staged migration instead of a single all-or-nothing rewrite.
Cons:
- Requires backfill and migration for old missions.
- Requires follow-up work to remove remaining prefix-based assumptions.
- Human-facing handles need a new convention once numeric prefixes stop being canonical.
Why Chosen: It is the only option that matches Spec Kitty's distributed workflow model while fitting the repository's partially completed migration direction.
More Information
Relevant repository evidence:
- Mission number allocation scans local
kitty-specs/and.worktrees/only:src/specify_cli/core/worktree.py - Mission creation currently formats the mission slug from that sequential
number and does not write
mission_id:src/specify_cli/core/mission_creation.py - Runtime lane manifests already describe
mission_idas the immutable machine-facing identity:src/specify_cli/lanes/models.py - Merge state is already scoped by
mission_id:src/specify_cli/merge/state.py
Follow-up implementation work is intentionally tracked outside this ADR so the architectural decision remains stable while the migration plan is executed in phases.