An unchanged fingerprint froze the report's body, not just its comments
CI / shell (shellcheck + syntax) (push) Successful in 8s
CI / python 3.11 (push) Failing after 13s
CI / python 3.12 (push) Failing after 12s
CI / python 3.13 (push) Failing after 13s

Two questions were sharing one answer.

  "Have the findings changed?" gates COMMENTS. They notify, and a daily "still
  broken, same as yesterday" is what teaches everyone to ignore the one that
  finally matters.

  "Is the body still true?" gates the BODY. Editing an issue body notifies nobody
  on either forge, so keeping it honest is free.

Conflated, an unchanged fingerprint froze the body -- and the fingerprint ignores,
by design, everything that moves on its own: healthy rows, the hardware-verified
column, point releases, and how a row is LABELLED. So the master -> 27-dev relabel
would have shipped to the README and never to the issue anybody actually opens.
The report would have gone on saying "master (unreleased) BROKEN" -- the precise
false alarm the relabel exists to kill -- until iX happened to break something
else.

The body is now rewritten whenever it is stale; comments stay strictly a changelog
of real changes. Bodies are compared after normalising line endings, because a
forge that round-trips \r\n would otherwise trigger a silent rewrite every run and
leave the issue looking freshly touched every morning.
This commit is contained in:
2026-07-14 03:05:01 +00:00
parent 89eb3a16be
commit e426045255
3 changed files with 129 additions and 5 deletions
+13
View File
@@ -48,6 +48,19 @@ worse than no alert, because one day it carries a security fix.
count as shipped and a break in it would fail the build as a live outage — on a
version nobody is running yet.
- **An unchanged fingerprint froze the bug report's body, not just its comments.** Two
questions were sharing one answer. *Have the findings changed?* gates **comments**
they notify, and a daily "still broken, same as yesterday" is what teaches everyone
to ignore the one that finally matters. *Is the body still true?* gates the **body**
and editing an issue body notifies nobody on either forge, so keeping it honest is
free. Conflated, the report could never be corrected while the findings held steady,
and the fingerprint deliberately ignores everything that moves on its own — healthy
rows, the hardware-verified column, point releases, and how a row is labelled. The
`master``27-dev` relabel above would have reached the README and never the issue
anybody actually opens. The body is now rewritten whenever it is out of date (after
normalising line endings, so a forge round-tripping `\r\n` does not cause a rewrite
every run) and comments remain strictly a changelog of real changes.
- **The compatibility bot filed a new duplicate bug report on every Gitea run.**
`find_issue()` skipped pull requests by testing for the *presence* of the
`pull_request` key. GitHub omits that key on a plain issue; Gitea sends it as
+81
View File
@@ -15,6 +15,7 @@ dangerous thing this file can say -- it means "TrueNAS does this now, retire the
module" -- and it rests on nothing more than a substring match.
"""
import json
import os
import sys
@@ -725,3 +726,83 @@ class TestMasterIsNotTheNextRelease:
# ...and the ordinary rows are untouched.
assert "| 25.10.4 |" in md
assert "| 26.0.0-BETA.3 _(unreleased)_ |" in md
class TestTheBodyIsTruthAndCommentsAreTheChangelog:
"""An unchanged fingerprint used to freeze the BODY, not just silence the comments.
Two different questions were sharing one answer. "Have the findings changed?" gates
COMMENTS -- they notify, and a daily "still broken, same as yesterday" is what
teaches people to ignore the one that finally matters. But "is the body still
true?" gates the BODY, and editing a body notifies nobody, so keeping it honest
costs nothing.
Conflated, an unchanged fingerprint meant the report could never be corrected --
and the fingerprint deliberately ignores everything that moves on its own, which
includes how a row is LABELLED. Relabelling master `27-dev` would have reached the
README and never the issue anybody opens.
"""
ROWS = [{"ref": "master", "unreleased": True,
"modules": check_files(with_(**{
"plugins/cloud_backup/restic.py":
"class ResticConfig:\n cmd: list\n\n"
"def get_restic_config(entry, credentials):\n pass\n",
}))}]
def _run(self, monkeypatch, tmp_path, existing_body):
calls = []
def fake(url, token, method="GET", data=None):
calls.append((method, url, data))
if url.endswith("/issues?state=all&per_page=100&limit=100"):
return [{"number": 1, "title": compat_publish.TITLE,
"state": "open", "body": existing_body,
"pull_request": None}]
return {"number": 1}
monkeypatch.setattr(compat_publish, "_call", fake)
matrix = tmp_path / "m.json"
matrix.write_text(json.dumps(self.ROWS))
compat_publish.main([
"prog", "--api", "https://forge/api", "--token", "t",
"--matrix", str(matrix)])
return calls
def _writes(self, calls):
patched = [c for c in calls if c[0] == "PATCH"]
commented = [c for c in calls if c[0] == "POST" and c[1].endswith("/comments")]
return patched, commented
def test_identical_body_and_findings_touches_nothing(self, monkeypatch, tmp_path):
body = compat.render_issue(self.ROWS)
patched, commented = self._writes(self._run(monkeypatch, tmp_path, body))
assert not patched and not commented, "a quiet run must be completely silent"
def test_a_relabel_refreshes_the_body_but_says_NOTHING(self, monkeypatch, tmp_path):
# Same findings (same fingerprint), different rendering -- the exact shape of
# the master -> 27-dev relabel.
stale = compat.render_issue(self.ROWS).replace("27-dev", "unreleased")
assert compat.extract_fingerprint(stale) == compat.fingerprint(self.ROWS)
patched, commented = self._writes(self._run(monkeypatch, tmp_path, stale))
assert patched, "the body was left stale, so the issue keeps telling lies"
assert "27-dev" in patched[0][2]["body"]
assert not commented, (
"a rendering change is not news -- commenting on it is how the bot gets "
"muted before the next real finding"
)
def test_a_REAL_findings_change_still_comments(self, monkeypatch, tmp_path):
# ...and the fix must not have made it mute.
stale = compat.render_issue(self.ROWS).replace(
compat.fingerprint(self.ROWS), "0" * 16)
patched, commented = self._writes(self._run(monkeypatch, tmp_path, stale))
assert patched and commented, "a genuine change must still notify"
def test_a_body_differing_only_by_CRLF_is_not_rewritten(self, monkeypatch, tmp_path):
# Forges round-trip line endings. Without normalising, every run would rewrite
# the body -- silent, but it churns updated_at and looks freshly touched daily.
body = compat.render_issue(self.ROWS).replace("\n", "\r\n")
patched, _ = self._writes(self._run(monkeypatch, tmp_path, body))
assert not patched
+35 -5
View File
@@ -62,6 +62,19 @@ def _call(url, token, method="GET", data=None):
return json.load(r) if r.length != 0 else {}
def _same(a, b):
"""Is the issue body already what we would write?
Compared after normalising line endings and trailing space: forges are free to
round-trip `\r\n`, and a body that only "differs" by that would be rewritten on
every single run -- a silent edit, but a pointless one that churns `updated_at`
and makes the issue look freshly touched every morning.
"""
def norm(s):
return "\n".join(line.rstrip() for line in (s or "").replace("\r\n", "\n").split("\n")).strip()
return norm(a) == norm(b)
def find_issue(api, token, title):
"""The LOWEST-numbered issue with this title, open or closed.
@@ -124,11 +137,24 @@ def main(argv):
have = extract_fingerprint(issue.get("body") or "")
n = issue["number"]
# ── the findings are UNCHANGED: say nothing ──────────────────────────────
# ── two different questions, and they were being answered with one answer ────
#
# This is the whole point. A daily "still broken, same as yesterday" comment is
# what taught everyone to ignore the last one.
if have == want and issue["state"] == "open":
# * IS THE BODY STILL TRUE? -> if not, rewrite it. Editing an issue body
# notifies NOBODY on either forge, so keeping it honest is free.
# * HAVE THE FINDINGS CHANGED? -> only then comment. Comments DO notify, and a
# daily "still broken, same as yesterday" is what teaches everyone to ignore
# the one that finally matters.
#
# Conflating them meant an unchanged FINGERPRINT froze the BODY. The fingerprint
# deliberately ignores everything that moves on its own -- healthy rows, the
# hardware-verified column, point releases, how a row is LABELLED -- so none of
# that could ever reach the report. Relabelling master `27-dev` (it is not the
# next release; a red row there was reading as "the version you are about to
# install is broken") would have shipped to the README and never to the issue
# anybody actually opens.
body_is_current = _same(issue.get("body"), body)
if have == want and issue["state"] == "open" and body_is_current:
print(f"#{n} is already current ({want}) — staying quiet")
return 0
@@ -146,8 +172,12 @@ def main(argv):
)
_call(f"{args.api}/issues/{n}/comments", args.token, "POST", {"body": note})
print(f"updated #{n}: {have} -> {want}")
else:
elif issue["state"] != "open":
print(f"reopened #{n}")
else:
# Same findings, new rendering. Silent by design: nothing has changed that
# anybody needs waking up for, but the report should not be telling lies.
print(f"#{n}: findings unchanged ({want}); body refreshed silently")
return 0