Skip to content

fix(taskstoissues): scope issue dedup to the feature the tasks belong to (#4271) - #4314

Open
ntdatt812 wants to merge 1 commit into
github:mainfrom
ntdatt812:fix/4271-taskstoissues-feature-scope
Open

fix(taskstoissues): scope issue dedup to the feature the tasks belong to (#4271)#4314
ntdatt812 wants to merge 1 commit into
github:mainfrom
ntdatt812:fix/4271-taskstoissues-feature-scope

Conversation

@ntdatt812

Copy link
Copy Markdown
Contributor

Closes #4271.

Task IDs are local to a feature — every tasks.md restarts at T001 — but the dedup step matched existing issues on the bare ID:

For each issue title, match it against the task ID pattern \bT\d{3,}\b … and, when it matches one of your task IDs, mark that ID as already having an issue.

So once 001-auth had an issue titled T001: …, running the command for 002-billing saw "T001 exists" and skipped it. The issue was never created and the report said T001 already has an issue, skipping, which reads like success. Every feature after the first loses its low-numbered tasks, and it degrades with repo age rather than showing up on the first run.

This is the inverse of #2968: tightening the matching to stop duplicates made it aggressive enough to suppress valid tasks.

What changed

The canonical title now carries the feature. FEATURE_DIR is already parsed in step 1, so its basename — the NNN-name spec directory — goes in front:

[002-billing] T001: Create project structure

The ID keeps its own word boundaries inside that prefix, so the \bT\d{3,}\b matching #2968 established still works unchanged, including the four-digit case.

A task is skipped only when both match. Feature identity and task ID. A T001 belonging to another feature is a different task and no longer suppresses this one.

Existing issues still count. Anything filed before the prefix existed carries a bare T001: …. Those are treated as matching only when no [<feature>]-prefixed issue for that ID is present in the fetched set, so upgrading does not re-create work that is already tracked — which would have been the obvious way to turn this fix into a fresh duplicate problem.

On the choice of title over label

The issue offers "title or a label". Title, for two reasons: the dedup already reads titles from list_issues, so nothing new is fetched and no extra call is added to a step that is explicitly written to bound its pagination; and a label needs create-label permission and a repo-side convention, which is a heavier requirement for a command whose whole contract is "create issues on the remote". A label would be the better choice if the identifier had to be filterable in the GitHub UI — happy to switch if you would rather have that.

I did not implement the second bullet of the issue (re-checking immediately before creation so two concurrent invocations cannot race). It is a real gap, but it is a different change — the fetch-then-create window — and it is worth its own PR rather than being folded in here where it would be untested.

Tests

tests/unit/test_taskstoissues_feature_scope.py, three cases over the template text:

  • the canonical title rule names [<feature>] T001: and says where <feature> comes from
  • the skip rule requires both the feature and the ID
  • the pre-existing-issue clause is still present, so an upgrade cannot start duplicating

Each assertion fails with the offending instruction line quoted, and _line_containing raises if the instruction disappears entirely — so deleting the step cannot make the suite pass vacuously.

Mutation-checked — dropping [<feature>] from the canonical title fails exactly one case:

1 failed, 2 passed
python -m pytest tests/unit/ -q
469 passed, 2 skipped, 2 failed

The two failures are test_add_source_refuses_symlinked_specify_escape and test_save_records_refuses_symlinked_specify_escape. Both fail identically on main on this machine — creating a symlink on Windows needs elevation — measured on a clean checkout before this branch, not assumed.

Task IDs are local to a feature -- every tasks.md restarts at T001 -- but the
dedup matched existing issues on the bare ID. So once feature 001-auth had an
issue titled T001, running the command for 002-billing saw "T001 exists" and
skipped it. The task was never created and nothing said so, which is a silent
gap in exactly the multi-feature repos this command targets.

The canonical title now carries the feature directory basename, and a task is
skipped only when an existing issue matches both that identifier and the ID.
The ID keeps its own word boundaries inside the prefixed title, so the
\bT\d{3,}\b matching from github#2968 is unchanged.

Issues filed before the prefix existed carry a bare `T001: ...`; those are
still recognised for their own feature, so upgrading does not re-create work
that is already tracked.

Closes github#4271
@ntdatt812
ntdatt812 requested a review from mnriem as a code owner August 25, 2026 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: /speckit-taskstoissues dedup matches bare task IDs across features — valid tasks are silently skipped

1 participant