Quickstart: Mission Terminology Cleanup
Mission: 077-mission-terminology-cleanup Audience: The agent or engineer implementing Scope A (#241) Estimated path: helper → one command end-to-end → batch the rest → grep guards → docs → orchestrator-api verification
This quickstart walks through the implementation in the order that minimizes rework. Read the spec, plan, research, data-model, and contracts first. Then start here.
Prerequisites
- Python 3.11+ installed
uvinstalled (perCONTRIBUTING.md;pipx install uvorcurl -LsSf https://astral.sh/uv/install.sh | sh)- Repo checked out at HEAD (validated baseline
54269f7c131a5efc40b729d412de26f6b05c65fbor later) - You are on
main(planning/base branch) - The repo uses PEP 621 + Hatch (
pyproject.toml) anduvfor dependency management. Do not usepoetry— there is nopoetry.lockandpoetry installwill fail. - You have read:
kitty-specs/077-mission-terminology-cleanup/spec.mdkitty-specs/077-mission-terminology-cleanup/plan.md(especially the Charter Reconciliation note)kitty-specs/077-mission-terminology-cleanup/research.mdkitty-specs/077-mission-terminology-cleanup/data-model.mdkitty-specs/077-mission-terminology-cleanup/contracts/selector_resolver.mdkitty-specs/077-mission-terminology-cleanup/contracts/deprecation_warning.mdkitty-specs/077-mission-terminology-cleanup/contracts/grep_guards.md- You understand that
--featurebecomes a hidden alias, not a deleted flag (charter requirement) - You understand that the orchestrator-api stays canonical-only and is not modified (C-010)
- You understand that historical artifacts under
kitty-specs/andarchitecture/are not modified (C-011)
Step 0: Reproduce the Verified Bug
Before writing any fix, reproduce the bug in spec §8.2 to confirm the baseline:
cd /private/tmp/241/spec-kitty
uv sync
Then run the current mission current command directly against the source tree:
uv run spec-kitty mission current --mission 077-mission-terminology-cleanup --feature 047-namespace-aware-artifact-body-sync
Observe that this command does not raise a conflict error. It silently resolves to the second value (typer's last-alias-wins). This is the verified bug. Once your fix lands, this exact command must exit non-zero with the conflict error from contracts/selector_resolver.md §"Conflict Error Format".
Step 1: Write the Failing Tests First
Create tests/specify_cli/cli/commands/test_selector_resolution.py with the 18 test cases enumerated in contracts/selector_resolver.md §"Required Test Coverage". All tests should fail at this point (the helper does not exist yet).
uv run pytest tests/specify_cli/cli/commands/test_selector_resolution.py -x
You should see 18 failures, all of the form ModuleNotFoundError: No module named 'specify_cli.cli.selector_resolution'.
Step 2: Implement the Helper
Create src/specify_cli/cli/selector_resolution.py following data-model.md and contracts/selector_resolver.md. The whole module should be ~80 lines:
SelectorResolutiondataclass (frozen, slots)_warned: set[tuple[str, str]] = set()module-level state_err_console = Console(stderr=True)module-level rich console_doc_path_for(alias_flag)helper_emit_deprecation_warning(canonical_flag, alias_flag, suppress_env_var)private sub-helperresolve_selector(...)public function
Run the helper unit tests until they pass:
uv run pytest tests/specify_cli/cli/commands/test_selector_resolution.py -x -k "not integration"
Step 3: Wire One Command End-to-End
Pick mission current (because it has the smallest blast radius and is the verified-bug site). Modify src/specify_cli/cli/commands/mission.py lines 172-194:
Before (current buggy state):
@app.command("current")
def current_cmd(
feature: str | None = typer.Option(
None,
"--mission",
"--feature",
"-f",
help="Mission slug",
),
) -> None:
...
After:
from typing import Annotated
import typer
from specify_cli.cli.selector_resolution import resolve_selector
@app.command("current")
def current_cmd(
mission: Annotated[str | None, typer.Option(
"--mission",
"-f",
help="Mission slug",
)] = None,
feature: Annotated[str | None, typer.Option(
"--feature",
hidden=True,
help="(deprecated) Use --mission",
)] = None,
) -> None:
"""Show the active mission type for a mission."""
resolved = resolve_selector(
canonical_value=mission,
canonical_flag="--mission",
alias_value=feature,
alias_flag="--feature",
suppress_env_var="SPEC_KITTY_SUPPRESS_FEATURE_DEPRECATION",
command_hint="--mission <slug>",
)
mission_slug = resolved.canonical_value
# ... rest of function unchanged ...
Important: do not keep the old feature parameter name on the canonical flag. Use mission for the canonical parameter and feature for the hidden alias parameter. They must be two separate parameters; that's the entire point of the fix.
Now run:
uv run pytest tests/specify_cli/cli/commands/test_selector_resolution.py -x
The integration tests for mission current should now pass. Run the manual repro from Step 0:
uv run spec-kitty mission current --mission 077-mission-terminology-cleanup --feature 047-namespace-aware-artifact-body-sync
You should now see a non-zero exit and the conflict error message. The verified bug is fixed.
Step 4: Batch the Rest of the Tracked-Mission Sites (WPA2a)
Use the WPA1 audit output to identify every tracked-mission selector site. The verified-known sites are:
src/specify_cli/cli/commands/next_cmd.py:33(1 site, plus the example in line 48 which becomes documentation cleanup)src/specify_cli/cli/commands/agent/tasks.py(9 sites at lines 842, 1389, 1572, 1655, 1726, 1945, 2205, 2295, 2659)
For each site, apply the same pattern as Step 3: 1. Split the multi-alias Option into two separate parameters: canonical --mission (visible) and --feature (hidden, if it was previously aliased). 2. Drop --mission-run entirely from tracked-mission selector aliases. The WPA1 audit must confirm that no caller in the runtime/session code depends on --mission-run being a tracked-mission alias on these specific commands. If any caller does, that's a runtime bug to fix separately. 3. Update the help string from "Mission run slug" to "Mission slug". 4. Call resolve_selector(...) in the function body and use the returned canonical value.
After each file, run the full selector-resolution test suite:
uv run pytest tests/specify_cli/cli/commands/test_selector_resolution.py
Step 5: Inverse Drift (WPA2b)
Three known sites in contracts/selector_resolver.md §"Inverse-Drift Command":
src/specify_cli/cli/commands/agent/mission.py:488src/specify_cli/cli/commands/charter.py:67src/specify_cli/cli/commands/lifecycle.py:27
For each, apply the inverse-direction pattern: 1. Rename the canonical parameter to use --mission-type as its primary flag. 2. Add a hidden --mission alias parameter with hidden=True. 3. Call resolve_selector with canonical_flag="--mission-type", alias_flag="--mission", suppress_env_var="SPEC_KITTY_SUPPRESS_MISSION_TYPE_DEPRECATION". 4. Update tests for these commands to use --mission-type as canonical.
Step 6: Doctrine Skills (WPA6)
Open src/doctrine/skills/spec-kitty-runtime-next/SKILL.md and replace every --mission-run <slug> instruction with --mission <slug> (only when the slug means a tracked mission). Leave any genuine --mission-run references for runtime/session contexts alone.
Search for other doctrine skills that might mention tracked-mission selection:
grep -rn "mission-run" src/doctrine/skills/ | grep -v "runtime\|session"
Update each match.
Step 7: Agent-Facing Docs and Top-Level Project Docs (WPA7)
Spec FR-022 puts both docs/** and the top-level project docs in scope.
Step 7a: Live docs/** files
Open docs/explanation/runtime-loop.md and replace legacy selector teaching. Search for other live docs:
grep -rn "mission-run\|--feature" docs/explanation/ docs/reference/ docs/tutorials/ 2>/dev/null
Update each match. Do not scan kitty-specs/ or architecture/ or docs/migration/** (the migration docs are required to mention --feature and --mission-run by name; that's where the deprecation warnings link to).
Step 7b: Top-level project docs
Per FR-022, the top-level files README.md, CONTRIBUTING.md, and the Unreleased section of CHANGELOG.md are also in scope. Verified drift sites at HEAD 35d43a25:
README.md:883— example block teaches# Accept mission (legacy --feature flag name)followed by aspec-kitty agent mission accept --jsonexample. Remove the "(legacy --feature flag name)" comment and any--feature-using example block; keep the canonical--mission-using example.README.md:910—--feature <slug>is documented in thespec-kitty acceptOptions table. This is the most visible drift in the repo because it teaches a new user that--featureis a valid option for an active command. Replace the row with--mission <slug>and remove the "Legacy flag name retained as a software-dev compatibility alias" prose.
Search for any other top-level drift:
grep -n "mission-run\|--feature" README.md CONTRIBUTING.md
For CHANGELOG.md, only the Unreleased section is in scope. Historical version entries (e.g., the line at CHANGELOG.md:172 describing what changed in a past release) are explicitly excluded by the "CHANGELOG-style historical entries" carve-out in FR-022. Do not rewrite history. If the Unreleased section has any active mention of --feature or --mission-run for tracked-mission selection, update it; otherwise leave CHANGELOG.md alone.
# Check the Unreleased section only — everything above the first ## [<version>] heading
awk '/^## \[[0-9]+\.[0-9]+\.[0-9]+/{exit} {print}' CHANGELOG.md | grep -n "mission-run\|--feature"
Do not scan kitty-specs/ or architecture/ under any circumstances (C-011).
Step 8: Migration Docs (WPA9)
Create the two migration doc pages referenced from the deprecation warnings:
docs/migration/feature-flag-deprecation.mddocs/migration/mission-type-flag-deprecation.md
Each should include:
- Why the change (links to ADR + initiative + this mission's spec)
- What changed (the canonical flag names)
- How to migrate scripts (find/replace examples)
- How to suppress the warning during cutover (the env var name)
- Removal criteria (named conditions only, no date — per spec §15 Q1)
Step 9: Grep Guards (WPA6 + WPA7 finalization)
Create tests/contract/test_terminology_guards.py following contracts/grep_guards.md. Implement all 8 guard test functions. Run them:
uv run pytest tests/contract/test_terminology_guards.py
If any guard fails, the failure message names the file and the fix. Fix the source of the failure (not the guard) and re-run. The guards should be green when all of WPA1..WPA8 are complete.
Critical: do not silence a failing guard. The guards exist precisely to fail noisily.
Step 10: Cross-Surface Verification (WPA8)
This work package is mostly reading code, not writing it. Verify:
1. src/specify_cli/orchestrator_api/commands.py is unchanged. 2. src/specify_cli/orchestrator_api/envelope.py is unchanged. 3. src/specify_cli/core/upstream_contract.json is unchanged in the orchestrator_api section. 4. tests/contract/test_orchestrator_api.py is unchanged. 5. The new tests/contract/test_terminology_guards.py::test_orchestrator_api_envelope_width_unchanged passes.
The intentional asymmetry from spec §11.1 means there is no orchestrator-api code change in this mission. Resist the temptation to "tidy up" the orchestrator-api side; that would violate C-010.
Step 11: Spec Edit (Charter Reconciliation)
Per the plan's Charter Reconciliation note, edit spec §11.1 to use the language "hidden deprecated compatibility alias" instead of "deprecated compatibility alias". This is a one-line clarification that aligns the spec with the charter without changing behavior. Make this edit in the same PR as the implementation.
Do not edit any other spec section. The spec is the authoritative artifact for this mission and should not drift from its post-review state except for this one clarification.
Step 12: Full Test Suite + Coverage
PWHEADLESS=1 uv run pytest tests/
uv run pytest --cov=specify_cli.cli.selector_resolution --cov-report=term-missing tests/specify_cli/cli/commands/test_selector_resolution.py
Verify:
- All tests pass.
- Coverage on
selector_resolution.pyis ≥ 90% (NFR-005). It should be 100%. - mypy --strict passes:
uv run mypy --strict src/specify_cli/cli/selector_resolution.py
Step 13: Manual End-to-End Smoke Test
Run each of the following and verify the behavior matches the contract:
# Canonical works
uv run spec-kitty mission current --mission 077-mission-terminology-cleanup
# expected: succeeds, no warning
# Hidden alias works with warning
uv run spec-kitty mission current --feature 077-mission-terminology-cleanup
# expected: succeeds, one yellow "Warning: --feature is deprecated; use --mission..." on stderr
# Same value with both flags works with warning
uv run spec-kitty mission current --mission 077-mission-terminology-cleanup --feature 077-mission-terminology-cleanup
# expected: succeeds, one warning
# Conflict fails deterministically
uv run spec-kitty mission current --mission 077-mission-terminology-cleanup --feature 047-namespace-aware-artifact-body-sync
# expected: non-zero exit, conflict error message naming both flags and both values
# Suppression env var works
SPEC_KITTY_SUPPRESS_FEATURE_DEPRECATION=1 uv run spec-kitty mission current --feature 077-mission-terminology-cleanup
# expected: succeeds, NO warning
# --feature is hidden in help
uv run spec-kitty mission current --help | grep -- "--feature"
# expected: empty (no match) — --feature must not appear in help
# Inverse direction: canonical works
uv run spec-kitty agent mission create new-quickstart-test --mission-type software-dev --json
# expected: succeeds, no warning, creates a new mission directory
# Inverse direction: hidden alias with warning
uv run spec-kitty agent mission create another-quickstart-test --mission software-dev --json
# expected: succeeds, one yellow "Warning: --mission is deprecated; use --mission-type..." on stderr
# Clean up the test missions
rm -rf kitty-specs/*new-quickstart-test*
rm -rf kitty-specs/*another-quickstart-test*
Step 14: Pre-PR Checklist
Before opening the PR for Scope A acceptance review:
- □ All 15 acceptance gates in spec §10.1 pass
- □ CI is green
- □ Coverage on
src/specify_cli/cli/selector_resolution.pyis ≥ 90% - □ mypy --strict is clean on all touched files
- □ No file under
kitty-specs/**(other than077-mission-terminology-cleanup/) is modified - □ No file under
architecture/**is modified - □
src/specify_cli/orchestrator_api/envelope.pyis unchanged - □
src/specify_cli/core/upstream_contract.jsonorchestrator-api section is unchanged - □
tests/contract/test_orchestrator_api.pycontinues to assert--featureis rejected (regression check) - □ All 9 guards in
tests/contract/test_terminology_guards.pypass (8 original + Guard 5b for top-level docs) - □
README.mdno longer documents--feature <slug>as a live option for any command (verified drift sites at lines 883 and 910 cleaned up) - □
CONTRIBUTING.mddoes not teach--featureor--mission-runfor tracked-mission selection - □
CHANGELOG.mdhistorical version entries are unchanged; only the Unreleased section was edited (if at all) - □ Migration docs at
docs/migration/feature-flag-deprecation.mdanddocs/migration/mission-type-flag-deprecation.mdexist and link to the spec, ADR, and initiative - □ spec §11.1 clarification edit is included
- □ PR description references
Priivacy-ai/spec-kitty#241and includes the spec gates checklist
Out of Scope for Scope A
Do not do any of the following in the same PR:
- Touching the orchestrator-api (C-010, §10.1 item 10)
- Renaming
MissionCreated/MissionClosed(locked non-goal §3.3) - Adding
mission_run_sluganywhere (C-009) - Renaming
aggregate_type="Mission"(locked non-goal §3.3) - Renaming any directory under
kitty-specs/(locked non-goal §3.3, C-002) - Modifying historical mission specs under
kitty-specs/**(C-011) - Modifying ADRs or initiatives under
architecture/**(C-011) - Removing
--feature(out of scope per spec §11.2 — removal is gated on telemetry) - Any Scope B work (gated on Scope A acceptance per spec §2 + C-004)
When Scope A Is Accepted
Open a follow-on issue or work package set for Scope B (#543). Reference spec §13.2 for the work package outline. Do not start Scope B until Scope A is merged and the gates in §10.1 are confirmed green on main.