Skip to content

T-LDWC-package-removal-sweeps-untracked-residue

Status: open/ready · Impact: medium · Complexity: small

AUTO-DEFINED: this spec was best-effort machine-authored by /sdlc:task-auto-define on 2026-07-20 because the task is autonomy: autonomous/pr. Review the Goal, Approach, Today, Files-to-touch, and Acceptance-criteria carefully before trusting it.

A package removal that only runs git rm -r <dir> leaves gitignored, untracked residue on disk, so glob-based project discovery keeps finding the removed package and any acceptance criterion asserting its disappearance fails. This repo needs a documented — and where possible mechanized — package-removal recipe that pairs git rm -r <dir> with an untracked-residue sweep, plus the companion deletions-only lockfile assertion that proves the removal re-resolved additively. Originating from T-J5DI-remove-clear-duplicate-augmented-packages in sksizer/dev.

During T-J5DI, git rm -r packages/ts/render left a gitignored node_modules/ residue behind, so the directory still existed on disk and moon’s packages/ts/* glob kept discovering the project — the AC asserting moon query projects no longer listed it failed until a manual rm -rf cleared it. git rm alone does not satisfy a glob-discovery assertion. Document (and where possible mechanize) a package-removal recipe that pairs git rm -r <dir> with an untracked-residue sweep, and note the deletions-only lockfile assertion (zero additions across bun.lock and Cargo.lock) as the companion proof that a removal re-resolved additively. Touchpoints: the SDLC conventions directory apps/sdlc/conventions/ for the recipe, and apps/sdlc/skills/task-work/SKILL.md if the sweep is briefed to the implementation sub-agent.

T-J5DI-remove-clear-duplicate-augmented-packages

LocationRole today
apps/sdlc/conventions/The canonical home for shared procedural prose that SKILL.md files reference instead of inlining (per apps/sdlc/skills/CLAUDE.md). Holds ten topic docs; none covers package removal
justfileThe command catalog whose header calls its recipes “the deterministic tails skills and humans share”. Carries release-lib, fmt-markdown, check-markdown, dev, gen-dashboard-client — no removal recipe
apps/sdlc/scripts/release-lib.tsThe reference shape for a just recipe backed by a Bun script under apps/sdlc/scripts/
apps/sdlc/lib/util/command.tsThe command seam (runChecked / spawnRunner) every TypeScript shell-out must route through, per the repo CLAUDE.md
apps/sdlc/lib/services/git/easy.tsThe typed git client preferred over hand-built argv for git operations
.moon/workspace.ymlDeclares glob-based project discovery (apps/*, packages/ts/*, packages/rust/*, sites/*, tools/*). A directory that still exists on disk keeps matching these globs regardless of git tracking state — the mechanism that made T-J5DI’s AC fail
apps/sdlc/skills/task-work/SKILL.mdThe implementation skill. Step 6 briefs the implementation sub-agent; its ## Notes section is where cross-skill conventions are surfaced as one-line references
bun.lockBun workspace lockfile — one half of the deletions-only assertion
Cargo.lockCargo workspace lockfile — the other half

A package removal is one documented recipe with a mechanized tail. The convention doc apps/sdlc/conventions/package-removal.md states the three phases (tracked delete, untracked-residue sweep, deletions-only lockfile assertion); just remove-package <dir> executes them; and apps/sdlc/skills/task-work/SKILL.md points an implementer at the convention from its ## Notes block so a removal task never re-derives the recipe.

  1. Write the convention. Create apps/sdlc/conventions/package-removal.md in the house style of the existing conventions (title, why-it-exists, contract, phases). Document three phases in order:
    • Phase 1 — tracked delete. git rm -r <dir> removes the tracked files only.
    • Phase 2 — untracked-residue sweep. git clean -xdff -- <dir> followed by rm -rf <dir> clears gitignored/untracked residue (node_modules/, target/, dist/) and the now-empty directory itself. State the failure this prevents: glob-based discovery in .moon/workspace.yml matches directories that exist on disk, so moon query projects keeps listing a package whose tracked files are gone. Cite T-J5DI’s packages/ts/render case as the motivating incident.
    • Phase 3 — deletions-only lockfile assertion. After re-resolving (bun install, cargo metadata --locked or cargo check --locked), assert git diff --numstat -- bun.lock Cargo.lock reports zero added lines. A nonzero additions column means the removal pulled new resolutions in rather than only dropping the removed package’s — the proof the removal re-resolved additively.
    • Also state the reference sweep: enumerate and remove every remaining citation of the removed path (manifest dependency entries, workspace member lists, docs, CI) before asserting.
  2. Mechanize the recipe. Add apps/sdlc/scripts/remove_package.ts — a Bun script taking one or more package directories plus a --base <ref> flag (default origin/main) for the lockfile diff. It runs Phase 1 and Phase 2 per directory, then Phase 3 once across the lockfiles, printing a one-line-per-phase progress log and exiting nonzero on assertion failure. Support --dry-run (print the commands, mutate nothing). All shell-outs go through runChecked / spawnRunner from apps/sdlc/lib/util/command.ts, with git operations preferring apps/sdlc/lib/services/git/easy.ts; no raw child_process import.
  3. Expose it as a deterministic tail. Add a remove-package recipe to justfile following the release-lib shape — a doc comment naming the three phases, then bun run apps/sdlc/scripts/remove_package.ts {{dirs}} {{flags}}.
  4. Surface it to the implementer. Add one bullet to the ## Notes section of apps/sdlc/skills/task-work/SKILL.md pointing at ${CLAUDE_PLUGIN_ROOT}conventions/package-removal.md, matching the existing reference-bullet shape used there for commit-messages.md and branch-naming.md. Do not inline the recipe into the SKILL.md — the no-duplicate-prose rule in apps/sdlc/skills/CLAUDE.md makes the convention the single source of truth.
  5. Verify. Exercise the script against a throwaway package directory seeded with an untracked node_modules/, confirm the directory is gone and moon query projects no longer lists it, then run just check-markdown.
LocationKindChange
apps/sdlc/conventions/package-removal.mdnewThe three-phase package-removal recipe: tracked delete, untracked-residue sweep, deletions-only lockfile assertion
apps/sdlc/scripts/remove_package.tsnewBun script mechanizing the three phases; shells out via the command seam
justfilemodifyAdd the remove-package recipe delegating to remove_package.ts
apps/sdlc/skills/task-work/SKILL.mdmodifyAdd one ## Notes bullet referencing the new convention doc
  • AC-1: apps/sdlc/conventions/package-removal.md exists and documents all three phases — a git rm -r tracked delete, an untracked-residue sweep, and the deletions-only lockfile assertion — naming .moon/workspace.yml glob discovery as the reason the sweep is required.
  • AC-2: just --list includes a remove-package recipe, and that recipe’s body invokes apps/sdlc/scripts/remove_package.ts.
  • AC-3: Given a throwaway directory packages/ts/scratch-removal/ containing a tracked file and an untracked node_modules/ subtree, running the recipe against it leaves test ! -e packages/ts/scratch-removal true and moon query projects printing no scratch-removal line.
  • AC-4: grep -n "child_process" apps/sdlc/scripts/remove_package.ts returns no match, and the script imports from apps/sdlc/lib/util/command.ts and/or apps/sdlc/lib/services/git/easy.ts.
  • AC-5: The script exits nonzero when git diff --numstat <base> -- bun.lock Cargo.lock reports a nonzero additions column, and exits 0 when the lockfile diff is empty or deletions-only.
  • AC-6: apps/sdlc/skills/task-work/SKILL.md contains exactly one line matching conventions/package-removal.md, inside its ## Notes section, and the recipe’s prose is not duplicated into that file.
  • AC-7: just check-markdown exits 0 on the branch.
  • Adding a lefthook / CI gate that blocks a commit when residue survives. The recipe and its just tail are on-demand; enforcement is a separate decision.
  • Removing any actual package. This task ships the recipe, not a removal.
  • Teaching sdlc quality run or the moon task graph about the sweep.
  • Changing the discovery globs in .moon/workspace.yml to ignore tracked-file-free directories.
  • none

Spawned by /sdlc:spawn-task-pr on 2026-07-20 UTC from T-J5DI-remove-clear-duplicate-augmented-packages in sksizer/dev.


← Back to Tasks