Studio: r19 fixes - cross-strip closed artifacts, iterate skeleton matches, bare-intent colon plan

- _has_answer_artifact() now strips closed code fences before checking
  for unclosed markup, and strips closed markup before checking for
  unclosed code fences. A Python / JS snippet containing literal
  "<html>" / "<svg>" strings no longer trips the unclosed-markup
  cross-check, and complete HTML containing a JS string with literal
  backticks no longer trips the unclosed-fence cross-check.

- _looks_like_real_artifact() iterates every artifact match. An empty
  <html></html> / <svg></svg> skeleton followed by a real complete
  page no longer hides the real artifact.

- _is_empty_markup_skeleton() strips an optional <!doctype ...> prefix
  before testing the empty-skeleton pattern, so
  "<!doctype html><html></html>" plan-only mentions also re-prompt.

- _BARE_INTENT_NUMBERED_PLAN catches the tight "I'll:\n1. Open ..." /
  "Let me:\n1. Parse ..." shape where bare first-person intent +
  colon + newline is immediately followed by numbered action items.
  No work verb is required between the intent and the list.
This commit is contained in:
Daniel Han 2026-05-24 21:22:26 +00:00
commit 2ea2f3519a
2 changed files with 143 additions and 10 deletions

View file

@ -119,6 +119,16 @@ _MAX_REPROMPTS = 3
# `<html>` or `<!doctype>` do not bypass the re-prompt.
# * All `[\s\S]{...}?` runs are length-bounded so the search stays
# linear on adversarial input (CRLF spam, repeated `<html>` etc.).
_CLOSED_CODE_FENCE = re.compile(
r"(?<!`)(?P<bf>`{3,})(?!`)[^\r\n]{0,200}\r?\n[\s\S]{1,4000}?\r?\n[ \t]*(?P=bf)`*[ \t]*(?:\r?\n|\Z)"
r"|(?<!~)(?P<tf>~{3,})(?!~)[^\r\n]{0,200}\r?\n[\s\S]{1,4000}?\r?\n[ \t]*(?P=tf)~*[ \t]*(?:\r?\n|\Z)",
re.IGNORECASE,
)
_CLOSED_MARKUP_ARTIFACT = re.compile(
r"(?:<!doctype\b[\s\S]{0,200}?)?<html\b[\s\S]{0,4000}?</html>"
r"|<svg\b[\s\S]{0,4000}?</svg>",
re.IGNORECASE,
)
_HAS_ANSWER_ARTIFACT = re.compile(
# Closed backtick code fence (any markdown info string, optional indent
# on close). CommonMark allows opening fences of 3+ backticks; the
@ -254,6 +264,18 @@ _EMPTY_MARKUP_SKELETON = re.compile(
r"<(html|svg)\b[^>]*>\s*</\1>",
re.IGNORECASE,
)
_DOCTYPE_PREFIX = re.compile(
r"^<!doctype\b[\s\S]{0,200}?>",
re.IGNORECASE,
)
def _is_empty_markup_skeleton(matched: str) -> bool:
"""True if ``matched`` is just an empty <html></html> / <svg></svg>
(optionally with a `<!doctype>` prefix and surrounding whitespace).
These read as plan-only mentions, not substantive answers."""
candidate = _DOCTYPE_PREFIX.sub("", matched.strip(), count=1).strip()
return _EMPTY_MARKUP_SKELETON.fullmatch(candidate) is not None
# A numbered list whose item lines start with a strong work / tool
@ -283,16 +305,34 @@ _STRONG_INTENT_BEFORE_LIST = re.compile(
re.IGNORECASE,
)
# Bare first-person intent immediately followed by ``:`` and a numbered
# list whose first item begins with a work verb. Catches
# ``I'll:\n1. Open the URL`` and ``Let me:\n1. Parse the JSON`` where
# no work verb appears between the intent phrase and the list. The
# verb set here is broader than _LOCAL_ACTION_VERBS because the
# tight ``intent + : + newline + numbered`` shape is itself the strong
# signal that this is a tool stall.
_BARE_INTENT_NUMBERED_PLAN = re.compile(
r"\b(?:i['](?:ll|m going to|m gonna)|i am (?:going to|gonna)|"
r"i will|i shall|let me|allow me|now i|next i)\s*:[ \t]*"
r"(?:\r?\n)[ \t]*\d+\.[ \t]+"
r"(?:open|read|search|look up|check|verify|create|build|add|set up|"
r"load|inspect|parse|calculate|compute|analy[sz]e|extract|run|execute|"
r"fetch|download|query|summari[sz]e|implement|generate|draft|write)\b",
re.IGNORECASE,
)
def _looks_like_real_artifact(text: str) -> bool:
"""Match _HAS_ANSWER_ARTIFACT but reject empty markup skeletons."""
m = _HAS_ANSWER_ARTIFACT.search(text)
if not m:
return False
matched = m.group(0).strip()
if _EMPTY_MARKUP_SKELETON.fullmatch(matched):
return False
return True
"""Match _HAS_ANSWER_ARTIFACT but reject empty markup skeletons.
Iterates every artifact match in ``text`` so an empty <html></html>
skeleton followed by a real complete page still classifies as a
real artifact (the second match wins)."""
for m in _HAS_ANSWER_ARTIFACT.finditer(text):
if not _is_empty_markup_skeleton(m.group(0)):
return True
return False
def _has_answer_artifact(text: str) -> bool:
@ -310,9 +350,16 @@ def _has_answer_artifact(text: str) -> bool:
answer, even when an earlier complete artifact is also present.
Empty `<html></html>` / `<svg></svg>` skeletons do not count.
"""
if _has_unclosed_code_fence(text):
# Cross-strip closed artifacts before the unclosed-state checks so
# delimiter-like content INSIDE a complete code fence (e.g.
# `html = '<html>'` literal in a Python snippet) or INSIDE complete
# HTML (e.g. a JS string containing backticks) does not falsely
# disqualify the artifact path.
text_without_closed_fences = _CLOSED_CODE_FENCE.sub("", text)
text_without_closed_markup = _CLOSED_MARKUP_ARTIFACT.sub("", text)
if _has_unclosed_code_fence(text_without_closed_markup):
return False
if _has_unclosed_markup_block(text):
if _has_unclosed_markup_block(text_without_closed_fences):
return False
if _looks_like_real_artifact(text):
return True
@ -321,6 +368,8 @@ def _has_answer_artifact(text: str) -> bool:
return False
if _DIRECT_NUMBERED_PLAN_FRAMING.search(text):
return False
if _BARE_INTENT_NUMBERED_PLAN.search(text):
return False
# First-person pronoun intent + numbered list where the items
# themselves start with a strong work verb ("First, I'll:\n
# 1. Load...\n2. Run...") is a plan stall, even when no work

View file

@ -956,6 +956,90 @@ def test_reprompts_on_empty_html_or_svg_skeleton_mention():
assert _would_reprompt(content), content
def test_no_reprompt_on_code_fence_containing_markup_literal():
"""A closed code fence whose body contains literal ``<html>``,
``<svg>``, ``<body>`` strings is still a complete code answer.
The unclosed-markup cross-check operates on text with closed
fences stripped out so code literals do not falsely trip it."""
samples = [
(
"First, let me write the scraper.\n"
"```python\n"
"html = '<html><body>'\n"
"svg = \"<svg width='100'>\"\n"
"print(html, svg)\n"
"```"
),
(
"First, let me write the parser.\n"
"```javascript\n"
"const open = '<html>';\n"
"const fragment = '<svg width=\"10\">';\n"
"console.log(open, fragment);\n"
"```"
),
]
for content in samples:
assert _has_answer_artifact(content), content
assert not _would_reprompt(content), content
def test_no_reprompt_on_html_containing_backtick_literal():
"""A complete <html> answer whose body contains a JS string with
literal backticks is still a complete page. The unclosed-fence
cross-check operates on text with closed markup stripped out."""
content = (
"First, here is the page.\n"
"<html><body><script>const fence = '```';</script></body></html>"
)
assert _has_answer_artifact(content)
assert not _would_reprompt(content)
def test_empty_markup_before_real_artifact_still_counts_real_artifact():
"""An empty <html></html> / <svg></svg> skeleton that PRECEDES a
real complete artifact must not hide it. _looks_like_real_artifact
iterates every match."""
samples = [
(
"First, the minimal skeleton is <html></html>. "
"Here is the full page: <html><body><h1>Hello</h1></body></html>"
),
(
"First, the icon skeleton is <svg></svg>. "
"Here is the full SVG: "
"<svg width='10'><circle cx='5' cy='5' r='4'/></svg>"
),
]
for content in samples:
assert _has_answer_artifact(content), content
assert not _would_reprompt(content), content
def test_doctype_empty_html_skeleton_still_reprompts():
"""``<!doctype html><html></html>`` is an empty skeleton even with
a doctype prefix; the artifact check must reject it."""
content = (
"First, I'll create a <!doctype html><html></html> skeleton, then add CSS."
)
assert not _has_answer_artifact(content)
assert _would_reprompt(content)
def test_reprompts_on_bare_intent_colon_numbered_plan():
"""Bare ``I'll:`` / ``Let me:`` immediately followed by a numbered
list of work verbs is a tool stall regardless of whether the verbs
sit before or in the list."""
samples = [
"I'll:\n1. Open the URL.\n2. Read the page.\n3. Summarize the answer.",
"First, I'll:\n1. Create the Python file.\n2. Build the game loop.\n3. Test it.",
"Let me:\n1. Parse the JSON.\n2. Calculate the average.",
]
for content in samples:
assert not _has_answer_artifact(content), content
assert _would_reprompt(content), content
def test_reprompts_on_intent_plus_numbered_action_items():
"""``First, I'll:\\n1. Load CSV\\n2. Compute total`` is a plan stall:
the work verbs are in the list ITEMS even though no work verb