From cf934e781fe53394dcdaa3e91bdb12dff72c119c Mon Sep 17 00:00:00 2001 From: Jean-Pierre Martineau Date: Sun, 20 Sep 2026 13:11:15 -0400 Subject: [PATCH] The form stops asking for what the tool already writes down Nine of the bug form's fourteen fields were transcription: version, host, OS, terminal, size, platform, device model, firmware, transport. `meshterm diagnostics --out` writes every one of them, correctly, from the machine that has the problem, and the form went on asking a tired person to copy them out by hand. A field that demands precision from someone who does not have it does not produce precision. It produces an abandoned form, or an invented answer -- a reporter guessing at their radio's chip name because the form would not let them past without one. A guess is worse than a blank, because it reads as a fact. So the required set drops from nine fields to five, and not one of the five asks for something the reporter has to look up: what happened, how often, how to repeat it, whether `--mock` changes it, and the diagnostics block itself. Two of the cut fields came back as optional, because the block genuinely cannot know them. It reads the model and firmware off the radio, so a report about a radio that will not connect -- the most common first-run report there is -- has nothing to read them from. And `platform` only separates PicoCalc from regular, so a uConsole and a laptop look alike to it. Both fields now say exactly when they apply, which is rarely. The command itself lands in 0.9.0 and the released build is 0.3.7, so both the form and the forum say what to do when it is not recognised: name your version and send what you can. That line comes out once nobody is running an older one. The trailer workflow inverts the obvious design. It does not nag about commits with no issue trailer, because most commits here legitimately close nothing and a workflow that complains about all of them gets muted within a week. It checks the trailers that are there: `Fixes: #42` against an issue that does not exist is not an error in GitHub's eyes, it is silence -- the issue stays open, the reporter is never told, and the commit looks like it did the job. That is the failure worth a comment. Four labels join the stock set for the states a report actually passes through: needs-diagnostics, confirmed, needs-hardware, cannot-reproduce. They say what state a thing is in. The hardware goes in the title, where it does not multiply. Co-Authored-By: Claude Opus 5 (1M context) --- .github/ISSUE_TEMPLATE/bug.yml | 173 +++++++++++---------------- .github/ISSUE_TEMPLATE/config.yml | 8 +- .github/workflows/issue-trailers.yml | 84 +++++++++++++ 3 files changed, 158 insertions(+), 107 deletions(-) create mode 100644 .github/workflows/issue-trailers.yml diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 2be92a9..1730ccb 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -7,12 +7,26 @@ body: value: | Hopefully you never need this form. - If you do: how much of this you fill in really does decide what happens next. A - report I can act on gets worked on. A vague one usually doesn't, because I can't - do anything with it. If it isn't worth describing properly, it's probably not - worth fixing. + If you do: start by running this, and attach the file it writes. - Most of these take ten seconds each. + ``` + meshterm diagnostics --out bug.md + ``` + + It answers which MeshTerm, on which machine, in which terminal, talking to which + radio over which transport and firmware — most of what I would otherwise ask one + question at a time, and the details people most often end up guessing at. Nothing + in it is private: no keys, no pairing PINs, no positions, no contacts. That is a + tested property, not a promise. + + No clipboard on your handheld? The **Diagnostics** page saves the same file with + `s`, next to the log. + + **`diagnostics` not recognised?** You are on 0.3.7 or older, where the command does + not exist yet. Say which version you have in the block below and fill in what you + can — that is a complete answer, not a failure. + + The rest of this form asks only for what that file cannot know. - type: textarea id: problem @@ -49,118 +63,63 @@ body: validations: required: true - - type: textarea - id: workaround - attributes: - label: Is there a way around it - description: Anything that avoids it, or gets you unstuck. Say so even if it's ugly. - - - type: input - id: version - attributes: - label: MeshTerm version - description: It's on the splash screen when you start, and on the About page. - placeholder: "0.2.0" - validations: - required: true - - - type: input - id: hardware - attributes: - label: What are you running MeshTerm on - description: > - Desktop, laptop, Mac, or a handheld — and if it's a handheld, which one. - placeholder: "ClockworkPi uConsole (CM4), or: desktop PC" - validations: - required: true - - - type: input - id: os - attributes: - label: Which OS, exact version - placeholder: "Windows 11 24H2 / macOS 15.3 / Debian 12" - validations: - required: true - - - type: input - id: terminal - attributes: - label: Which terminal, and how big - description: > - PowerShell, Windows Terminal, iTerm2, the PicoCalc console, ssh from something. - Size in columns x rows if you know it — a lot of layout bugs are really terminal - bugs. - placeholder: "Windows Terminal, 120x40" - validations: - required: true - - - type: input - id: companion - attributes: - label: Which companion device - description: > - Be precise. There are a lot of these and they don't behave the same. "A Xiao" - doesn't narrow it down — "Seeed XIAO nRF52840 + Wio-SX1262" does. Leave blank if - no radio was involved. - placeholder: "Heltec V3 / RAK4631 / XIAO nRF52840 + Wio-SX1262" - - - type: input - id: firmware - attributes: - label: Which firmware version is on it - placeholder: "MeshCore 1.15.0" - - - type: dropdown - id: transport - attributes: - label: How is it connected - options: - - USB / serial - - Bluetooth - - TCP - - SPI-to-TCP bridge - - Something else (say so below) - - No radio — simulator / --mock - validations: - required: true - - - type: dropdown - id: flavour - attributes: - label: Regular or PicoCalc - description: > - MeshTerm draws itself differently on a PicoCalc — narrower, fewer colours, a - different font. A bug in one often doesn't exist in the other, so this saves a - round of guessing. - options: - - Regular (desktop, laptop, ssh) - - PicoCalc handheld - - Not sure - validations: - required: true - - type: dropdown id: mock attributes: label: Does it still happen with `meshterm --mock` description: > - `--mock` runs the whole app against a fake radio. If the bug survives that, it's - in MeshTerm and I can chase it without hardware. If it doesn't, it's in the link - or the radio, which is a different hunt. Skip it if the bug is about connecting - in the first place. + `--mock` runs the whole app against a fake radio. If the bug survives that, it is + in MeshTerm and I can chase it without owning your hardware. If it doesn't, it is + in the link or the radio, which is a different hunt. This one question decides + which of those two jobs this is, so it is worth the minute it takes. options: - "Yes, still happens with --mock" - "No, --mock is fine" - "Haven't tried" - - "Doesn't apply" + - "Doesn't apply — the problem is connecting" + validations: + required: true + + - type: textarea + id: diagnostics + attributes: + label: The diagnostics block + description: > + Attach `bug.md` by dragging it in, or paste its contents here. If you genuinely + cannot produce one — an older build, or MeshTerm will not start at all — write down + why instead, along with your version, and I will work with that. + render: markdown + validations: + required: true + + - type: input + id: handheld + attributes: + label: Which handheld, if it is a handheld + description: > + Only if you are running on something the diagnostics file cannot name. It knows a + PicoCalc from a regular terminal, but a uConsole and a laptop look much the same to + it. Leave blank on a desktop, laptop or Mac. + placeholder: "ClockworkPi uConsole (CM4) / M5Stack Cardputer" + + - type: input + id: radio + attributes: + label: Which radio and firmware, if MeshTerm could not reach it + description: > + The diagnostics file reads the model and firmware off the radio itself, so when the + problem *is* connecting, it has nothing to read. That is the one case where I need + you to tell me. Be precise — "a Xiao" covers several boards that do not behave + alike. + placeholder: "Seeed XIAO nRF52840 + Wio-SX1262, MeshCore 1.15.0" - type: textarea id: error attributes: label: The exact error, if there was one description: > - Copy it as text rather than photographing the screen — text can be searched, and - a photo of a terminal is hard to read. Paste the whole traceback if it crashed. + Copy it as text rather than photographing the screen — text can be searched, and a + photo of a terminal is hard to read. Paste the whole traceback if it crashed. render: text - type: textarea @@ -170,10 +129,16 @@ body: description: > MeshTerm keeps one at `~/.meshterm/meshterm.log` (or wherever `MESHTERM_HOME` points). Attach it, or paste the last twenty lines. If the problem is hard to - catch, set **Log detail** to `DEBUG` on the Preferences page, do it once more, - and send that instead — it records a great deal more. + catch, set **Log detail** to `DEBUG` on the Preferences page, do it once more, and + send that instead — it records a great deal more. render: text + - type: textarea + id: workaround + attributes: + label: Is there a way around it + description: Anything that avoids it, or gets you unstuck. Say so even if it's ugly. + - type: textarea id: extra attributes: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 24495df..d951483 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -4,10 +4,12 @@ contact_links: url: https://discord.gg/AZwe5Uvb3S about: > Setup trouble, "is it supposed to do this", or anything you want a second opinion - on. The support forum is the place. Someone running your radio has probably hit it. + on. The #support forum is the place. Someone running your radio has probably hit it. - - name: No GitHub account? Report it on Discord instead + - name: No GitHub account? Report it in the #bugs forum instead url: https://discord.gg/AZwe5Uvb3S about: > - Drop reproducible bugs in #bugs and they'll get moved here. Being told beats + One post per bug, tagged, with the diagnostics file attached. Real ones get opened + here and your post is renamed with the issue number, so you can follow it without an + account — and something comes back to tell you when it ships. Being told beats filing it tidily. diff --git a/.github/workflows/issue-trailers.yml b/.github/workflows/issue-trailers.yml new file mode 100644 index 0000000..03bfde7 --- /dev/null +++ b/.github/workflows/issue-trailers.yml @@ -0,0 +1,84 @@ +name: Issue trailers + +# A commit that names an issue is the only thing connecting a fix to the person +# who reported it. GitHub resolves that link silently: `Fixes: #42` against an +# issue that does not exist is not an error, it is nothing at all -- the issue +# stays open, the reporter is never told, and the commit looks like it did the +# job. A typo costs a whole loop and announces itself nowhere. +# +# So this checks the trailers that ARE there rather than nagging about the ones +# that are not. Most commits here are internal work that legitimately closes +# nothing, and a workflow that complains about all of them gets muted in a week. +# +# Cross-repo trailers (`Fixes: owner/repo#12`) are reported but not verified: +# GITHUB_TOKEN only reaches this repository. + +on: + push: + branches: [main] + +permissions: + contents: write # to leave a comment on the offending commit + issues: read + +jobs: + check: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[skip ci]')" + steps: + - name: Verify every issue named in a trailer exists + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + COMMITS: ${{ toJSON(github.event.commits) }} + run: | + set -euo pipefail + + echo "$COMMITS" > /tmp/commits.json + count=$(jq 'length' /tmp/commits.json) + echo "checking $count commit(s)" + + problems=0 + + for i in $(seq 0 $((count - 1))); do + sha=$(jq -r ".[$i].id" /tmp/commits.json) + msg=$(jq -r ".[$i].message" /tmp/commits.json) + short="${sha:0:8}" + + # Trailers only: a line that starts with the keyword. A bare "#12" + # in prose is a reference someone wrote on purpose, not a claim + # that this commit closes anything. + refs=$(printf '%s\n' "$msg" \ + | grep -oiE '^(fixes|closes|resolves|refs|see):[[:space:]]*([A-Za-z0-9._-]+/[A-Za-z0-9._-]+)?#[0-9]+' \ + || true) + + [ -z "$refs" ] && continue + + while IFS= read -r ref; do + [ -z "$ref" ] && continue + target=$(printf '%s' "$ref" | sed -E 's/^[A-Za-z]+:[[:space:]]*//') + num="${target##*#}" + + if [ "$target" != "#${num}" ]; then + repo="${target%%#*}" + echo " $short -> $target (other repository, not verified here)" + continue + fi + + if state=$(gh api "repos/$REPO/issues/$num" --jq .state 2>/dev/null); then + echo " $short -> #$num ($state)" + else + echo "::error::commit $short names #$num, which does not exist in $REPO" + problems=$((problems + 1)) + gh api "repos/$REPO/commits/$sha/comments" \ + -f body="This commit's trailer names **#$num**, which does not exist in \`$REPO\`. + + GitHub resolves a closing keyword silently, so nothing failed and nothing was linked — the issue this was meant to close is still open, and whoever reported it has not been told. Worth a follow-up commit with the right number, or a note on the issue itself." \ + >/dev/null + fi + done <<< "$refs" + done + + if [ "$problems" -gt 0 ]; then + echo "::warning::$problems trailer(s) pointed at issues that do not exist" + fi