| name | drupal-issue-comment |
| description | Draft a drupal.org issue comment after reproducing or investigating a bug. Use when the user has finished reproducing an issue and wants to write up findings as a comment on the d.o issue queue. Covers screenshot capture, HTML formatting for the d.o comment box, and conversational tone.
|
| license | GPL-2.0-or-later |
| metadata | {"author":"ajv009","version":"1.0.0"} |
drupal-issue-comment
Announce at start: "I'm using the drupal-issue-comment skill to draft the d.o comment."
IRON LAW: NO SELF-CONGRATULATORY FILLER IN COMMENTS. Never mention passing tests, PHPCS results, or how clean your code is. Let the MR speak for itself.
IRON LAW: NO EM DASHES, EN DASHES, OR DOUBLE HYPHENS. They look AI-generated. Use commas, colons, semicolons, or periods instead.
Gotchas
- drupal.org accepts Filtered HTML only. Supported tags:
<h2>-<h6>,
<em>, <strong>, <pre>, <code>, <del>, <img>, <blockquote>,
<q>, <a>, <ul>/<ol>/<li>, <dl>/<dt>/<dd>. No <style>, <head>,
<script>, <!DOCTYPE>. Unknown tags are silently stripped.
[#NNNNNN] auto-links to the issue. #NNNNNN on its own does not.
Use the brackets every time.
- Screenshot paths include the upload date. After uploading to d.o,
the file lives at
/files/issues/YYYY-MM-DD/filename.png where the date
is the day you uploaded (today), not the issue creation date.
- Never add
Co-Authored-By: Claude ... to drupal.org commits. Different
norms than GitHub. The transparency note in the comment body is the
correct disclosure on d.o.
tui.json entry is pre-created. drupal-issue.sh creates the
numeric-issue entry with an issue-page action. Only APPEND the comment
action — never overwrite or remove the pre-existing entries.
- Author comment #9 pattern ≠ humility. "Happy to change X if you want"
on incomplete work defers scoping to the reviewer. Finish mechanical
follow-throughs (tests, standards, naming) before drafting, not after.
Called From
This skill is invoked as the final step by:
/drupal-issue (category F: reply with context, category I: re-review)
/drupal-issue-review (step 5: draft a comment after reproduction)
/drupal-contribute-fix (after pushing, to document changes on the issue)
It is a terminal skill with no handoff to a next skill. Its output is the .html comment file at DRUPAL_ISSUES/{issue_number}/issue-comment-{issue_number}.html.
Write a drupal.org issue queue comment after reproducing or investigating a bug.
When to use
- After reproducing a d.o issue locally
- When the user says "write up the findings", "comment on the issue", "draft a reply"
- When test results, screenshots, or analysis need to be packaged for d.o
Comment style
The comment goes into a Drupal.org issue comment box which accepts Filtered HTML.
It is NOT a report, NOT a README, NOT documentation. It is a conversation with other
contributors on the issue.
Tone
- Conversational. Write as if you're a contributor replying to another contributor.
- Address people by their d.o username with
@username.
- Say "Hey @person, I tried X and here's what I found" not "Section 1: Findings".
- When someone couldn't reproduce or got different results, don't say "why they failed".
Instead say "could you try this?" or "this is what worked for me".
- Be helpful, not authoritative. Suggest, don't lecture.
- Keep it concise. Contributors are busy.
Humility over showmanship
-
Never write sentences that prove your work is superior or correct. Let the
diff speak for itself. Don't say "this does exactly what X suggested" or
"this is the right approach". Just state what you did.
-
No self-congratulatory filler. Lines like "Minimal diff, easy to read,
does the job" or "Clean, simple, works perfectly" are garbage. Cut them.
-
Don't advertise your own quality checks. Never mention that tests pass,
PHPCS is clean, or that you ran linting. The MR pipeline shows that. Lines
like "Existing tests still pass, PHPCS clean" read as boasting. If you wrote
tests, say you added them. Don't narrate that they pass.
-
Offer to revert completed work. Do NOT defer incomplete work.
There is a sharp line between these two patterns, and only one is
legitimate humility:
- OK (reverting completed work): "happy to revert this if you
prefer the previous approach". You did the work, reviewed it,
pushed it, and are offering to undo it if the reviewer disagrees.
This respects the reviewer's authority over the final shape.
- NOT OK (deferring incomplete work): "did not add a kernel test,
happy to add one if you want", "didn't nest the new fields to match
the upstream convention, say the word if you'd rather have
consistency", "will open an MR if preferred". This turns the
reviewer into your PM and asks them to do the scoping work you
should have done before posting.
Rule of thumb for deciding: if the work is within the scope of the
already-touched files, is mechanical (no new design decisions), or
is a standard follow-through (tests, standards compliance, naming
consistency with existing conventions), DO IT before posting. If the
work genuinely needs a policy choice from the maintainer (a magic
number, an API shape, a user-visible name), state the choice you
made and the reasoning as a FACT ("picked 2 as one nudge, one retry,
then release") rather than framing it as an open question. If the
reviewer disagrees, they will say so; that is what the review is
for.
-
No "separate follow-up" language without a linked issue AND a
completed pre-follow-up search. Saying "I'll file a separate issue
for X" without a link is a smell: either it should already be filed
(paste the link), or X belongs in this MR. Before mentioning any
follow-up in a comment, run the three-part pre-follow-up search from
drupal-issue Q10 (issue queue, existing MRs, existing code pattern
in the same codebase) and state what was searched so the reviewer
knows the check was done.
-
State facts, not opinions about your own work. "Added strtolower to the
array_map chains" is a fact. "This is much cleaner" is self-promotion.
What NOT to do
- No
<style> tags, no CSS, no <head>, no <!DOCTYPE>.
- No section headers like "## Root Cause Analysis". This isn't a paper.
- No summary tables or status badges.
- No over-engineering. It's a comment box, not a landing page.
- Don't repeat the entire issue description back. People can read the thread.
- Never use em dashes (—), en dashes (–), or double hyphens (--).
They look AI-generated. Restructure the sentence instead: use commas,
colons, semicolons, periods, or parentheses. Write like a normal person.
- Don't glorify or over-praise other people's work. A one-liner like
"looks good" is fine. Don't write paragraphs listing everything they did
right. If someone already reviewed it, don't repeat their review.
- Don't repeat yourself. If you mention a caveat or dependency earlier
in the comment, don't restate it at the end as a "summary" or "note".
Say it once, in the right place. No recap paragraphs.
- Don't pad with filler. If your actual content is three sentences,
the comment should be three sentences. Don't stretch it to look thorough.
- Don't narrate implementation details nobody asked for. If you pushed
a commit, the diff shows what changed. Don't write paragraphs explaining
which method you called or why you chose one cache tag over another. One
sentence per change is enough: "Split the NULL/disabled branches in
blockAccess()" not a 200-word essay about addCacheableDependency behavior.
If a reviewer needs to understand the change, they will read the diff.
- Contribute substance, not commentary. Instead of writing prose about
what someone else's code does, contribute something concrete: a test that
was missing, a reproduction script, a screenshot, a CI artifact link.
Marcus's comment "Added a functional js test" with video/screenshot links
moved the issue forward more than 2000 characters of cache tag analysis.
Ask yourself: "Does this comment move the issue closer to being committed,
or does it just show that I understood the code?" If the latter, cut it.
Allowed HTML tags
Drupal.org Filtered HTML supports:
<h2> <h3> <h4> <h5> <h6> <em> <strong> <pre> <code> <del>
<img> <blockquote> <q> <a> <ul> <ol> <li> <dl> <dt> <dd>
Issue references auto-link: [#1234567] becomes a clickable link to that issue.
Source-aware format (read issue.json["source"] first)
Before drafting, read source from DRUPAL_ISSUES/{issue_number}/artifacts/issue.json.
The format and posting path branch on it.
source == "do" (default, legacy d.o queue)
Use the Filtered HTML format described in this skill (the <h2>-<dd> tag
set above, [#NNNNNN] issue links). Posting is manual: the contributor
pastes the .html file into the d.o comment box. This is the existing
behavior, unchanged.
source == "gitlab" (migrated GitLab work-item)
Draft the note as GitLab-flavored Markdown, not d.o Filtered HTML.
References use GitLab syntax:
| Reference | Syntax |
|---|
| Issue in the same project | #<iid> |
| Issue in another project | <project>#<iid> |
| Merge request | !<mr_iid> |
Standard Markdown applies: **bold**, _italic_, fenced ``` code
blocks, -/1. lists, > blockquotes, [text](url) links. Inline images
use . The tone, humility rules, and quality gate below still
apply. Save the draft as .md (not .html) at
DRUPAL_ISSUES/{issue_number}/issue-comment-{issue_number}.md.
Screenshots
Capturing
Use agent-browser (installed at ~/.cargo/bin/agent-browser, headless, no npm/node needed).
For the full command reference, see the agent-browser skill at .claude/skills/agent-browser/SKILL.md.
ULI=$(ddev drush uli --no-browser 2>/dev/null)
agent-browser open "$ULI"
agent-browser wait --load networkidle
agent-browser open "https://d{issue_id}.ddev.site/path/to/page"
agent-browser wait --load networkidle
agent-browser screenshot --full "DRUPAL_ISSUES/{issue_number}/screenshots/01-page-name.png"
agent-browser screenshot "DRUPAL_ISSUES/{issue_number}/screenshots/before.png"
agent-browser reload && agent-browser wait --load networkidle
agent-browser screenshot "DRUPAL_ISSUES/{issue_number}/screenshots/after.png"
agent-browser close
Name screenshots with numbered prefixes and descriptive slugs:
01-form-display.png
02-widget-settings.png
03-error-triggered.png
04-watchdog-detail.png
Where to save
Store screenshots under the issue working directory:
DRUPAL_ISSUES/{issue_number}/screenshots/
Embedding in the comment
Screenshots get uploaded to the d.o issue as file attachments. Once uploaded,
they live at a predictable path. Embed them in the comment HTML like:
<img src="/files/issues/YYYY-MM-DD/03-error-triggered.png" alt="AJAX error after clicking the automator button" />
- Use the date the files were uploaded (today's date) in the path.
- Always include meaningful
alt text describing what the screenshot shows.
- Add a short line of context before each image so people know what they're looking at:
<p>Error after clicking the automator button:</p>
<img src="/files/issues/2026-03-21/03-error-triggered.png" alt="AJAX error after clicking automator button" />
Don't dump all screenshots at the end in a "Screenshots" section. Inline them
where they're relevant in the flow of the comment.
Linking related issues
Drupal.org auto-links issue numbers in square brackets. Use this format:
[#3558728]
When mentioning a related issue, briefly say what it covers so people don't have
to click through:
(related to [#3558728] which covers the same calculateDependencies() crash for $field_name)
Search drupal.org before drafting to find related/duplicate issues. Check if
the problems you discovered during reproduction already have open issues.
Structure of a good comment
A typical comment flows like this (but don't use these as literal headers):
- Greeting + context - "Hey @person, I reproduced this on X environment..."
- Confirmation - "I can confirm the error" + the error message in
<pre>
- Relevant screenshots - inlined where they make sense
- Analysis - what you found, confirming or adding to existing analysis
- Response to previous commenter - if someone had trouble, help them out
- Reproduction steps - what worked for you, as an
<ol>
- Recommendation - what should happen next with the issue/MR
Transparency note
If the reproduction and comment drafting were done with AI assistance, add a
brief italic+bold note at the end disclosing this. Be honest, humble, and make
it clear this is not AI slop. Keep it to 2-3 lines max:
<em><strong>Transparency note:</strong> This research and comment were drafted
with highly supervised usage of Claude Code. Not here to waste anyone's efforts
or demean the work being done, and definitely not posting AI slop. I verified
everything personally. If I'm not doing something right process-wise, please
flag it, would be more than happy to course correct.</em>
No Co-Authored-By on drupal.org
Never add Co-Authored-By: Claude ... (or any AI co-author tag) to commit
messages or MR descriptions on drupal.org. The transparency note in the comment
is sufficient disclosure. The co-author tag is fine on GitHub and other forges,
but d.o is a different community with different norms.
Comment Quality Gate (MANDATORY — runs before output)
Every draft MUST pass all 4 filters below before being written to the
output file. These are mechanical checks, not suggestions.
Filter 1: Word count limit
Count the words in the draft (excluding HTML tags). Compare against the
limit for this comment type:
| Comment type | Max words | When to use |
|---|
| Follow-up to maintainer | 200 | Default |
| Scope expansion explanation | 350 | Category E (feedback-loop) or explicit scope change |
| "Confirming this works" | 80 | MR verified, working correctly |
| Initial issue reply | 250 | First response on the issue from us |
If the draft exceeds the limit: compress. Remove paragraphs that restate
known information, collapse implementation details into bullet points,
cut any paragraph that doesn't answer "what changed?" or "what decision
is needed?". Re-count. If STILL over: rewrite from scratch targeting
50% of the limit, then expand only what's missing.
Do NOT present an over-limit draft to the user. The limit is a hard
gate, not a guideline.
Filter 2: Audience filter
Before drafting (not after), internalize this context:
The maintainers reading this comment already know how this module
works internally. They have read the issue and the MR diff. They do
NOT need: explanations of what the module does, restatements of the
issue, summaries of the MR diff they already have, or congratulatory
framing about how thorough the work was. They DO need: what changed
since their last review, the specific decision point that needs their
input, and any new evidence they don't already have.
This filter is a mindset, not a regex. Apply it during drafting AND
during compression.
Filter 3: Filler-pattern check
Load references/filler-patterns.txt (one pattern per line, 33 patterns).
Grep the draft (case-insensitive) against every pattern. Each match is a
violation that MUST be fixed:
- If the sentence works without the filler phrase, delete the phrase.
- If the sentence needs restructuring, rewrite it.
- If the entire sentence is filler, delete it.
Re-run the check after fixes. Zero violations required before proceeding.
Extending: When new filler patterns emerge from session evidence,
append them to references/filler-patterns.txt. One pattern per line,
lowercase, no regex — plain substring match.
Filter 4: Two-version presentation
After filters 1-3 pass, present the draft to the user in TWO versions:
=== FULL VERSION ({N} words) ===
{the filtered draft, as HTML}
=== COMPRESSED VERSION ({M} words) ===
{first ~100 words as prose + remaining points as a bullet list, as HTML}
Pick: [k]eep full | [c]ompressed | [e]dit
Wait for the user's choice:
- k (keep): write the full version to the output file
- c (compressed): write the compressed version to the output file
- e (edit): ask the user what to change, apply edits, re-run filters
The compressed version is a best-effort summary, not a separate draft.
Generate it by: keeping the opening greeting + first substantive paragraph,
converting remaining paragraphs to one bullet each, dropping any paragraph
that is pure context-setting.
Rationalization Prevention
| Thought | Reality |
|---|
| "This comment needs the extra context to be understood" | The maintainers already read the issue thread. Cut it. |
| "200 words isn't enough for this complex topic" | If 200 words isn't enough, you're explaining implementation, not communicating decisions. |
| "The filler pattern matched but my usage is legitimate" | It's never legitimate. Rewrite the sentence. |
| "The compressed version loses important nuance" | That's what the [e]dit option is for. Don't inflate the full version to compensate. |
Output
The final output is a single .html file saved at:
DRUPAL_ISSUES/{issue_number}/issue-comment-{issue_number}.html
This file contains ONLY the comment body HTML, no wrapper, no doctype, no head.
Ready to copy-paste into the d.o comment box.
For source == "gitlab" issues the file is .md (GitLab-flavored Markdown,
see "Source-aware format" above), not .html.
Posting (source-dependent)
After the draft is written and the user has picked a version at the Filter 4
stop-gate, the posting path depends on issue.json["source"].
source == "do"
Leave the .html file for the contributor to paste into the d.o comment box.
The workbench does not auto-publish to drupal.org. This is the existing
behavior.
source == "gitlab"
After presenting the draft, ASK the user: "Post this note to the issue
now?" (mirror the d.o manual-post stop point; never auto-publish).
-
YES: post it via the fetcher's post-note mode:
./scripts/fetch-issue --mode post-note \
--issue <iid> \
--project project/<project> \
--body-file DRUPAL_ISSUES/{issue_number}/issue-comment-{issue_number}.md \
--gitlab-token-file git.drupalcode.org.key
<iid> and <project> come from issue.json. If the command returns a
PARTIAL: ... line (read-only or missing token), tell the user to paste
the note manually at the issue url from issue.json.
-
NO: leave the .md draft for the user to post manually (same as the
d.o path).
TUI Browser Integration (MANDATORY)
After writing the comment HTML file, append a comment action to the issue's
entry in CONTRIB_WORKBENCH/tui.json. The entry is pre-created by
drupal-issue.sh; only append, never overwrite existing actions like
issue-page.
Action format (append to the actions array of the numeric issue key):
{ "id": "comment", "label": "Comment", "icon": "comment", "type": "file-open", "path": "/absolute/path/to/DRUPAL_ISSUES/{issue_number}/issue-comment-{issue_number}.html" }
Rules: type must be file-open, path must be absolute (not relative),
issue key is the numeric ID (e.g., "3508503"). Skipping this step means
the contributor loses one-click access to the comment from TUI Browser's
terminal toolbar.