Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions engine/hooks/diu-stop/claude_stop_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
FENCE_MARKER = "```"
FENCED_BODY_RE = re.compile(r"```[^\n]*\n(.*?)```", re.DOTALL)
INLINE_CODE_RE = re.compile(r"`([^`\n]+)`")
FILE_LINE_RE = re.compile(r"(?<![\w/.-])(?:[\w.-]+/)*[\w-]+\.[A-Za-z]\w*(?::\d+\b|#L\d+\b)")
OUTPUT_SHAPE_RE = re.compile(
r"^(?:\$ |> |\+\+\+ |--- |@@ |diff --git|commit [0-9a-f]{7,}|[0-9a-f]{7,10} )"
r"|Traceback|^\s*at [\w.$<>]+ \(.*:\d+:\d+\)"
Expand Down Expand Up @@ -143,10 +144,15 @@ def find_unverified_claim(message):
truth check."""
fenced_output = any(OUTPUT_SHAPE_RE.search(body) for body in FENCED_BODY_RE.findall(message))
for para in re.split(r"\n\s*\n", message):
para = FENCED_BODY_RE.sub("", para)
if not para.strip():
continue
if FENCE_MARKER in para:
continue
if markers.excuses_paragraph(para):
continue
if FILE_LINE_RE.search(para):
continue
inline = INLINE_CODE_RE.findall(para)
if inline and (fenced_output or any(OUTPUT_SHAPE_RE.search(code) for code in inline)):
continue
Expand Down
25 changes: 25 additions & 0 deletions engine/hooks/diu-stop/tests/test_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,31 @@ def test_unrelated_later_backtick_does_not_suppress_causal_claim(self):
self.assertTrue(blocked)
self.assertIn("unverified-shaped claim", err.lower())

def test_fenced_block_does_not_silence_later_prose_in_same_paragraph(self):
message = (
"I checked the local snippet.\n"
"```text\n"
"unrelated fenced content\n"
"```\n"
"This fixes it now."
)
blocked, err = run_claude_check({"last_assistant_message": message})
self.assertTrue(blocked)
self.assertIn("this fixes it", err.lower())

def test_file_line_citation_still_silences_claim_after_fence_normalization(self):
message = (
"I checked the relevant snippet.\n"
"```ts\n"
"const guard = true;\n"
"```\n"
"The issue was the stale guard at file.ts:1276."
)
self.assertIsNone(claude_stop_check.find_unverified_claim(message))
blocked, err = run_claude_check({"last_assistant_message": message})
self.assertFalse(blocked)
self.assertEqual(err, "")

def test_hedge_i_think_it_happened_without_evidence_is_flagged(self):
message = "I think the deploy happened around 2am, so that's why the build is stale."
blocked, err = run_claude_check({"last_assistant_message": message})
Expand Down
Loading