- _has_unclosed_markup_block() now compares open / close tag counts.
A response with one closed <html> followed by a second still-open
<html> (multi-page mid-stream) or <svg></svg><svg> is unbalanced,
so the artifact path returns False and the re-prompt fires. The
helper now runs BEFORE _HAS_ANSWER_ARTIFACT so an earlier complete
artifact cannot mask a later open block.
- _looks_like_real_artifact() rejects empty <html></html> /
<svg></svg> skeletons. Plan-only mentions ("First, I'll create an
<html></html> skeleton, then add CSS.") no longer suppress the
re-prompt.
- _NUMBERED_ACTION_ITEM + _STRONG_INTENT_BEFORE_LIST catches plans
where the work verbs sit in the list ITEMS rather than before the
list (e.g. "First, I'll:\n1. Load the CSV.\n2. Compute total").
The verb whitelist is intentionally narrow (load, parse, calculate,
compute, analyze, run, execute, fetch, download, query, inspect,
extract) so ordinary algorithm answers ("First, use binary search:
1. Search the left half") stay valid. The intent gate excludes
bare "First" / "Step N:" for the same reason - direct first-person
pronoun is required.