Skip to content

Commit 37f3f49

Browse files
committed
Fix DetectIntent ordering bug and substring matching in parse hints
- Reorder DetectIntent checks so "unarchive" is checked before "archive" (previously "unarchive" was always misclassified as "archive" because the string contains the substring) - Switch from substring matching to whole-word matching in both DetectIntent and FindClosestPattern to prevent false positives (e.g. "sunset" matching "set", "address" matching "add") - Use lastIndexOf for PARSE_HINT marker in frontend to handle edge case where LLM response contains the marker text - Add regression tests for "unarchive my board" and "rename board"
1 parent 58371a7 commit 37f3f49

File tree

3 files changed

+20
-12
lines changed

3 files changed

+20
-12
lines changed

backend/src/Taskdeck.Application/Services/AutomationPlannerService.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -461,19 +461,25 @@ internal static string BuildParseHintMessage(string instruction)
461461
internal static string? DetectIntent(string instruction)
462462
{
463463
var lower = instruction.Trim().ToLowerInvariant();
464+
var words = lower.Split(' ', StringSplitOptions.RemoveEmptyEntries);
465+
466+
// Check more-specific intents before their substrings (e.g. "unarchive" before "archive",
467+
// "rename" before "new"). Use word-level matching to avoid substring false positives
468+
// like "sunset" matching "set" or "address" matching "add".
469+
bool hasWord(string word) => words.Any(w => w == word);
464470

465-
if (lower.Contains("create") || lower.Contains("add") || lower.Contains("new"))
471+
if (lower.Contains("unarchive") || hasWord("restore"))
472+
return "unarchive";
473+
if (lower.Contains("rename") || hasWord("edit") || hasWord("change") || hasWord("update"))
474+
return "update";
475+
if (hasWord("reorder") || hasWord("position"))
476+
return "reorder";
477+
if (hasWord("create") || hasWord("add") || hasWord("new"))
466478
return "create";
467-
if (lower.Contains("move") || lower.Contains("drag") || lower.Contains("transfer"))
479+
if (hasWord("move") || hasWord("drag") || hasWord("transfer"))
468480
return "move";
469-
if (lower.Contains("archive") || lower.Contains("remove") || lower.Contains("delete"))
481+
if (hasWord("archive") || hasWord("remove") || hasWord("delete"))
470482
return "archive";
471-
if (lower.Contains("update") || lower.Contains("edit") || lower.Contains("change") || lower.Contains("rename") || lower.Contains("set"))
472-
return "update";
473-
if (lower.Contains("unarchive") || lower.Contains("restore"))
474-
return "unarchive";
475-
if (lower.Contains("reorder") || lower.Contains("position"))
476-
return "reorder";
477483

478484
return null;
479485
}
@@ -490,10 +496,10 @@ internal static (string Pattern, string Example) FindClosestPattern(string instr
490496
{
491497
var score = 0;
492498

493-
// Boost patterns whose keywords overlap with the instruction words
499+
// Boost patterns whose keywords match whole words in the instruction
494500
foreach (var keyword in entry.Keywords)
495501
{
496-
if (words.Any(w => w.Contains(keyword)))
502+
if (words.Any(w => w == keyword))
497503
score += 2;
498504
}
499505

backend/tests/Taskdeck.Application.Tests/Services/AutomationPlannerServiceTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -860,6 +860,8 @@ public async Task ParseInstruction_ShouldReturnStructuredParseHint_ForUnrecogniz
860860
[InlineData("delete old stuff", "archive")]
861861
[InlineData("change the card name", "update")]
862862
[InlineData("restore the board", "unarchive")]
863+
[InlineData("unarchive my board", "unarchive")]
864+
[InlineData("rename board to Sprint 5", "update")]
863865
public void DetectIntent_ShouldIdentifyIntent_FromNaturalLanguage(string instruction, string expectedIntent)
864866
{
865867
// Act

frontend/taskdeck-web/src/utils/chat.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export interface ParsedHintMessage {
2020
}
2121

2222
export function extractParseHint(content: string): ParsedHintMessage | null {
23-
const markerIndex = content.indexOf(PARSE_HINT_MARKER)
23+
const markerIndex = content.lastIndexOf(PARSE_HINT_MARKER)
2424
if (markerIndex === -1) {
2525
return null
2626
}

0 commit comments

Comments
 (0)