Quickstart: Legacy Sparse-Checkout Cleanup and Review-Lock Hardening

Mission: legacy-sparse-and-review-lock-hardening-01KP54ZW Phase: 1 (Design) Date: 2026-04-14

This quickstart walks the four user-facing flows that this mission delivers. It is written against the interfaces and behaviours defined in data-model.md and plan.md; it doubles as the acceptance script for the success criteria in spec.md.


Flow 1 — Detect and remediate a legacy-sparse repo

Setup: An upgraded repo where git config --get core.sparseCheckout returns true and .git/info/sparse-checkout exists. This is Kent's configuration on kg-automation.

spec-kitty doctor

Expected excerpt of output:

⚠ Legacy sparse-checkout state detected
  Primary: /Users/you/your-repo
    core.sparseCheckout = true
    pattern file: .git/info/sparse-checkout (42 lines)
  Lane worktrees: 2 affected
    .worktrees/feature-lane-a
    .worktrees/feature-lane-b

  Why this matters:
    spec-kitty v3.0+ removed sparse-checkout support but does not ship a
    migration. This state can cause silent data loss during mission merge
    and broken lane worktrees on agent action implement.

  Fix:
    spec-kitty doctor --fix sparse-checkout

Run the fix:

spec-kitty doctor --fix sparse-checkout

Doctor refuses on a dirty tree (FR-005). If the primary or any worktree has uncommitted tracked changes, the output is:

✗ Cannot remediate: uncommitted changes in:
  .worktrees/feature-lane-a (2 tracked files modified)

  Commit or stash your changes and retry:
    cd .worktrees/feature-lane-a
    git stash push -u
    spec-kitty doctor --fix sparse-checkout

On a clean tree, doctor prompts once (interactive TTY only) and applies the remediation:

Proceed? This will:
  1. git sparse-checkout disable (primary)
  2. git config --unset core.sparseCheckout (primary)
  3. rm .git/info/sparse-checkout (primary)
  4. git checkout HEAD -- . (primary)
  5. repeat steps 1–4 in .worktrees/feature-lane-a
  6. repeat steps 1–4 in .worktrees/feature-lane-b
[y/N] y

✓ Primary: remediated (4 steps, clean verify)
✓ .worktrees/feature-lane-a: remediated
✓ .worktrees/feature-lane-b: remediated

Verification:

git config --get core.sparseCheckout             # empty
cat .git/info/sparse-checkout                    # No such file or directory
git status --short                               # (clean)

for wt in .worktrees/*; do
  (cd "$wt" && git config --get core.sparseCheckout)  # empty each
done

Non-interactive (CI) behaviour: When sys.stdin.isatty() is false or a common CI env var is set, doctor --fix sparse-checkout exits non-zero with a one-line remediation pointer rather than prompting (FR-023).


Flow 2 — Merge refuses on legacy-sparse; proceeds after remediation

Setup: A mission ready to merge, sparse-checkout still active.

spec-kitty agent mission merge --mission 017-example --json

Expected:

✗ Merge aborted: legacy sparse-checkout state detected

  This repository has core.sparseCheckout=true configured, which v3.0+
  spec-kitty does not handle correctly and which has caused silent data
  loss in prior mission merges (Priivacy-ai/spec-kitty#588).

  Fix:
    spec-kitty doctor --fix sparse-checkout

  If you have an intentional sparse configuration and understand the risk,
  you may pass --allow-sparse-checkout to proceed. Use of this override is
  logged at WARNING level.

Exit code: non-zero. No commits on the target branch. No MergeState file written.

Power user with legitimate sparse (FR-008, FR-009):

spec-kitty agent mission merge --mission 017-example --allow-sparse-checkout --json

Produces a log record (typically on stderr):

WARNING  spec_kitty.override.sparse_checkout command=mission-merge mission_slug=017-example mission_id=01KP54ZWEEPCC2VC3YKRX1HT8W actor=kentonium3 at=2026-04-14T05:10:02Z

Merge then runs. The commit-layer backstop (FR-011, C-007) remains active regardless; --allow-sparse-checkout does not disable it.


Flow 3 — Approve / reject a WP without --force

Setup: Lane worktree review in progress. .spec-kitty/review-lock.json exists in the worktree, no other untracked content.

# Reviewer has finished review and is ready to approve.
spec-kitty agent tasks move-task WP02 --to approved \
  --mission 017-example \
  --note "Review passed: acceptance criteria met"

Expected: transition succeeds. The guard's drift scan filters .spec-kitty/ (FR-015, C-003); the rest of the worktree is clean.

After the transition:

ls -la .worktrees/017-example-lane-a/.spec-kitty/
# No such file or directory (review lock released, dir removed when empty) — FR-018

Negative case: if the reviewer has a genuine uncommitted file alongside the lock, the guard still trips on that file (C-004):

Cannot move WP02 to approved

Uncommitted implementation changes in worktree!

Modified files:
  M  src/feature.py

Commit your work first:
  cd .worktrees/017-example-lane-a
  git add src/feature.py
  git commit -m "feat(WP02): finalize review fixes"

Then retry: spec-kitty agent tasks move-task WP02 --to approved

(Note the retry suggestion names the caller's actual target lane — approved — not a hardcoded for_review. FR-017.)

Reject flow (FR-019):

spec-kitty agent tasks move-task WP02 --to planned \
  --mission 017-example \
  --review-feedback-file feedback.md

Same filter applies; no --force needed when the only untracked content is .spec-kitty/.


Flow 4 — safe_commit backstop prevents silent data loss

Setup: A scenario where HEAD has advanced ahead of the working tree (for any reason — external pull, missed refresh, legacy-sparse state that slipped past preflight via --allow-sparse-checkout). The next call into safe_commit would have historically swept phantom deletions into the commit.

spec-kitty agent tasks move-task WP03 --to done --mission 017-example

Expected when the staging area would contain unexpected paths:

✗ Commit aborted: staging area contains unexpected paths

  Requested paths (what safe_commit was told to commit):
    kitty-specs/017-example/status.events.jsonl
    kitty-specs/017-example/status.json

  Unexpected paths staged (would have been committed):
    D  docs/runbooks/vikunja-date-handling.md
    M  scripts/openclaw/agents/felix-admin-habits/AGENTS.md

  This usually means the working tree is behind HEAD. Investigate before
  committing:
    git diff --cached
    git status
    git checkout HEAD -- <unexpected-paths>

  The backstop cannot be bypassed by --force.

Exit code: non-zero. No commit produced. The user resolves the staging-area state and retries. The cascade that caused Priivacy-ai/spec-kitty#588's silent revert is closed off at the commit layer (FR-011, FR-012, C-005).


Flow 5 — Recovery for users already hit (documentation-only)

Setup: A user whose main already contains a phantom-deletion commit from a pre-fix merge. Example from mission 023: commit 84bf7b6 on Kent's main silently reverted 243 lines across 4 files from the preceding merge commit 113d734.

The CHANGELOG entry (FR-021) documents the recipe:

# Identify the merge commit that originally introduced the content.
git log --merges --oneline -- <affected-file>

# Restore from the merge commit.
git checkout <merge-sha> -- <affected-files...>

# Commit the restoration.
git add <affected-files...>
git commit -m "fix: restore content reverted by phantom-deletion bug (pre-fix merge)"

Users should run the remediation (spec-kitty doctor --fix sparse-checkout) before invoking another merge to prevent recurrence.


Test-map summary

FlowSuccess criterionIntegration test
1SC-001tests/integration/sparse_checkout/test_remediation_primary_and_worktrees.py
2SC-002, SC-007tests/integration/sparse_checkout/test_merge_preflight_blocks.py + test_merge_with_allow_override.py
3SC-003, SC-004, SC-007tests/integration/review/test_approve_without_force.py + test_reject_without_force.py
4SC-005, SC-007tests/integration/git/test_safe_commit_backstop.py
5SC-008CHANGELOG review; no code test

All integration tests run in both "clean 3.x-born repo" and "legacy-sparse repo" fixtures (C-008).