Data Model: Remove hidden --feature alias

Mission: feature-alias-removal-01KW0N87 Scope: Selector-resolution contract (minimal — this mission has no new persistent entities)


Selector-Resolution Contract (post-alias-removal)

This mission simplifies the selector-resolution path. The following contract replaces the resolve_selector(alias_value=feature, alias_flag="--feature", ...) pattern in all 8 in-scope files.

Inline Guard — Canonical Form

# Applied in: research, next_cmd, context, lifecycle.plan, lifecycle.tasks, mission_type.current
mission_norm = mission.strip() if isinstance(mission, str) else None
if not mission_norm:
    raise typer.BadParameter("--mission <slug> is required")
mission_slug = mission_norm

Input: mission: str | None — Typer-parsed value of --mission option; may be None if omitted, a non-empty string, or (edge case) a whitespace-only string.

Behaviour: 1. None input → raises typer.BadParameter (Typer maps to exit code 2) 2. Whitespace-only string → normalized to None → raises typer.BadParameter 3. Non-empty string after strip → passed downstream as mission_slug

Exit code on failure: 2 (standard Typer BadParameter exit code)

Message shape: "Error: Invalid value for '--mission': --mission <slug> is required" (Typer default BadParameter format)

accept.py Variant (uses resolve_mission_handle — no resolve_selector)

# accept.py — replaces `raw_handle = mission or feature`
raw_handle = mission.strip() if isinstance(mission, str) else None
if not raw_handle:
    # ... emit error message ...
    raise typer.Exit(2)
resolved = resolve_mission_handle(raw_handle, repo_root, json_mode=json_output)

merge.py Variant (uses _resolve_slug_or_exit helper — no resolve_selector)

# _resolve_slug_or_exit — replaces `(mission or feature or "").strip() or None`
mission_slug_raw = (mission or "").strip() or None

No-selector at merge.py end-of-flow (if not resolved_mission) → raise typer.Exit(2).

implement.py Variant (uses detect_feature_context — no resolve_selector)

detect_feature_context() is updated to remove feature_flag param:

def detect_feature_context(
    mission_flag: str | None = None,
    repo_root: Path | None = None,
) -> tuple[str | None, str]:
    raw_handle = mission_flag.strip() if isinstance(mission_flag, str) else None
    if raw_handle is None:
        raise typer.BadParameter("--mission <slug> is required")
    # ... resolver logic unchanged ...

Preserved Contracts (out-of-scope, unchanged)

ContractLocationStatus
resolve_selector function signatureselector_resolution.py:123Retained (C-005)
resolve_mission_handle functionselector_resolution.py:185Retained
feature_slug JSON field keysall persisted artifactsImmutable (C-003)
SPEC_KITTY_SUPPRESS_FEATURE_DEPRECATION env varselector_resolution.pyRetained but inert after this mission

No New Entities

This mission introduces no new domain entities, database tables, or persistent data structures. All changes are confined to the CLI surface layer and Python variable names.