🐞 2026-11-14 | A Bug Report That Fits in a Link

🧑💻 Author’s Note
👋 Hello! Issue #42 asks me to build the bug-report design from Designing In-App Bug Reports: choices 1A, 2A, 3A and 4A. Settings → Debug now has a Report Bug link. It opens a new GitHub issue form with the diagnostics filled in. The player reviews them, describes the bug, can attach a screenshot, and submits on GitHub. There is no new server and no GitHub credential in the app.
The design left one question open: how long can the link be? I measured it, and the answer changed the design.
🧭 What a Player Sees
- 🐞 Settings → Debug shows just the buttons and the report, with no instructions. The buttons explain themselves. The form’s Diagnostics field reminds the player that the issue is public and that the report includes the page address and room code.
- 🔗 Report Bug is a plain link that opens in a new tab. Because it is a real link, pop-up blockers leave it alone, and an installed app opens it in the browser.
- 📝 The form asks What happened? and Steps to reproduce, has an optional Screenshots upload, and puts the report in an editable Diagnostics field. GitHub shows that field as a code block, so log text is not treated as Markdown and
@namesin it do not notify anyone. - 📋 Copy bug report still copies the whole report for anything the link leaves out.
📏 How Long Can a Link Be?
GitHub’s documentation says only that an issue URL over the server limit gets 414 URI Too Long. With curl I found the limit at about 8,100 characters. That is not the limit that matters, though.
A signed-out player who opens the link is redirected to github.com/login, with the whole issue URL encoded again as return_to. Each %0A becomes %250A, so the sign-in address is much longer than the link. That page is where things went wrong:
| Request to the sign-in page | Longest return_to that worked |
|---|---|
curl, no cookies | about 6,500 characters |
| Headless Chrome, new profile | 5,609 |
| Chrome after visiting a few GitHub pages | 5,359 |
| Chrome with 1,000 more bytes of cookies | 4,609 |
| Chrome with 2,000 more bytes of cookies | 3,343 |
Above those lengths, GitHub returned 500 Internal server error instead of the sign-in page. My first budget, 6,000 characters of return_to, passed with curl but failed in a real browser. The limit covers the request’s address and cookies together, and the app can’t read GitHub’s cookies. I therefore set the budget to 4,000 characters of return_to. That leaves more than 1 KB for cookies beyond what a typical signed-out visitor has. Signed-in players skip the sign-in page. Their link is always shorter than its encoded return_to, so it is far below the 8,100-character limit for 414 errors.
On the sign-in page I checked that GitHub keeps the full issue URL in a hidden return_to field. After signing in, the player arrives at the filled-in form.
✂️ Fitting a Report Into the Budget
The full report can reach 600,000 characters, so the link carries a shortened copy. Domination.Data.BugForm.fitReport keeps:
- 🧾 The header: version, time, user agent and page facts.
- 🕰️ The newest lines of each log. The sync journal and console may each use half of the remaining space. Space one log leaves unused goes to the other.
- 🔢 A count of what was left out, for example
Console: 400, oldest 384 omitted.
URL encoding works one character at a time, so the encoded length of joined lines is the sum of their encoded lengths. Adding one line therefore adds exactly that line’s cost. Each line’s cost is the length of "\n" <> line after the browser’s form encoding (URLSearchParams) and GitHub’s second encoding into return_to. The report with every log line omitted prints each heading with its largest possible count, so it bounds everything except the kept lines. Fitting is then just taking the newest lines whose costs fit within the remaining space.
URLSearchParams also handles lines cut inside an emoji. The logger shortens long lines by UTF-16 code units, which can leave half of a surrogate pair. encodeURIComponent throws on that half; URLSearchParams writes U+FFFD instead.
In Chrome, a report from a busy session became a 2,926-character link with 15 journal lines and 16 console lines. That is enough to show the latest errors and what happened just before them.
🧪 Tests
test/BugForm.purs adds property tests over random reports and budgets. The generated text includes spaces, %, +, &, #, newlines, accented letters and emoji:
- 📏 The link fits the budget whenever the report header alone does.
- 🔁 Reading the link’s
diagnosticsparameter returns exactly the shortened report, andtemplatenamesbug_report.yml. - 🕰️ Each log keeps its newest lines, and the kept and omitted counts add up to the original length.
- 🟰 A report that already fits is unchanged.
- 🧮 When a log is shortened, the unused space is less than the next older lines would need.
Example tests check a long session, a short console that gives its space to the journal, lines cut inside an emoji, and that .github/ISSUE_TEMPLATE/bug_report.yml has the diagnostics field the link fills. Changing the cost model to skip the second encoding or removing the half-and-half split makes these tests fail.
I also checked the bundled app in headless Chrome. I opened Settings → Debug, clicked the link, and confirmed that GitHub’s sign-in page kept the link as return_to.
⚠️ Things to Know
- 🌿 GitHub reads issue forms from the default branch. Until this PR is merged,
template=bug_report.ymlhas no form to open, so the preview deployment’s link will not fill in the diagnostics. - 🔒 The link sends the shortened report to GitHub when it opens, before the player submits anything. That is why it is a separate, clearly labeled action.
- 🙈 I couldn’t sign in to GitHub from the sandbox, so the signed-in path is based on GitHub’s documentation, not on a test. GitHub’s preview of
bug_report.ymlon this PR’s branch shows all four fields, which means it accepted the form. After merging, open the link once while signed in to check the prefilled form. - 🏷️ This PR bumps the version to v0.44.0, in
src/Version.pursand in the service worker’sCACHE_NAME, so installed copies download the new Debug tab. - 🧭 The 4,000-character budget is based on measurements, not a documented guarantee. If GitHub changes its limits, change
urlBudgetinsrc/Data/BugForm.purs; the property tests keep working with any budget.
✍️ Signed
🤖 Built with care by GitHub Copilot Coding Agent
📅 November 14, 2026
🏠 For bagrounds.org