Skip to content

fix(reporter): pre-fill the GitHub issue form with a crash summary - #58

Open
soloturn wants to merge 6 commits into
masterfrom
soloturn-prefill-github-issue
Open

fix(reporter): pre-fill the GitHub issue form with a crash summary#58
soloturn wants to merge 6 commits into
masterfrom
soloturn-prefill-github-issue

Conversation

@soloturn

@soloturn soloturn commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

AI-assisted change proposal. Filed by agent driven by @soloturn via GDD.

Summary

"Report Issue" just opened REPORT_ISSUE_LINK as-is - a bare https://github.com/.../issues/new, always blank, discarding everything the dialog already knows about the crash.

CrashSummary extracts, from the exception and the crashed process' own log output (the reporter runs in its own JVM per #52's subprocess isolation, so there's no other way to reach engine-version/module info):

  • Every exception found - one labeled block each (full trace, capped at 15 lines, plus the 5 log lines logged right before it), naming the log tab it was found in. The one that triggered the report is always first, attributed to whichever tab also logged it if any (otherwise labeled "this crash"); every other exception found across the log tabs follows, so a crash whose real cause is an earlier exception logged in a different tab (e.g. during init) isn't left out.

    On macOS the crash reporter always relaunches in a subprocess (requiresProcessIsolation()), which reconstructs the exception from just its class name and message - its own stack trace points into the reporter's own relaunch machinery, not the real crash site. Whenever the triggering exception was also logged in a tab, the trace captured from that log text is used instead, since it's the real one.

  • Engine version + active modules - extracted via regex against two fixed lines TerasologyEngine already emits at startup.

  • OS + Java version - read directly via System.getProperty.

  • The uploaded PasteBin link, when the user uploaded one.

How it reaches GitHub: @BenjaminAmos noted this shouldn't invent its own body format when Terasology already has a crash-bug-report template, and suggested converting it to an issue form so fields could be pre-populated individually. MovingBlocks/Terasology#5390 does that conversion. So:

  • New GlobalProperties.KEY.REPORT_ISSUE_TEMPLATE - when a downstream app sets it (cr-terasology now does, to crash-bug-report.yml), CrashSummary.buildIssueFormFields() + a new GitHubIssueLinkBuilder.build(baseUrl, template, title, fields) overload build a template=+per-field-ID query, landing the summary in that form's real "Terasology Version"/"Operating System"/"Java Version"/"What actually happened"/"Log details"/"Additional Infos" fields instead of overwriting the whole issue.
  • Apps without a configured template (cr-destsol, standalone cr-core) keep the original generic buildTitle()/buildBody() title+body fallback unchanged - the field IDs a configured template targets are inherently tied to whichever form that specific downstream repo defines, so this can never be cr-core's unconditional default.

Both CrashSummary and GitHubIssueLinkBuilder are plain, dependency-free classes with no Swing dependency, so they're covered directly by unit tests without a headless UI harness.

Second fix from #53 (item 3 of 5); log tab ordering, the dead forum link, and the Discord invite are still open follow-ups.

Test plan

  • CrashSummaryTest - version/display-version extraction, module dedup, graceful fallback, title formatting, exception blocks (full trace + context lines) for both the triggering exception and others found in other tabs, PasteBin link handling, and buildIssueFormFields()'s per-field extraction/omission.
  • GitHubIssueLinkBuilderTest - query-param encoding, null when REPORT_ISSUE_LINK isn't configured, the template+fields overload's query building and its omission of empty fields.
  • ./gradlew build - clean.
  • Manually verified live: clicked "File an issue on GitHub" in the running dialog - the crash summary landed in the real crash-bug-report.yml form fields (Terasology Version, Operating System, Java Version, What actually happened, etc.), not a blank/fallback issue.

Related

'Report Issue' just opened REPORT_ISSUE_LINK as-is - a bare
https://github.com/.../issues/new, always blank, discarding everything
the dialog already knows about the crash.

Adds CrashSummary, which builds a title (exception class + message) and
a Markdown body from three sources:
- the exception itself (available directly, no parsing needed) - class,
  message, and a capped stack trace extract
- the engine version and active module list, which only exist in the
  crashed process' own log output (the reporter runs in its own JVM per
  #52's subprocess isolation, so it has no other way to reach them) -
  extracted via regex against two fixed log lines TerasologyEngine
  already emits at startup (TerasologyVersion's bracketed
  key=value dump, and one "Activating module: id:version" line per
  active module)
- the OS, read directly via System.getProperty (same machine, same
  session as the crash - no parsing needed)
- the uploaded PasteBin link, when the user uploaded one

GitHubIssueLinkBuilder turns that into a real pre-filled URL via
GitHub's own title=/body= query parameters. Both are plain, dependency-
free classes - no Swing - so they're covered directly by
CrashSummaryTest/GitHubIssueLinkBuilderTest without needing a headless
UI harness.

Second fix from #53 (item 3 of 5); log tab ordering, the dead forum
link, and the Discord invite are still open follow-ups.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 945d4f8f-c1d7-4658-a4f0-e4dcfbfddbf5


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

BenjaminAmos pushed a commit that referenced this pull request Aug 22, 2026
sortLogFiles() sorted by file creation time, reversed (newest first).
With two log files from one session (Terasology-init.log,
Terasology-menu.log) that reads as arbitrary - neither the order they
were written in nor the order their names suggest - rather than a
deliberate choice. Sorting by filename instead is deterministic and,
for Terasology's own log naming, happens to match session order too.

Also carries the GlobalProperties NPE guard from #56 (needed to
construct GlobalProperties() at all in cr-core's own test classpath,
same as there - see that PR for the full explanation). Will collapse
to a no-op merge once #56 lands first.

First item from #53 (1 of 5); items 2 and 3 are #56 and #58.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@BenjaminAmos

Copy link
Copy Markdown
Contributor

Rather than inventing a new template, can you use the existing one at https://github.com/MovingBlocks/Terasology/blob/develop/.github/ISSUE_TEMPLATE/crash-bug-report.md instead?

If we convert that template into an issue form, then we might be able to do even better and just pre-populate certain fields (see https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/creating-an-issue#creating-an-issue-from-a-url-query).

CrashSummary.buildBody() only ever showed the single in-process
Throwable the reporter happened to be invoked with. Found while
testing with multiple log tabs (init/menu/game): the game.log tab had
its own NullPointerException, but "File an issue on GitHub" only
pre-filled the crash that triggered the reporter, silently leaving the
other one out even though ErrorMessagePanel#getLog() already combines
every tab's text into what CrashSummary gets.

New "### Other exceptions found in logs" section scans that combined
text for stack traces (any "some.FullyQualified.NameException[:
message]" header line immediately followed by "at "/"Caused by:"
frames - the shape every JVM logging framework prints a Throwable in),
attributes each to its tab, and skips the primary exception's own
entry so it isn't listed twice when the crashed process also logged it
in its own log file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
soloturn and others added 3 commits August 22, 2026 14:51
Splitting this into a separate "Exception" block (the primary crash,
full multi-line trace) and a completely separate "Other exceptions
found in logs" section far below Environment made the two hard to
connect and easy to miss - reported as confusing after testing live.

Now every exception found - the primary one first, then every other
one - is a single row directly under "### Exceptions", each naming the
log tab it was found in (or "this crash" if it wasn't logged in any
tab, which is normal - it's the in-process exception that triggered
the report). That section comes immediately before "### Environment".
The primary exception is attributed to whichever tab also logged it,
instead of being duplicated as a separate "other" entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous one-line-per-exception format was too terse to actually
debug from - a real fix needs the trace, not just a class name and
message. Every exception found now gets its own labeled code block
(full trace, capped at 15 lines like before) instead of a single-line
summary, still all together under "### Exceptions" before
"### Environment".

Also: on macOS the crash reporter always relaunches in a subprocess
(requiresProcessIsolation()), which reconstructs the exception from
just its class name and message - its own stack trace points into
CrashReporter's own relaunch machinery, not the real crash site. So
whenever the primary exception was also logged in one of the log tabs
(the normal case for an engine-level crash handler), the trace
captured from that log text is used instead - it's the real one. The
exception object's own trace is now only a fallback for when nothing
better is available.

Fixed a real bug found while writing this: collectExceptionHeaders()
and framesFromThrowable() both called String.trim() on the captured
frame text, which silently ate the leading tab off the *first* frame
line ("\tat ...") since trim() only strips from the very edges of the
whole string, not per-line - every trace in the pre-filled issue would
have rendered its first frame without the indentation the rest have.
New stripTrailingWhitespace() strips only the trailing newline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tion

Requested after testing: knowing only the exception itself often isn't
enough to understand what led up to it. Each exception block now
starts with up to 5 lines of whatever was logged immediately before it
in that tab, then the exception's own header and trace as before. The
in-process fallback trace (see the previous commit's javadoc on macOS
reconstruction) has no log text to pull context from, so it's
unaffected.

Context is not subject to the trace's own 15-line cap - a few lines of
what led up to the crash shouldn't cost trace detail.

Found and fixed a boundary bug while writing this: the substring for
each log tab started right after "=== tab ===", before that line's own
newline, so every tab's text began with a blank artifact line -
counted as a real (empty) line of context. Tab boundaries now skip
past that line terminator, so the 5 lines captured are always real log
content.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@soloturn

Copy link
Copy Markdown
Contributor Author

Good idea - opened MovingBlocks/Terasology#5390 converting crash-bug-report.md to an issue form (same sections/fields, individually addressable by ID). Once that's in, I'll rework this PR's CrashSummary to pre-fill it via the field-ID query params instead of the custom body it builds now.

…stom body

@BenjaminAmos noted "Report Issue" should use the repo's existing
crash-bug-report template rather than a bespoke Markdown format, and
suggested converting it to a GitHub issue form so individual fields
could be pre-populated by ID via URL query
(MovingBlocks/Terasology#5390 does that conversion).

New GlobalProperties.KEY.REPORT_ISSUE_TEMPLATE: when a downstream app
sets it (cr-terasology now does, to "crash-bug-report.yml"),
FinalActionsPanel builds a `template=`+per-field-ID query via a new
GitHubIssueLinkBuilder.build(baseUrl, template, title, fields)
overload and CrashSummary.buildIssueFormFields(), landing the crash
summary in that form's real "Terasology Version"/"Operating
System"/"Java Version"/"What actually happened"/"Log
details"/"Additional Infos" fields instead of overwriting the whole
issue with a custom body.

Apps without a configured template (cr-destsol, standalone cr-core)
keep the existing generic buildTitle()/buildBody() title+body fallback
unchanged - REPORT_ISSUE_TEMPLATE is engine-agnostic cr-core plumbing,
but the field IDs it targets when set are inherently tied to whichever
form the downstream app's own repo defines, so this can never be the
unconditional default.

Also reapplied GitHubIssueLinkBuilder's null-baseUrl guard here (this
branch predates that fix, added directly on merge-train earlier this
session) - build() must not silently produce a broken "null?title=..."
link when REPORT_ISSUE_LINK isn't configured.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants