Mission Specification: Mission-identity naming seam & #1908 panel hardening

Mission ID: 01KV6510YXX3HM222Y0YG5JY3M Slug: mission-identity-seam-and-1908-panel-01KV6510 Type: software-dev Addresses: Cluster A — #1978, #1949, #1918, #1899 (epic #1868); Cluster B — #1915, #1917, #1916 (post-#1908 panel; meta-tracker #1929)

Purpose

Two correctness/robustness clusters surfaced by adversarial review:

1. Mission-identity naming seam (Cluster A). The slug ↔ mid8 ↔ branch/worktree-name mapping is reimplemented ad-hoc in several places, each with its own idempotency or parsing bug. The now-closed #1860 was the same wrong-compose class on the status-read path — a recurring regression that signals a missing single authority. One of these bugs (#1978) currently blocks spec-kitty merge. Bind the mapping to one canonical seam (compose + parse, idempotent) and route the broken call sites through it. 2. Post-#1908 panel hardening (Cluster B). Three discrete robustness findings from the PR #1908 adversarial panel (tracked via the #1929 meta-checklist): lane-merge atomicity, base-ref argument hygiene, and an accept-gate identity side-effect. Each is a self-contained TDD fix.

> Dogfooding note (drives sequencing): this mission's own slug ends in its > mid8 (…-01KV6510), so its own merge is exactly the #1978 case. #1978 is > therefore the first/priority fix, and the mission may need a manual merge if it > lands before #1978 is verified.

Scope

In scope

one authority in lanes/branch_naming.py that composes and parses branch, lane, and worktree/coordination dir names, keyed on the declared (slug, mission_id), idempotency-preserving (a slug already embedding its mid8 is left as-is — no strip-reappend churn). Every ad-hoc compose/parse site routes through it (no name-guessing f-strings). The seam bugs are fixed in place and the parallel implementations are unified:

correctness paths pass mission_id (#1918, see FR-004).

minus the kitty/mission- prefix (single derived grammar) + a worktree_path() emit-don't-guess helper; literal-ban ratchet (#1899/FR-005).

+ cli/commands/merge.py (#1978), runtime/next/runtime_bridge.py (false compose), lanes/{worktree_allocator,merge,recovery,lifecycle_sync,implement_support}, workspace/context.py, orchestrator_api/commands.py, cli/commands/agent/tasks.py, and the parallel coordination impls coordination/{workspace._compose_mission_dir, transaction._mission_specs_dir_name, status_transition._transaction_dir_name, surface_resolver._coord_mid8} (FR-010).

_filter_accept_owned_project_config stopgap — #1916.

  • Cluster A — canonical seam (FULL consolidation, per paula-patterns review):
  • compose idempotency — mission_branch_name/_required (#1949).
  • parse safety — mid8_from_slug demoted to non-authoritative detector;
  • worktree dir-name grammar worktree_dir_name() == lane_branch_name(...)
  • route ALL leak sites through the seam (paula F-1/F-2/F-5): merge preflight
  • Cluster B — discrete fixes:
  • lanes/worktree_allocator.py::_merge_dependency_lane_tips atomicity — #1915.
  • cli/commands/implement.py _validate_base_ref -- separator — #1917.
  • accept-gate ensure_identity moved off the readiness path; retire the

Out of scope

what the naming seam requires.

fixed by mission 083; this mission only fixes the name derivation/parsing).

  • The broader coord/main/lane execution-context unification (#1619/#1716) beyond
  • lanes.json schema changes beyond honoring the canonical mission_branch.
  • Renaming the mid8/mission_id/mission_slug domain terms (identity model is
  • Re-parenting/closing #1929 (it stays a meta-checklist).

User Scenarios & Testing

Primary actor: a Spec Kitty operator / the runtime invoking merge, lane allocation, accept, and worktree/branch resolution.

spec-kitty merge on a mission whose slug already ends in its mid8 (like THIS mission). Trigger: preflight resolves the mission branch. Outcome: preflight uses the canonical mission_branch (kitty/mission-<slug>-<mid8>, or the already-embedded form) and finds the real branch — merge proceeds (today it false-negatives and blocks).

where slug already ends in mid8. Outcome: the name is not double-appended (…-<mid8>, not …-<mid8>-<mid8>); composing then parsing round-trips.

slug whose final hyphen-segment is coincidentally 8 Crockford-base32 chars but is not a real mid8. Outcome: it does not falsely resolve; the dual-era heuristic raises/declines rather than returning a wrong mid8.

obeys the same seam grammar; a 4th ratchet assertion guards it.

dependency lanes where an earlier dep merges clean and a later dep conflicts. Outcome: the rollback leaves the lane at its pre-merge state (no orphaned earlier-dep merge commit survives).

Outcome: git rev-parse --verify receives the value after a -- separator, so a leading-dash value is treated as a ref, not an option.

--no-commit (readiness) on a project with no minted identity. Outcome: it does not write .kittify/config.yaml (no ensure_identity` side effect, no dirtied tree); identity minting happens only on a write-authorized boundary.

  • Scenario — merge a mid8-embedded mission (happy path, #1978): operator runs
  • Scenario — compose a branch for an embedded slug (#1949): mission_branch_name(slug, mission_id)
  • Scenario — parse a coincidental slug (#1918): mid8_from_slug on a modern
  • Scenario — worktree name twin (#1899): worktree dir-name composition/parse
  • Scenario — multi-dependency lane merge rollback (#1915): a lane with ≥2
  • Scenario — base-ref arg hygiene (#1917): implement --base=--something.
  • Scenario — accept readiness is side-effect-free (#1916): `spec-kitty accept

Rules / invariants

compose is a fixpoint and round-trips; the declared mission_id is the authority, the embedded tail is only confirmation (matches → idempotent; mismatch → fail-closed/decline). The bare-string mid8_from_slug is heuristic and never authoritative on a correctness path (paula F-3).

fails over to legacy (NNN-/bare) only on a miss, with a one-shot deprecation warning (FR-004). The legacy path is a warned compatibility branch, not a co-equal parser — so resolution stays deterministic across the dual era.

parsing have exactly one implementation (branch_naming.py); no call site re-derives names locally and no parallel algorithm exists (incl. coordination/).

persist state (the canonical rule generalizing #1916; paula F-6).

its fix (these are defects).

call sites (merge preflight, worktree allocator, implement base-ref, accept gate); no edits to unrelated runtime/status hot paths.

  • Round-trip (keyed on declared identity): for any (slug, mission_id),
  • Canonical-first failover: the resolver tries new-style resolution first and
  • Single authority: branch/lane/worktree/coordination-dir name composition and
  • Readiness is side-effect-free: readiness/--no-commit paths MUST NOT mint or
  • TDD-first: each bug gets a failing regression/repro test committed before
  • Bounded surface: changes confined to the naming seam module + the listed

Domain Language

lanes.json.mission_branch); when the slug already ends in <mid8> the embedded form is canonical (no double suffix).

names from (slug, mid8).

  • mid8 — first 8 chars of the ULID mission_id; branch/worktree disambiguator.
  • mission_slug — human kebab slug; may or may not already embed the mid8.
  • canonical branchkitty/mission-<slug>-<mid8> (the form recorded in
  • naming seam — the single authority that composes/parses branch + worktree

Requirements

Functional Requirements

IDRequirementStatus
FR-001A single canonical seam MUST compose AND parse branch, lane, and worktree/coordination-dir names, keyed on the declared (slug, mission_id), idempotency-preserving (a slug already embedding its mid8 is left as-is). ALL compose/parse call sites MUST route through it — no ad-hoc f-strings or duplicate algorithms.Draft
FR-002The merge mission-branch resolution (cli/commands/merge.py fallback + the preflight) MUST resolve via mission_branch_name_required(slug, mission_id) (which fail-closes), not a kitty/mission-{slug} f-string that drops -{mid8}, so merge no longer false-negatives for mid8-embedded slugs (#1978). runtime/next/runtime_bridge.py's identical false-compose MUST be fixed the same way.Draft
FR-003mission_branch_name()/_required MUST be idempotent: when the slug already carries its mid8 it MUST NOT double-append; strip-then-reappend is allowed only when the embedded tail matches mission_id[:8] (#1949).Draft
FR-004The canonical reader/resolver MUST use canonical-first, legacy-failover logic: try the new style (declared mission_id / slug+mid8) first; only if that misses fall over to legacy (NNN-/bare) parsing, emitting a one-shot deprecation warning that nudges migration. mid8_from_slug is demoted in place to a best-effort, non-authoritative detector that declines on a coincidental 8-char tail with no mission_id rather than mis-resolving (#1918); an authoritative resolve_mid8(slug, *, mission_id) MUST be added for correctness-path use, and all ~12 mid8_from_slug callers MUST be audited and routed — value-uses to resolve_mid8, pure boolean-detector uses kept only where provably correct under the stricter decline. The two existing final-fallback consumers (resolve_transaction_mid8, coordination.surface_resolver._coord_mid8) MUST NOT newly fail-close for a genuine embedded-mid8 slug.Draft
FR-005A worktree_dir_name() (and an emit-don't-guess worktree_path()) MUST be added to the seam. It MUST reproduce the current on-disk grammar exactly in BOTH modes — mission_id=None ⇒ legacy {slug}-{lane} (no mid8 appended, matching today's f"{mission_slug}-{lane_id}" call sites), mission_id present ⇒ the embedded {slug}-{mid8}-{lane} form — so routing causes no on-disk worktree churn for any mission, not only mid8-embedded slugs. A shared golden-value name table (one canonical (slug, mission_id, lane_id) → expected branch / lane-branch / worktree-dir / coord-dir / coord-branch) MUST be the binding byte-identical assertion shared by all routing WPs. A literal-ban ratchet MUST fail any name-guess outside the seam (#1899).Draft
FR-010Exactly one algorithm MUST exist for the grammar. ALL parallel compose/parse/derive implementations MUST delegate to the seam: in coordination/ (workspace._compose_mission_dir, transaction._mission_specs_dir_name, status_transition._transaction_dir_name, surface_resolver._coord_mid8) AND in missions/ (_create.coordination_branch_name — the LIVE coord-branch composer run at every mission create, with its own endswith dedup — and _read_path_resolver._compose_mission_dir / feature_dir_resolver). The seam MUST expose a bare <slug>-<mid8> mission-dir primitive + coord-branch/coord-dir derivations so these have a real (non-lane-suffixed) delegation target (paula F-2; squad-verified gaps).Draft
FR-006_merge_dependency_lane_tips MUST be atomic across multiple dependency lanes: if a later dep merge conflicts, the lane MUST be rolled back fully (no earlier-dep merge commit survives) (#1915).Draft
FR-007_validate_base_ref MUST pass the --base value to git rev-parse --verify after a -- end-of-options separator so a leading-dash value is treated as a ref (#1917).Draft
FR-008The accept --no-commit/readiness path MUST NOT call ensure_identity (no .kittify/config.yaml write side effect); identity minting moves to a write-authorized boundary and the _filter_accept_owned_project_config stopgap is retired (#1916).Draft
FR-009Each fix MUST be preceded by a committed failing regression test that reproduces the defect (TDD-first).Draft

Non-Functional Requirements

IDRequirementMeasurable thresholdStatus
NFR-001Conflict surface is the naming-seam consolidation set (intentionally cross-cutting per full-consolidation scope), NOT unrelated hot paths.Diffs limited to: lanes/branch_naming.py; the routed call-site files (lanes/{worktree_allocator,merge,recovery,lifecycle_sync,implement_support,compute}, cli/commands/{merge,implement,decision,agent/tasks,agent/mission,agent/workflow,agent/status,agent/context}.py, runtime/next/runtime_bridge.py, workspace/context.py, orchestrator_api/commands.py, coordination/{workspace,transaction,status_transition,surface_resolver}.py, missions/{_create,_read_path_resolver,feature_dir_resolver}.py, status/aggregate.py); acceptance/__init__.py + identity/project.py + sync/events.py (Cluster B / readiness side-effect); and tests. Zero hunks in status/ (other than aggregate.py) / task_utils/ reducer/store internals — enforced by a diff-scan in WP09.Draft
NFR-002No quality-gate regression.ruff, mypy, and the full tests/ suite pass with zero new issues; dead-symbol/ratchet baselines unchanged or reduced.Draft
NFR-003The seam is verified by a round-trip/property test keyed on (slug, mission_id) (NOT bare strings — round-trip is unsatisfiable on the bare grammar, paula F-3).A property test asserts compose is a fixpoint and round-trips for: embedded-tail==mid8(mission_id), embedded-tail≠mid8 (mismatch handling), coincidental-8-char-tail with no mission_id (detector declines), and legacy NNN- (no mid8).Draft

Constraints

IDConstraintStatus
C-001TDD-first: never delete a test to pass; each fix lands with its failing-then-passing regression test (DIRECTIVE_003).Active
C-002#1929 is a meta-checklist tracker — findings are parented under their functional epics (#1868/#1795/#1666/#1914), never under #1929.Active
C-003PR-bound: lands via PR to upstream/main; never a direct push to origin/main.Active
C-004Every addressed issue gets an issue-matrix row, a tracker comment naming this mission, and operator-assigned claim.Active
C-005#1978 is sequenced early (this mission's own merge depends on it); be prepared to merge the mission manually if it lands first.Active

Success Criteria

spec-kitty merge without a preflight false-negative.

mid8; the seam round-trips for embedded, non-embedded, and coincidental-8-char slugs.

orphaned earlier-dep merge commit.

bounded surface.

  • SC-001: A mission whose slug embeds its mid8 (e.g. this one) merges via
  • SC-002: Composing a branch/worktree name for an embedded slug yields no doubled
  • SC-003: A coincidental-8-char modern slug does not falsely resolve a mid8.
  • SC-004: A multi-dependency lane merge that conflicts on a later dep leaves no
  • SC-005: implement --base=--x treats the value as a ref (no option injection).
  • SC-006: accept --no-commit leaves the working tree clean (no identity write).
  • SC-007: Full suite + ratchet/dead-symbol gates green; net change confined to the

Key Entities

  • Naming seam module — the single compose/parse authority.
  • Mission branch / worktree name — derived artifacts of (slug, mid8).
  • lanes.json mission_branch — recorded canonical branch (authority for read).
  • Dependency-lane merge — the atomic unit #1915 hardens.

Assumptions

embedded form canonical when the slug already ends in <mid8> (per #1978/#1949).

branch at merge time.

only name derivation/parsing is in scope.

  • The canonical branch convention is kitty/mission-<slug>-<mid8>, with the
  • lanes.json.mission_branch is the recorded source of truth for the mission
  • The mission identity model (mid8/mission_id/mission_slug, mission 083) is fixed;

Dependencies

for Cluster B; meta-tracker #1929. Builds on the closed #1860 (same class) and the #1908 adversarial panel.

full-consolidation scope (paula F-2) routes the coordination/ parallel compose/parse impls through the seam — this mission delivers the identity-naming-seam slice of that strangler. Reference #1878; do not close it.

  • Epic #1868 (canonical seams) for Cluster A; functional epics #1795/#1666/#1914
  • Advances #1878 (complete the coordination placement/identity strangler): the