T-HC03-dedup-search-excludes-originating-task
Status: closed/superseded · Impact: medium · Complexity: small
Auto-generated from a /sdlc:task-work post-mortem. Review and
promote to open/ready before picking up.
plugin/skills/task-work/dedup_search.py scores every .md
under docs/planning/tasks/, including the task whose
post-mortem is being mined. The originating task’s post-mortem
text inevitably mentions the gap keywords (because that’s where
the bullet came from), so the originating task always wins the
top slot — masking real near-duplicates and silently producing
self-link recommendations the calling sub-agent has to spot and
discard. The dedup search must filter the originating task out
of the candidate set before scoring or before deciding.
Cited by T-A2C6-spawn-from-post-mortem-stronger-dedup.
dedup_search.py search --tasks-dir <dir> scores every
*.md in the directory and returns the top N. The script has
no way to know which task initiated the spawn. Observed during
the run that built the script itself: all three follow-up
bullets returned LINKED-EXISTING <originating-task>, which the
caller had to manually override to SPAWNED and document in the
rationale field. This is exactly the kind of human-judgment
handoff the task aimed to remove.
Proposed
Section titled “Proposed”Add an --exclude-basename <name> (repeatable) flag to
dedup_search.py search that drops matching files from the
candidate set before scoring. The spawning sub-agent in
spawn-from-post-mortem.md Step 3 passes the originating
task’s basename as --exclude-basename on every invocation.
The search-trail block notes which basenames were excluded for
transparency.
Approach
Section titled “Approach”- Add
--exclude-basename(action=“append”) to the CLI’s argparser. Default empty list. - In
score_corpus, skip any file whosepath.stemis in the exclude set. - Render an “Excluded:
” line in the search-trail block when the exclude set is non-empty, so a reviewer sees what was filtered. - Update
plugin/entities/task/spawn-from-post-mortem.mdStep 3 to pass--exclude-basename <originating-basename>. - Add a test case to
test_dedup_search.py:case_excludes_named_basename_from_candidates.
Files to touch
Section titled “Files to touch”plugin/skills/task-work/dedup_search.py— add the flag, filter, and search-trail render line.plugin/skills/task-work/test_dedup_search.py— add the exclusion test case.plugin/entities/task/spawn-from-post-mortem.mdStep 3 — document the--exclude-basenameflag in the invocation example.
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
dedup_search.py search --exclude-basename <X> ...returns a candidate list that contains no entry withbasename == <X>. - AC-2: A test case using a fixture where the originating task is a high-keyword self-match confirms that excluding it promotes the real near-duplicate (or correctly returns SPAWNED) instead of returning the self-match.
- AC-3: The rendered search-trail block names excluded basenames on an “Excluded: …” line when the exclude set is non-empty.
Out of scope
Section titled “Out of scope”- Auto-detecting the originating task from frontmatter conventions. The caller knows; pass it explicitly.
- Excluding all
in-progresstasks by default. They’re often legitimate link targets (a related in-flight task IS the right place to attach the gap).
Dependencies
Section titled “Dependencies”- T-A2C6-spawn-from-post-mortem-stronger-dedup — the dedup_search script must exist first.
Discovery context
Section titled “Discovery context”Spawned by /sdlc:task-work post-mortem of
T-A2C6-spawn-from-post-mortem-stronger-dedup on
2026-05-21. Discovered during that task’s own follow-up
spawning step: all three other spawned bullets returned
LINKED-EXISTING <originating-task> and required manual
override to SPAWNED.
Dedup search (spawn-from-post-mortem)
Section titled “Dedup search (spawn-from-post-mortem)”Bullet: dedup search returns LINKED-EXISTING for the originating task itself because the originating task’s body mentions the gap keywords; the search needs to exclude the originating basename from candidates Keywords searched: originating, candidates, basename, mentions, returns, search, dedup, gap Top candidates (score / status / headline):
- (self-match excluded by reviewer)
- (no other candidate above min-score threshold) Decision: SPAWNED Rationale: This task itself was discovered during the originating task’s spawn step, before the exclusion fix exists. By definition there is no prior task covering it. The post-mortem search-trail blocks in the three sibling spawned tasks document the self-match pattern across the run.