Skip to content

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.

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.

  1. Add --exclude-basename (action=“append”) to the CLI’s argparser. Default empty list.
  2. In score_corpus, skip any file whose path.stem is in the exclude set.
  3. Render an “Excluded: ” line in the search-trail block when the exclude set is non-empty, so a reviewer sees what was filtered.
  4. Update plugin/entities/task/spawn-from-post-mortem.md Step 3 to pass --exclude-basename <originating-basename>.
  5. Add a test case to test_dedup_search.py: case_excludes_named_basename_from_candidates.
  • 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.md Step 3 — document the --exclude-basename flag in the invocation example.
  • AC-1: dedup_search.py search --exclude-basename <X> ... returns a candidate list that contains no entry with basename == <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.
  • Auto-detecting the originating task from frontmatter conventions. The caller knows; pass it explicitly.
  • Excluding all in-progress tasks by default. They’re often legitimate link targets (a related in-flight task IS the right place to attach the gap).

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.

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.

← Back to Tasks