Skip to content

t1220: AI-powered semantic dedup for task creation#1863

Merged
marcusquinn merged 1 commit intomainfrom
bugfix/t1220-ai-semantic-dedup
Feb 19, 2026
Merged

t1220: AI-powered semantic dedup for task creation#1863
marcusquinn merged 1 commit intomainfrom
bugfix/t1220-ai-semantic-dedup

Conversation

@marcusquinn
Copy link
Owner

@marcusquinn marcusquinn commented Feb 19, 2026

Summary

  • Replaces keyword-only duplicate detection (t1218) with a two-layer approach: fast keyword pre-filter + sonnet AI verification
  • Prevents the supervisor from creating near-duplicate tasks when the AI generates slightly different titles for the same underlying issue (e.g., 11 variants of "worker_never_started" tasks)
  • Falls back to keyword-only mode (3+ match threshold) if AI is unavailable or disabled

Architecture

  1. _keyword_prefilter_open_tasks() — Fast, free keyword scan. Extracts distinctive words from proposed title, counts overlap with open tasks. Returns top 5 candidates with 2+ keyword matches.

  2. _ai_semantic_dedup_check() — Calls sonnet via resolve_ai_cli() + resolve_model("sonnet"). Sends focused prompt with proposed title + candidate list. AI returns {"duplicate": true, "existing_task": "tXXXX", "confidence": "high|medium"} or {"duplicate": false}. 30s timeout.

  3. _check_similar_open_task() — Orchestrator: runs keyword pre-filter first (instant, free), then if candidates found and AI_SEMANTIC_DEDUP_USE_AI=true (default), calls sonnet for final verdict.

Test Results

Test Expected Actual
Duplicate title ("Add diagnostics for worker_never_started...") Blocked t1113 duplicate, high confidence
Genuinely new task ("Implement GraphQL subscription...") Allowed No candidates, passed
Similar keywords, different intent ("Add logging to dispatch...") Allowed Sonnet says NOT duplicate

Config

AI_SEMANTIC_DEDUP_MIN_MATCHES=2      # Pre-filter threshold (lowered, AI decides)
AI_SEMANTIC_DEDUP_USE_AI=true         # Enable sonnet verification
AI_SEMANTIC_DEDUP_TIMEOUT=30          # AI call timeout

Cost

~$0.002 per dedup check (only fires when AI proposes task creation, ~5-10 times per pulse cycle max).

Closes #1862

Summary by CodeRabbit

  • New Features
    • Added AI-powered semantic duplicate task detection with configurable timeout protection.
    • Enhanced duplicate candidate identification with improved sensitivity and better keyword-based pre-filtering.

Replace keyword-only duplicate detection with two-layer approach:
1. Fast keyword pre-filter scans open tasks for word overlap (free, instant)
2. Sonnet AI call verifies semantic similarity (~$0.002 per check)

This prevents the supervisor from creating near-duplicate tasks when the AI
generates slightly different titles for the same underlying issue. The keyword
pre-filter catches candidates, then sonnet makes the final call — understanding
that 'Investigate X failures' and 'Add diagnostics for X' are the same work.

Falls back to keyword-only (3+ match threshold) if AI is unavailable.

New config: AI_SEMANTIC_DEDUP_USE_AI, AI_SEMANTIC_DEDUP_TIMEOUT
@gemini-code-assist
Copy link

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 19, 2026

Walkthrough

This change implements a two-layer semantic deduplication system for task creation in the AI-actions automation script. It introduces AI-powered duplicate detection with configurable thresholds, timeout settings, and fallback behavior, replacing single-function logic with a keyword pre-filter and optional AI verification approach.

Changes

Cohort / File(s) Summary
AI-Powered Semantic Deduplication
.agents/scripts/supervisor/ai-actions.sh
Refactored dedup workflow with two-layer approach: keyword pre-filter (_keyword_prefilter_open_tasks) followed by optional AI semantic verification (_ai_semantic_dedup_check). Added configuration flags (AI_SEMANTIC_DEDUP_MIN_MATCHES, AI_SEMANTIC_DEDUP_USE_AI, AI_SEMANTIC_DEDUP_TIMEOUT). Lowered pre-filter threshold from 3 to 2 and expanded candidate collection to 5 results. Integrated JSON-driven AI response parsing with fallback to keyword-only logic when AI unavailable.

Sequence Diagram

sequenceDiagram
    participant TC as Task Creation Flow
    participant KPF as Keyword Pre-Filter
    participant AI as AI Service<br/>(Claude/Sonnet)
    participant FB as Fallback Logic
    participant DB as Task Database

    TC->>KPF: proposed_title
    KPF->>DB: extract keyword matches
    DB-->>KPF: candidates (top 5)
    KPF-->>TC: candidate list

    alt AI_SEMANTIC_DEDUP_USE_AI enabled
        TC->>AI: proposed_title + candidates
        alt AI responds within timeout
            AI-->>TC: JSON {duplicate, task_id, confidence}
            TC->>TC: evaluate AI result
            alt Duplicate confirmed
                TC-->>TC: skip creation, return existing
            else No duplicate
                TC-->>TC: proceed with creation
            end
        else AI timeout/unavailable
            TC->>FB: fallback to keyword logic
            FB->>FB: apply strict threshold (3+)
            FB-->>TC: keyword-only result
        end
    else AI disabled
        TC->>FB: use keyword-only logic
        FB-->>TC: fallback result
    end
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🤖 Two filters dance in semantic delight,
Keywords scout the field, AI shines bright,
Duplicate tasks won't hide anymore—
Claude peers deeper than counts before! ✨
Fallback's your friend when networks delay,
Zero debt maintained—automation's way! 🚀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: introducing AI-powered semantic deduplication for task creation, which is the core feature of this PR.
Linked Issues check ✅ Passed All coding requirements from issue #1862 are met: keyword pre-filter function, AI semantic dedup function using sonnet, configuration flags, timeout handling, and fallback behavior implemented.
Out of Scope Changes check ✅ Passed All changes are scoped to the deduplication workflow. The modifications align with the stated objectives of adding AI-powered semantic duplicate detection to task creation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix/t1220-ai-semantic-dedup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 28 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Thu Feb 19 01:40:18 UTC 2026: Code review monitoring started
Thu Feb 19 01:40:18 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 28

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 28
  • VULNERABILITIES: 0

Generated on: Thu Feb 19 01:40:20 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link

@marcusquinn marcusquinn merged commit 2a8c8ef into main Feb 19, 2026
10 of 11 checks passed
@marcusquinn marcusquinn deleted the bugfix/t1220-ai-semantic-dedup branch February 19, 2026 01:42
marcusquinn added a commit that referenced this pull request Feb 19, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
.agents/scripts/supervisor/ai-actions.sh (1)

375-379: Docstring misstates the output format — minor documentation inconsistency.

The header says "task_id|title_excerpt" (2 fields) but the actual output (Line 437) is task_id|match_count|excerpt (3 fields). Callers already use cut -d'|' -f3- correctly, but the doc will mislead future readers.

📝 Proposed fix
-#   Newline-separated list of "task_id|title_excerpt" for candidates
+#   Newline-separated list of "task_id|match_count|excerpt" for candidates (sorted by match_count desc, top 5)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/supervisor/ai-actions.sh around lines 375 - 379, The
docstring/header currently says the output is "task_id|title_excerpt" but the
actual echo prints three pipe-separated fields (task_id, match_count, excerpt)
using variables like match_count and excerpt; update the header to document the
correct format (e.g., "task_id|match_count|title_excerpt" or similar) so it
matches the echo that emits task_id|${match_count}|${excerpt} and callers that
use cut -d'|' -f3-.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/scripts/supervisor/ai-actions.sh:
- Around line 493-497: resolve_model always returns 0 and ignores the second
parameter, so the current "|| { log_warn ...; return 1; }" branch is dead and
the extra "$ai_cli" arg is unused; change the call to ai_model=$(resolve_model
"sonnet") (drop "$ai_cli"), then explicitly check the captured ai_model (e.g. if
[ -z "$ai_model" ]; then log_warn "AI Actions: semantic dedup AI check skipped —
sonnet model unavailable"; return 1; fi) so the script correctly handles a
missing model; reference resolve_model, ai_model, ai_cli and log_warn when
making the change.
- Around line 592-614: The current branch returns immediately when AI is enabled
but _ai_semantic_dedup_check fails, which skips the intended keyword-only
fallback; change the logic in the AI block so that if ai_result is set (success)
you print and return 0, but if _ai_semantic_dedup_check exits non‑zero (AI
unavailable/unreachable) you do NOT return 1 — instead log that AI was
unavailable (mention AI_SEMANTIC_DEDUP_USE_AI and ai_result) and let execution
fall through to the existing keyword-only fallback that uses candidates /
best_id / best_count; this ensures the 3+ keyword guard runs when the AI CLI is
down.
- Around line 519-537: Replace the fragile default-format + ANSI-strip + grep
JSON extraction with a robust JSON path: when calling opencode (check ai_cli and
the opencode invocation that sets ai_result using portable_timeout, ai_timeout
and ai_model), use --format json and pipe its output through jq to extract the
object (instead of the sed+grep sequence), and similarly for the claude branch
(claude_model/ai_result). Then remove the brittle json_block grep logic and
instead set json_block by parsing ai_result with jq (or by collapsing multiline
JSON and piping to jq) so pretty-printed responses are handled reliably.

---

Nitpick comments:
In @.agents/scripts/supervisor/ai-actions.sh:
- Around line 375-379: The docstring/header currently says the output is
"task_id|title_excerpt" but the actual echo prints three pipe-separated fields
(task_id, match_count, excerpt) using variables like match_count and excerpt;
update the header to document the correct format (e.g.,
"task_id|match_count|title_excerpt" or similar) so it matches the echo that
emits task_id|${match_count}|${excerpt} and callers that use cut -d'|' -f3-.

Comment on lines +493 to +497
local ai_model
ai_model=$(resolve_model "sonnet" "$ai_cli" 2>/dev/null) || {
log_warn "AI Actions: semantic dedup AI check skipped — sonnet model unavailable"
return 1
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Dead error handler — resolve_model always returns 0.

Per ai-reason.sh, resolve_model unconditionally returns 0 for any input (all case branches end with return 0). The || { log_warn "sonnet model unavailable"; return 1; } branch is unreachable dead code. The warning will never be emitted; if the model string is ever wrong, the failure surfaces only at the AI CLI invocation level. The extra $ai_cli argument passed to resolve_model is also silently ignored since the function only accepts $1.

🔧 Proposed fix
-	local ai_model
-	ai_model=$(resolve_model "sonnet" "$ai_cli" 2>/dev/null) || {
-		log_warn "AI Actions: semantic dedup AI check skipped — sonnet model unavailable"
-		return 1
-	}
+	local ai_model
+	ai_model=$(resolve_model "sonnet")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
local ai_model
ai_model=$(resolve_model "sonnet" "$ai_cli" 2>/dev/null) || {
log_warn "AI Actions: semantic dedup AI check skipped — sonnet model unavailable"
return 1
}
local ai_model
ai_model=$(resolve_model "sonnet")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/supervisor/ai-actions.sh around lines 493 - 497,
resolve_model always returns 0 and ignores the second parameter, so the current
"|| { log_warn ...; return 1; }" branch is dead and the extra "$ai_cli" arg is
unused; change the call to ai_model=$(resolve_model "sonnet") (drop "$ai_cli"),
then explicitly check the captured ai_model (e.g. if [ -z "$ai_model" ]; then
log_warn "AI Actions: semantic dedup AI check skipped — sonnet model
unavailable"; return 1; fi) so the script correctly handles a missing model;
reference resolve_model, ai_model, ai_cli and log_warn when making the change.

Comment on lines +519 to +537
if [[ "$ai_cli" == "opencode" ]]; then
ai_result=$(portable_timeout "$ai_timeout" opencode run \
-m "$ai_model" \
--format default \
--title "dedup-check-$$" \
"$prompt" 2>/dev/null || echo "")
# Strip ANSI escape codes
ai_result=$(printf '%s' "$ai_result" | sed 's/\x1b\[[0-9;]*[mGKHF]//g; s/\x1b\[[0-9;]*[A-Za-z]//g; s/\x1b\]//g; s/\x07//g')
else
local claude_model="${ai_model#*/}"
ai_result=$(portable_timeout "$ai_timeout" claude \
-p "$prompt" \
--model "$claude_model" \
--output-format text 2>/dev/null || echo "")
fi

# Parse the response — extract JSON from potentially noisy output
local json_block=""
json_block=$(printf '%s' "$ai_result" | grep -oE '\{[^}]+\}' | head -1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all opencode invocations in shell scripts
echo "=== All opencode invocations ==="
rg -n 'opencode' -A 4 -B 1 --type sh

Repository: marcusquinn/aidevops

Length of output: 50379


🏁 Script executed:

#!/bin/bash
# Also check for any opencode documentation or config files
echo "=== Files mentioning opencode ==="
rg -l 'opencode' --type sh --type txt --type md

echo ""
echo "=== Any opencode help or docs ==="
find . -type f \( -name "*opencode*" -o -name "*openai*" \) 2>/dev/null | head -20

Repository: marcusquinn/aidevops

Length of output: 6684


🏁 Script executed:

#!/bin/bash
# Check for JSON extraction patterns elsewhere in the codebase
echo "=== JSON extraction patterns in shell scripts ==="
rg -n 'grep.*\{' --type sh -A 2 -B 2

Repository: marcusquinn/aidevops

Length of output: 50377


🏁 Script executed:

#!/bin/bash
# Check the actual code at the specified lines
echo "=== ai-actions.sh lines 519-537 ==="
sed -n '515,540p' .agents/scripts/supervisor/ai-actions.sh

Repository: marcusquinn/aidevops

Length of output: 1036


🏁 Script executed:

#!/bin/bash
# Search for --format default usage
echo "=== Uses of --format default ==="
rg -n '\-\-format\s+default' --type sh

echo ""
echo "=== Uses of --format json ==="
rg -n '\-\-format\s+json' --type sh | head -20

Repository: marcusquinn/aidevops

Length of output: 2716


🏁 Script executed:

#!/bin/bash
# Check for opencode documentation files
echo "=== Check opencode.md documentation ==="
if [[ -f ".agents/tools/opencode/opencode.md" ]]; then
    head -100 ".agents/tools/opencode/opencode.md"
fi

Repository: marcusquinn/aidevops

Length of output: 2924


🏁 Script executed:

#!/bin/bash
# Look for any test/example patterns of opencode run with format flag
echo "=== All opencode run patterns ==="
rg 'opencode\s+run' --type sh -A 3 | head -60

Repository: marcusquinn/aidevops

Length of output: 3702


Use --format json with jq for robust JSON extraction, not --format default with grep.

Two issues with the current approach:

1. Suboptimal flag choice (Lines 519–524). The code uses --format default (standard OpenCode flag, not undocumented) with ANSI stripping and then grep -oE '\{[^}]+\}' to extract JSON. This works for single-line JSON but fails on pretty-printed responses. Elsewhere in the codebase (batch-cleanup-helper.sh, ai-reason.sh, agent-test-helper.sh), the pattern is to use --format json with jq, which is more robust.

2. grep pattern fragility (Line 537). The regex \{[^}]+\} requires the entire JSON object to sit on a single line. If Claude returns pretty-printed JSON (with newlines inside the braces), the extraction silently fails, returning empty, and the function falls back to keyword-only dedup.

🔧 Proposed fix
-	ai_result=$(portable_timeout "$ai_timeout" opencode run \
-		-m "$ai_model" \
-		--format default \
-		--title "dedup-check-$$" \
-		"$prompt" 2>/dev/null || echo "")
-	# Strip ANSI escape codes
-	ai_result=$(printf '%s' "$ai_result" | sed 's/\x1b\[[0-9;]*[mGKHF]//g; s/\x1b\[[0-9;]*[A-Za-z]//g; s/\x1b\]//g; s/\x07//g')
+	ai_result=$(portable_timeout "$ai_timeout" opencode run \
+		-m "$ai_model" \
+		--format json \
+		--title "dedup-check-$$" \
+		"$prompt" 2>/dev/null || echo "")
 
 	# Parse the response — extract JSON from potentially noisy output
 	local json_block=""
-	json_block=$(printf '%s' "$ai_result" | grep -oE '\{[^}]+\}' | head -1)
+	json_block=$(printf '%s' "$ai_result" | jq -r '.content[] | select(.type=="text") | .text' 2>/dev/null | grep -oE '\{[^}]+\}' | head -1)

Or, matching the pattern from ai-reason.sh, collapse multi-line JSON before extraction if sticking with --format default:

-	json_block=$(printf '%s' "$ai_result" | grep -oE '\{[^}]+\}' | head -1)
+	json_block=$(printf '%s' "$ai_result" | tr '\n' ' ' | grep -oE '\{[^}]+\}' | head -1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/supervisor/ai-actions.sh around lines 519 - 537, Replace the
fragile default-format + ANSI-strip + grep JSON extraction with a robust JSON
path: when calling opencode (check ai_cli and the opencode invocation that sets
ai_result using portable_timeout, ai_timeout and ai_model), use --format json
and pipe its output through jq to extract the object (instead of the sed+grep
sequence), and similarly for the claude branch (claude_model/ai_result). Then
remove the brittle json_block grep logic and instead set json_block by parsing
ai_result with jq (or by collapsing multiline JSON and piping to jq) so
pretty-printed responses are handled reliably.

Comment on lines +592 to 614
# Step 2: AI semantic check (if enabled and CLI available)
if [[ "${AI_SEMANTIC_DEDUP_USE_AI:-true}" == "true" ]]; then
local ai_result
if ai_result=$(_ai_semantic_dedup_check "$title" "$candidates"); then
printf '%s' "$ai_result"
return 0
fi
# AI said not a duplicate or was unavailable — trust the AI over keywords
log_info "AI Actions: semantic dedup: AI did not confirm duplicate, allowing task creation"
return 1
fi

# Fallback: keyword-only mode (AI disabled) — require higher threshold
local best_id best_count
best_id=$(printf '%s' "$candidates" | head -1 | cut -d'|' -f1)
best_count=$(printf '%s' "$candidates" | head -1 | cut -d'|' -f2)

# Without AI confirmation, require 3+ keyword matches (stricter)
if [[ -n "$best_id" && "$best_count" -ge 3 ]]; then
log_info "AI Actions: semantic dedup (keyword-only fallback): $best_id matches with $best_count keywords"
printf '%s' "$best_id"
return 0
fi
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

AI-unavailable path skips keyword fallback — contradicts stated behavior.

When AI_SEMANTIC_DEDUP_USE_AI=true and the AI CLI is unreachable, _ai_semantic_dedup_check returns 1, the outer if on line 595 fails, and the function immediately returns 1 at line 601 — allowing task creation with zero dedup protection, even though keyword candidates were found.

The keyword-only fallback block at lines 604–614 is only reachable when AI_SEMANTIC_DEDUP_USE_AI=false. The PR description explicitly states "Falls back to keyword-only mode (3+ match threshold) if AI is unavailable or disabled", but the current code only falls back if disabled. An AI CLI outage effectively disables dedup entirely.

If the intended behavior is to always use the keyword guard as a last line of defence when AI is unavailable, consider:

🔧 Proposed fix to honour the stated fallback contract
 	# Step 2: AI semantic check (if enabled and CLI available)
 	if [[ "${AI_SEMANTIC_DEDUP_USE_AI:-true}" == "true" ]]; then
 		local ai_result
 		if ai_result=$(_ai_semantic_dedup_check "$title" "$candidates"); then
 			printf '%s' "$ai_result"
 			return 0
 		fi
-		# AI said not a duplicate or was unavailable — trust the AI over keywords
-		log_info "AI Actions: semantic dedup: AI did not confirm duplicate, allowing task creation"
-		return 1
+		# AI said not a duplicate — trust the AI verdict
+		# (If AI was unavailable, _ai_semantic_dedup_check already logged a warning;
+		#  fall through to keyword-only as a safety net)
+		log_info "AI Actions: semantic dedup: AI did not confirm duplicate, checking keyword threshold"
 	fi
 
 	# Fallback: keyword-only mode (AI disabled or unavailable) — require higher threshold
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Step 2: AI semantic check (if enabled and CLI available)
if [[ "${AI_SEMANTIC_DEDUP_USE_AI:-true}" == "true" ]]; then
local ai_result
if ai_result=$(_ai_semantic_dedup_check "$title" "$candidates"); then
printf '%s' "$ai_result"
return 0
fi
# AI said not a duplicate or was unavailable — trust the AI over keywords
log_info "AI Actions: semantic dedup: AI did not confirm duplicate, allowing task creation"
return 1
fi
# Fallback: keyword-only mode (AI disabled) — require higher threshold
local best_id best_count
best_id=$(printf '%s' "$candidates" | head -1 | cut -d'|' -f1)
best_count=$(printf '%s' "$candidates" | head -1 | cut -d'|' -f2)
# Without AI confirmation, require 3+ keyword matches (stricter)
if [[ -n "$best_id" && "$best_count" -ge 3 ]]; then
log_info "AI Actions: semantic dedup (keyword-only fallback): $best_id matches with $best_count keywords"
printf '%s' "$best_id"
return 0
fi
# Step 2: AI semantic check (if enabled and CLI available)
if [[ "${AI_SEMANTIC_DEDUP_USE_AI:-true}" == "true" ]]; then
local ai_result
if ai_result=$(_ai_semantic_dedup_check "$title" "$candidates"); then
printf '%s' "$ai_result"
return 0
fi
# AI said not a duplicate — trust the AI verdict
# (If AI was unavailable, _ai_semantic_dedup_check already logged a warning;
# fall through to keyword-only as a safety net)
log_info "AI Actions: semantic dedup: AI did not confirm duplicate, checking keyword threshold"
fi
# Fallback: keyword-only mode (AI disabled or unavailable) — require higher threshold
local best_id best_count
best_id=$(printf '%s' "$candidates" | head -1 | cut -d'|' -f1)
best_count=$(printf '%s' "$candidates" | head -1 | cut -d'|' -f2)
# Without AI confirmation, require 3+ keyword matches (stricter)
if [[ -n "$best_id" && "$best_count" -ge 3 ]]; then
log_info "AI Actions: semantic dedup (keyword-only fallback): $best_id matches with $best_count keywords"
printf '%s' "$best_id"
return 0
fi
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/scripts/supervisor/ai-actions.sh around lines 592 - 614, The current
branch returns immediately when AI is enabled but _ai_semantic_dedup_check
fails, which skips the intended keyword-only fallback; change the logic in the
AI block so that if ai_result is set (success) you print and return 0, but if
_ai_semantic_dedup_check exits non‑zero (AI unavailable/unreachable) you do NOT
return 1 — instead log that AI was unavailable (mention AI_SEMANTIC_DEDUP_USE_AI
and ai_result) and let execution fall through to the existing keyword-only
fallback that uses candidates / best_id / best_count; this ensures the 3+
keyword guard runs when the AI CLI is down.

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.

t1220: AI-powered semantic dedup for task creation using sonnet model

1 participant