A reopen can be correct and still be stale

Systemorph/MeshWeaver#4610, measured 2026-09-17 by re-reading the wave over the GitHub REST API.

On 2026-09-17, between 07:45Z and 07:55Z, the log-incident bot reopened roughly 128 closed issues in Systemorph/MeshWeaver, each headed "Reopened — this fault is happening again." Two issues report it: #4577 (≈130 issues, evidence 7–23 days old) and #4610 (≈51 issues, "the predicate is a count delta, not recency"). They are one wave, not two: #4610's #1109 carries a reopened event at 07:52:38Z, inside #4577's window, from the same actor.

The rule proposed for it

MeshWeaver.Plugins#2014 replaces "closed ⇒ reopen" with

issue.ClosedAt is { } closedAt && incident.LastSeen > closedAt

— a close is reverted only by an occurrence that postdates it. That is correct and it is the right first predicate: it is the one comparison that distinguishes "the fault came back after you fixed it" from "we are late telling you about the fault you already fixed", which is what the comment-debt clock made possible when it began settling old debt on a timer.

What it does NOT cover

Applying that rule to the wave, per issue, from the same evidence the bot used — the last closed event before the reopen, and the Last seen: line of the recurrence comment that accompanied it:

issue close reverted comment's Last seen under #2014 evidence age at reopen
#2177 2026-08-25 23:04:04Z 2026-08-25 15:01:44Z stays closed 22.7 d
#2621 2026-08-29 08:06:12Z 2026-08-28 20:50:31Z stays closed 19.5 d
#2897 2026-09-01 23:57:02Z 2026-08-31 09:24:00Z stays closed 16.9 d
#1127 2026-08-10 20:40:22Z 2026-08-13 16:23:01Z still reopens 34.6 d
#2153 2026-08-25 19:08:33Z 2026-08-26 10:06:46Z still reopens 21.9 d
#2370 2026-08-26 16:39:27Z 2026-08-27 13:03:48Z still reopens 20.8 d
#1114 2026-08-10 21:33:13Z 2026-09-02 07:40:30Z still reopens 15.0 d
#1253 2026-08-12 12:22:46Z 2026-09-06 05:54:48Z still reopens 11.1 d
#2951 2026-09-02 18:55:23Z 2026-09-10 14:13:06Z still reopens 6.7 d
#1422 2026-08-13 18:36:27Z 2026-09-10 14:47:02Z still reopens 6.7 d
#1109 2026-08-10 12:19:23Z (by a human) 2026-09-10 19:08:27Z still reopens 6.5 d

8 of 11 sampled still reopen — including all three rows #4610 measured for itself. #2014's own census of the same wave agrees on the proportion from the other side: 53 predate the close, 75 postdate it.

So the two issues report two different defects that produced one wave:

The reopens in the second group are not wrong: those faults really did recur after the close, so the close really was premature. What is wrong is the tense. A ticket reopened at 07:53Z on the strength of something last seen on 13 August, announcing a present-tense recurrence, sends the next reader after a fault that may have stopped five weeks ago — and #4610's harm ("a reopen means nothing until a human re-reads the record") survives the fix that was proposed for it.

What the second predicate has to be

Not a magic window. The system already has exactly one definition of currently happening: LogWatchOptions.CommentInterval, documented as the rate limit for a fingerprint that "fires continuously" — it is the resolution at which this system observes a fault as ongoing. A reopen that asserts the present tense should require evidence inside it.

Two things make that a real change rather than a line:

  1. Reopen has no clock. Every timing in the filer and in its tests comes from the incident's own LastSeen — deliberately: "no wall-clock dependency, no sleep, no scheduler to pump". A staleness predicate is the first thing here that genuinely needs now, so it needs a seam (a TimeProvider on LogIncidentFiler), and every existing reopen test needs to set it.
  2. The structural discriminator is NOT available where it is needed. "Was this comment triggered by a fresh report or by the debt clock" would answer the question with no clock at all — and LogIncidentWork.Reconcile carries exactly that. But the clock's reconcile only writes RequestedStatus = Comment; the comment itself is then performed by the ordinary path on the next collection emission, as a work item with Reconcile: false. By the time filer.Comment runs, which trigger fired is no longer knowable.

Both belong in the same change as #2014's predicate, in the method #2014 rewrites — not in a second, competing edit to it.

How this was measured

For each issue: GET /repos/Systemorph/MeshWeaver/issues/{n}/events, taking the most recent reopened event and the last closed event before it; then GET /repos/Systemorph/MeshWeaver/issues/{n}/comments, taking the bot comment within two minutes of that reopen and parsing its - Last seen: line. The verdict column is the new rule applied to those two timestamps, truncated to the second GitHub reports closed_at in. Read-only: no issue was opened, closed or reopened.

Reconnecting…
The server was updated. Reloading the page to pick up the latest version.