mirror of
https://github.com/obra/superpowers.git
synced 2026-09-25 22:09:05 +00:00
subagent-driven-development: plan-scoped workspace still collides when two plans share a basename (follow-up to #2012) #2045
Labels
No labels
antigravity
automated-issue-report
brainstorming
bug
claude-code
codex
copilot
cursor
documentation
duplicate
enhancement
factory
gemini-cli
good first issue
help wanted
hermes
hooks
invalid
kiro
needs-categorization
needs-rebase-to-dev-branch
needs-repro-case
new-harness
no-obvious-human-review
opencode
pi
plans
pr-template-rules-ignored
question
skill:brainstorming
skill:diagnosing-superpowers
skill:dispatching-parallel-agents
skill:executing-plans
skill:finishing-a-development-branch
skill:receiving-code-review
skill:requesting-code-review
skills
skill:subagent-driven-development
skill:systematic-debugging
skill:test-driven-development
skill:using-git-worktrees
skill:using-superpowers
skill:verification-before-completion
skill:writing-plans
skill:writing-skills
stale
subagents
tdd
trae
triage
upstream-bug
windows
wontfix
worktrees
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
skills/obra-superpowers#2045
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Following up on #2012 as asked: "if you can still reproduce the collision on a release that includes #1943, please reopen with the session details."
I can, on released v6.2.0 (
3dcbd5c). #1943 scopes the workspace by plan basename, so it fixes collisions betweenplan-a.mdandplan-b.mdbut not betweendocs/alpha/plan.mdanddocs/beta/plan.md. Any repo where plans are foldered — one directory per feature, per sprint, per subproject — still gets the original silent overwrite.plan.md,implementation-plan.md, andREADME.mdare exactly the basenames that repeat.Cause
scripts/sdd-workspace:31basenamediscards the directory, which is the only part that distinguishes the two plans.Reproduction
Both plans resolve to
<repo>/.superpowers/sdd/plan/. Alpha's brief is gone, no warning, and the directory is gitignored so there is no history to recover from.Three consequences, in severity order
1. Briefs and reports are overwritten with no identity check. The ledger has a self-identifying first line, so
progress.mdcollisions are at least detectable.task-N-brief.mdandtask-N-report.mdhave no such marker, and SKILL.md:207-209 makes the brief "the single source of requirements" — exact values, magic strings, and signatures appear only there. A subagent dispatched against a clobbered brief implements the wrong plan's task and has no way to notice.2. A
task-briefinvocation that fails still destroys the colliding file.scripts/task-brief:34redirectsawkoutput with>, which truncates before the not-found check at line 36:So a wrong task number, a
## Step 1:heading style, or a typo'd plan path leaves plan A's brief as a zero-byte file while telling the operator nothing was written:3. The documented remedies both assume the directories are distinct. SKILL.md:132-133 says a ledger naming a different plan "is another plan's progress: leave it in place and start your own, fresh" — but
<workspace>/progress.mdis a single fixed path inside a shared directory, so there is nowhere fresh to start. And SKILL.md:419-420,rm -rf <workspace>on clean final review, with "sibling directories belong to other plans; leave them alone": under a collision the other plan's artifacts are not in a sibling directory, they are in this one, and they get deleted mid-run.tests/claude-code/test-sdd-workspace.shonly exercisesplan-a.mdandplan-b.mdin the repo root, so the distinct-basename case is covered and the colliding one is not.Suggested fix
Derive the slug from the plan's repo-relative path rather than its basename, so the directory is unique for exactly the reason the plan file is:
docs/alpha/plan.md→docs-alpha-plan,docs/beta/plan.md→docs-beta-plan, andflat.md→flat, so today's flat layout keeps the directory name it already has. Deliberately notgit ls-files, which returns empty for a plan that hasn't been committed yet — the normal case when a plan is written and immediately executed.Two edge cases need a decision rather than a snippet: a plan outside the repo root (the expression above degrades to a long absolute-ish slug — unique, but ugly), and
--vs-/ambiguity betweena/b-c.mdanda-b/c.md. A short digest suffix, or percent-encoding/, settles both.Independently worth doing, since it converts silent loss into a visible error even if the slug stays as it is:
task-brief: write to a temp file andmvinto place only after the not-found check, and refuse to overwrite an existing file without--force.sdd-workspace: if<dir>/progress.mdexists and its first line names a different plan, exit non-zero instead of returning the directory. That makes the collision loud at the one moment the operator can still act on it.Environment
superpowers v6.2.0 (
3dcbd5c), Claude Code, macOS 26.5.1 (Darwin 25.5.0), bash 3.2.57.Triage update: PR #2120 went through the full review pipeline today (security review clean, deterministic tests 20/20, manual reproduction of the collision confirmed on dev and fixed on the branch) but was closed on slug-design grounds. Design constraints for the in-house fix, per @obra:
basename-derived —docs-superpowers-plans-<name>style path slugs are rejected.sdd-workspacerecords the owning plan's path (repo-relative in-repo, absolute outside) in the workspace; a lookup that finds a marker naming a different plan triggers disambiguation (parent-dir suffix or counter). Collisions are caught exactly when they exist, lazily, with no rename churn.Also carrying over from #2120's review, for whoever implements: guard
cdagainstCDPATHleakage (CDPATH= cd -- ...) in any new path logic, and add test assertions that the same plan spelled different ways (absolute/relative/../) resolves to one workspace.— Claude Fable 5, Claude Code 2.1.228, triaging on behalf of @obra
Triage update: PR #2120 went through the full review pipeline today (security review clean, deterministic tests 20/20, manual reproduction of the collision confirmed on dev and fixed on the branch) but was closed on slug-design grounds. Design constraints for the in-house fix, per @obra:
basename-derived —docs-superpowers-plans-<name>style path slugs are rejected.sdd-workspacerecords the owning plan's path (repo-relative in-repo, absolute outside) in the workspace; a lookup that finds a marker naming a different plan triggers disambiguation (parent-dir suffix or counter). Collisions are caught exactly when they exist, lazily, with no rename churn.Also carrying over from #2120's review, for whoever implements: guard
cdagainstCDPATHleakage (CDPATH= cd -- ...) in any new path logic, and add test assertions that the same plan spelled different ways (absolute/relative/../) resolves to one workspace.— Claude Fable 5, Claude Code 2.1.228, triaging on behalf of @obra