d4d-agent workflow: respond only to an explicit request with a dataset argument, never to a handle quoted in prose or code (#4108) - #4390
Open
realmarcin wants to merge 6 commits into
Conversation
…put directory (#4108) The check-mention job started on any issue, PR or comment body that contained the assistant's handle, and its detect step took the rest of the line after the handle as the request. For an allowed author, a body that merely quoted the handle before an input directory name could start a billed `d4d api run` and open a PR. On #4093 (run 36902377148) only the backtick after the quoted handle prevented a request. src/github/assistant_request.py now makes the decision. A request is one line that starts a paragraph with the handle at column 0, followed by the exact name of one directory under data/sheets_d4dassistant/inputs/ and nothing else, outside fenced code and multi-line HTML blocks. Quoted lines, list items and indented code never qualify, and a text naming two datasets holds none. Where the line reader cannot place the end of a fence or HTML block without parsing lists or HTML, it reads no later line as a request. Anything else logs "no request" and runs nothing. The workflow keeps contains() as a cheap first gate. The detect step now only authorizes the author and hands the text to the new "Read the request" step, which sets qualified-mention and dataset. The respond job takes --project from that output instead of grepping free text. The "could not tell which dataset" branch is gone, since nothing reaches it now. Allowed authors, the billed command and the prompts are unchanged. The README's request section describes the line format, and its free-text examples are removed. Tests cover the #4093 body verbatim, the handle in code spans, fences, quotes, mid-sentence and HTML comments, unknown datasets, and the workflow step run as written. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ctly (#4108) Docstrings only. A list item does continue past a blank line, on indented lines, so the paragraph rule rests on that plus column 0. The log head is plain "no request" when reasons follow it, and a Decision's notes also carry the conflicting-datasets reason. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 5, 2026
Open
Open
Open
…nflict past lost track cancel (#4422, #4423) Review round 1 of PR #4390. Raw HTML (#4422). A browser reads an HTML comment, a tag with its quoted attribute values, and the content of elements such as <textarea> to their own end, not to the end of the Markdown block that holds them, so one left open hides every later line. A line that may be raw HTML (in or opening an HTML block, quoted, on a list marker, or indented) must now close each comment and tag it opens, read by the HTML tokenizer's tag and comment states; an HTML block of kinds 1-5 must do so by its last line. A start tag of a raw-text, RCDATA or PLAINTEXT element or of <svg>/<math>, a processing instruction, CDATA and "--!>" are refused on any line outside a fence: CommonMark passes them through even inside a paragraph, and the tokenizer ends the last three before CommonMark does. Otherwise no later line is read. Conflicts (#4423). A line past the point where the reader loses track is still not a request, but one that would ask for another dataset now cancels a request before it, and the note says it cannot be ruled out. A request line now stands alone: a blank line, or the start or end of the text, after it as well as before it. With text under it Markdown makes the line a heading, a table header or the start of a sentence (#4429, #4430). The two notes state that rule instead of claiming the line "does not start a paragraph", which was false after a heading, a fence or a comment (#4430). The docstring credits the stand-alone rule, not the absence of the handle, for keeping quotes, list items and table rows out (#4429). The conflict note names every line that asks, a repeated dataset too (#4431). A directory a request cannot name because of its name is reported as such, not as "not an input directory" (#4433); input_directories() replaces declared_datasets(). Comments say why the blank-line and closing fence checks strip only spaces (#4427) and why the handle is ASCII-only (#4428). Tests: each HTML block kind the docstring lists, with a blank line after the request line so a broken opener reads a request (#4425); the closing-fence and tilde info-string rules (#4426); lines of other white space (#4427); the non-ASCII case folds of the handle (#4428); and the cases above. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t be, and the stand-alone rule (#4424, #4433) Review round 1 of PR #4390. The request section said the datasheet is generated "from the documents in" the input directory. The resolve step passes one file as the bundle: the first .txt or .md under the directory, paths sorted. The README now says so, says that no other file is read, and says to put several documents into one file (#4424). A comment at the resolve step points back to the README, by name and not by path, so the specificity audit does not take the README for a file the workflow reads. It now also says where the workflow looks for the directory: the default branch for a request in an issue or in any conversation comment, the pull request's merge commit for one in a pull request description or a review comment, the dispatched branch for a manual run. And it states the rule a directory name must follow, and that a request naming a directory that is not there, or whose name breaks the rule, logs "no request" and gets no reply (#4433). The line format follows the parser: a request line stands alone, and one after a block whose end the reader cannot place, or after raw HTML left open, is not read. The workflow's comment at "Read the request" says the same. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… and an inline "--!>" (#4422) Review round 1 of PR #4390. Three raw-HTML cases the tests did not reach, each checked to hide the request line when rendered (markdown-it in its commonmark and gfm-like presets, parsed by html5lib): - a bogus comment ends at its first ">", so a quote or comment after it is read as markup: without that branch a tag swallowing the "<!--" reads as closed; - a comment in a block quote inside a list item is raw only through the list-marker condition; - a comment that "--!>" ends early, in a paragraph, is caught only by the substring rule, since the line is not one that may be raw HTML. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…>" (#4422) OPAQUE refuses both before the tokenizer runs, so a mutant that drops them from _comment_end or the bogus-comment branch survives the tests. The docstring now says the redundancy is deliberate: the tokenizer keeps following its own rules, so narrowing OPAQUE cannot make it read past them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 5, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4108
.github/workflows/d4d-agent.ymlstarted its check-mention job on any issue, pull request or comment body that contained the assistant's handle (contains()does not see Markdown). Its detect step then took the rest of the line after the handle and a blank as the request. For an author in.github/ai-controllers.json, a body that merely quoted the handle before an input directory name could start a billedd4d api runand open a PR. Without a dataset name, it posted "I could not tell which dataset to generate". Run 36902377148 on #4093 reached the detect step; only the backtick after the quoted handle kept the regex from reading a request.What changes
New
src/github/assistant_request.pymakes the decision (standard library only, Python 3.9 or later). A request is one line that:data/sheets_d4dassistant/inputs/, with nothing after it;<pre>,<script>,<style>,<textarea>, processing instructions, declarations, CDATA);<textarea>,<script>,<svg>or a similar element, a processing instruction, CDATA or--!>.A quoted line, a list item, an indented code block or a table row either does not start with the handle or does not stand alone. A text whose request lines name different datasets holds no request. Anything else logs
no request, with the reason for each line that starts with the handle (where requests conflict, one note names every line that asks), and nothing runs.d4d-agent.yml:contains()check stays, documented as a cheap first gate only.$RUNNER_TEMP.python3, only for allowed authors, and setsqualified-mentionanddataset.--projectfrom thatdatasetoutput and re-checks it against its own checkout, instead of searching the free text for a dataset name.d4d api runcommand, the prompts, which file the run reads, and how manual dispatch picks a comment..github/D4D_ASSISTANT_README.md: the request section describes the line format. It says which file a run reads (the first.txtor.mdunder the directory, paths sorted), where the directory must exist for each kind of event, and which directory names a request can carry. The three free-text example requests are removed, since none of them is a request now.Decisions the brief left open
data/sheets_d4dassistant/inputs/. That is the set the old resolve step already matched;d4d api run --bundleaccepts any name. Names must match[A-Za-z0-9][A-Za-z0-9_.-]*because they reach shell commands; a subdirectory named otherwise is logged as one a request cannot name. Matching is exact; the old grep ignored case.Evidence
<template>, which hides its content by what the element does, not by where its markup ends (see the proposed follow-ups below).scan.py) onorigin/mainand on this branch: 167 violations in both. One surface is added:src/github/assistant_request.py, classedgithub_assistant/run_shaping, with no text spans. Rerun after review round 1: the same 167 violations and the same surfaces.Tests
tests/test_assistant_request.py(25 tests) covers:python -S.<?and--!>, which an earlier check already refuses.tests/test_generation_specificity_skill.pyandtests/test_audit_recall_leak.py: 215 passed. The parser and command-line tests also pass under Python 3.9.6.Follow-ups filed
.github/D4D_ASSISTANT_README.mdstill describes the open-ended assistant from before D4D assistant is non-functional: the GitHub Action it depends on no longer exists #172.Review round 1
Twelve findings were filed as #4422-#4433. All twelve are fixed here.
--!>. The rendering check found one case beyond the issue: an inline<textarea>in an ordinary paragraph also swallows every later line.Behavior change in round 1: a request line followed directly by other text was read before; it is now refused and logged.
Proposed follow-ups, not filed from this PR:
<template>, ahiddenattribute,<select>, the fallback content of<video>or<canvas>). This needs GitHub's sanitizer checked first.🤖 Generated with Claude Code